diff --git a/src/PepperDash.Essentials.Core/Config/BasicConfig.cs b/src/PepperDash.Essentials.Core/Config/BasicConfig.cs index 6df7b583..59ea9bae 100644 --- a/src/PepperDash.Essentials.Core/Config/BasicConfig.cs +++ b/src/PepperDash.Essentials.Core/Config/BasicConfig.cs @@ -5,6 +5,7 @@ using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using PepperDash.Essentials.Core.Devices; namespace PepperDash.Essentials.Core.Config { @@ -28,6 +29,9 @@ namespace PepperDash.Essentials.Core.Config [JsonProperty("audioControlPointLists")] public Dictionary AudioControlPointLists { get; set; } + [JsonProperty("cameraLists")] + public Dictionary> CameraLists { get; set; } + [JsonProperty("tieLines")] public List TieLines { get; set; } @@ -41,6 +45,7 @@ namespace PepperDash.Essentials.Core.Config SourceLists = new Dictionary>(); DestinationLists = new Dictionary>(); AudioControlPointLists = new Dictionary(); + CameraLists = new Dictionary>(); TieLines = new List(); JoinMaps = new Dictionary(); } @@ -84,6 +89,17 @@ namespace PepperDash.Essentials.Core.Config return AudioControlPointLists[key]; } + /// + /// Checks CameraLists for a given list and returns it if found. Otherwise, returns null + /// + public Dictionary GetCameraListForKey(string key) + { + if (string.IsNullOrEmpty(key) || !CameraLists.ContainsKey(key)) + return null; + + return CameraLists[key]; + } + /// /// Checks Devices for an item with a Key that matches and returns it if found. Otherwise, retunes null /// diff --git a/src/PepperDash.Essentials.Core/Devices/CameraListItem.cs b/src/PepperDash.Essentials.Core/Devices/CameraListItem.cs new file mode 100644 index 00000000..d519feb4 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Devices/CameraListItem.cs @@ -0,0 +1,76 @@ +using Newtonsoft.Json; +using PepperDash.Core; + +namespace PepperDash.Essentials.Core +{ + public class CameraListItem + { + [JsonProperty("deviceKey")] + public string DeviceKey { get; set; } + + /// + /// Returns the source Device for this, if it exists in DeviceManager + /// + [JsonIgnore] + public Device CameraDevice + { + get + { + if (_cameraDevice == null) + _cameraDevice = DeviceManager.GetDeviceForKey(DeviceKey) as Device; + return _cameraDevice; + } + } + Device _cameraDevice; + + /// + /// Gets either the source's Name or this AlternateName property, if + /// defined. If source doesn't exist, returns "Missing source" + /// + [JsonProperty("preferredName")] + public string PreferredName + { + get + { + if (string.IsNullOrEmpty(Name)) + { + if (CameraDevice == null) + return "---"; + return CameraDevice.Name; + } + return Name; + } + } + + /// + /// A name that will override the source's name on the UI + /// + [JsonProperty("name")] + public string Name { get; set; } + + + /// + /// Specifies and icon for the source list item + /// + [JsonProperty("icon")] + public string Icon { get; set; } + + /// + /// Alternate icon + /// + [JsonProperty("altIcon", NullValueHandling = NullValueHandling.Ignore)] + public string AltIcon { get; set; } + + /// + /// Indicates if the item should be included in the user facing list + /// + [JsonProperty("includeInUserList")] + public bool IncludeInUserList { get; set; } + + /// + /// Used to specify the order of the items in the source list when displayed + /// + [JsonProperty("order")] + public int Order { get; set; } + } +} diff --git a/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs b/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs index 18559b40..150313e7 100644 --- a/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs +++ b/src/PepperDash.Essentials.Core/Devices/DeviceJsonApi.cs @@ -1,83 +1,81 @@  +using Crestron.SimplSharp; +using Newtonsoft.Json; +using PepperDash.Core; +using Serilog.Events; using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Crestron.SimplSharp; using System.Reflection; -using Newtonsoft.Json; - -using PepperDash.Core; -using Serilog.Events; +using System.Threading.Tasks; namespace PepperDash.Essentials.Core { - public class DeviceJsonApi - { - /// - /// - /// - /// - public static void DoDeviceActionWithJson(string json) - { - if (String.IsNullOrEmpty(json)) - { - CrestronConsole.ConsoleCommandResponse( - "Please provide a JSON object matching the format {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}.\r\nIf the method has no parameters, the \"params\" object may be omitted."); - return; - } - try - { - var action = JsonConvert.DeserializeObject(json); + public class DeviceJsonApi + { + /// + /// + /// + /// + public static void DoDeviceActionWithJson(string json) + { + if (String.IsNullOrEmpty(json)) + { + CrestronConsole.ConsoleCommandResponse( + "Please provide a JSON object matching the format {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}.\r\nIf the method has no parameters, the \"params\" object may be omitted."); + return; + } + try + { + var action = JsonConvert.DeserializeObject(json); - DoDeviceAction(action); - } - catch (Exception ex) - { - CrestronConsole.ConsoleCommandResponse("Incorrect format for JSON. Please check that the format matches {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}"); - } - - } + DoDeviceAction(action); + } + catch (Exception) + { + CrestronConsole.ConsoleCommandResponse("Incorrect format for JSON. Please check that the format matches {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}"); + } + + } - /// - /// - /// - /// - public static void DoDeviceAction(DeviceActionWrapper action) - { - var key = action.DeviceKey; - var obj = FindObjectOnPath(key); - if (obj == null) - { - CrestronConsole.ConsoleCommandResponse("Unable to find object at path {0}", key); - return; - } + /// + /// + /// + /// + public static void DoDeviceAction(DeviceActionWrapper action) + { + var key = action.DeviceKey; + var obj = FindObjectOnPath(key); + if (obj == null) + { + CrestronConsole.ConsoleCommandResponse("Unable to find object at path {0}", key); + return; + } - if (action.Params == null) - { + if (action.Params == null) + { //no params, so setting action.Params to empty array - action.Params = new object[0]; - } + action.Params = new object[0]; + } - Type t = obj.GetType(); - try - { - var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList(); + Type t = obj.GetType(); + try + { + var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList(); - var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length); + var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length); - if (method == null) - { - CrestronConsole.ConsoleCommandResponse( - "Unable to find method with name {0} and that matches parameters {1}", action.MethodName, - action.Params); - return; - } + if (method == null) + { + CrestronConsole.ConsoleCommandResponse( + "Unable to find method with name {0} and that matches parameters {1}", action.MethodName, + action.Params); + return; + } var mParams = method.GetParameters(); var convertedParams = mParams @@ -85,60 +83,116 @@ namespace PepperDash.Essentials.Core .ToArray(); Task.Run(() => - { - try - { - Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey); - method.Invoke(obj, convertedParams); - } - catch(Exception e) - { - Debug.LogMessage(e, "Error invoking method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey); - } - }); + { + try + { + Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey); + method.Invoke(obj, convertedParams); + } + catch (Exception e) + { + Debug.LogMessage(e, "Error invoking method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey); + } + }); - CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name, - action.DeviceKey); - } - catch (Exception ex) - { - CrestronConsole.ConsoleCommandResponse("Unable to call method with name {0}. {1}", action.MethodName, - ex.Message);} - } + CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name, + action.DeviceKey); + } + catch (Exception ex) + { + CrestronConsole.ConsoleCommandResponse("Unable to call method with name {0}. {1}", action.MethodName, + ex.Message); + } + } - private static object ConvertType(object value, Type conversionType) - { - if (!conversionType.IsEnum) - { - return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture); - } + public static async Task DoDeviceActionAsync(DeviceActionWrapper action) + { + var key = action.DeviceKey; + var obj = FindObjectOnPath(key); + if (obj == null) + { + Debug.LogMessage(LogEventLevel.Warning, "Unable to find object at path {deviceKey}", null, key); + return; + } - var stringValue = Convert.ToString(value); + if (action.Params == null) + { + //no params, so setting action.Params to empty array + action.Params = new object[0]; + } - if (String.IsNullOrEmpty(stringValue)) - { - throw new InvalidCastException( - String.Format("{0} cannot be converted to a string prior to conversion to enum")); - } - return Enum.Parse(conversionType, stringValue, true); - } + Type t = obj.GetType(); + try + { + var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList(); - /// - /// Gets the properties on a device - /// - /// - /// - public static string GetProperties(string deviceObjectPath) - { - var obj = FindObjectOnPath(deviceObjectPath); - if (obj == null) - return "{ \"error\":\"No Device\"}"; + var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length); - 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); - } + if (method == null) + { + Debug.LogMessage(LogEventLevel.Warning, + "Unable to find method with name {methodName} and that matches parameters {@parameters}", null, action.MethodName, + action.Params); + return; + } + var mParams = method.GetParameters(); + + var convertedParams = mParams + .Select((p, i) => ConvertType(action.Params[i], p.ParameterType)) + .ToArray(); + + await Task.Run(() => + { + try + { + Debug.LogMessage(LogEventLevel.Verbose, "Calling method {methodName} on device {deviceKey} with {@params}", null, method.Name, action.DeviceKey, action.Params); + method.Invoke(obj, convertedParams); + } + catch (Exception e) + { + Debug.LogMessage(e, "Error invoking method {methodName} on device {deviceKey}", null, method.Name, action.DeviceKey); + } + }); + } + catch (Exception ex) + { + Debug.LogMessage(ex, "Unable to call method with name {methodName} with {@parameters}", null, action.MethodName, action.Params); + } + } + + private static object ConvertType(object value, Type conversionType) + { + if (!conversionType.IsEnum) + { + return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture); + } + + var stringValue = Convert.ToString(value); + + if (String.IsNullOrEmpty(stringValue)) + { + throw new InvalidCastException( + String.Format("{0} cannot be converted to a string prior to conversion to enum")); + } + return Enum.Parse(conversionType, stringValue, true); + } + + /// + /// Gets the properties on a device + /// + /// + /// + public static string GetProperties(string deviceObjectPath) + { + var obj = FindObjectOnPath(deviceObjectPath); + if (obj == null) + return "{ \"error\":\"No Device\"}"; + + 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); + } /// /// Gets a property from a device path by name @@ -149,9 +203,9 @@ namespace PepperDash.Essentials.Core public static object GetPropertyByName(string deviceObjectPath, string propertyName) { var dev = FindObjectOnPath(deviceObjectPath); - if(dev == null) + if (dev == null) return "{ \"error\":\"No Device\"}"; - + object prop = dev.GetType().GetType().GetProperty(propertyName).GetValue(dev, null); // var prop = t.GetProperty(propertyName); @@ -166,126 +220,126 @@ namespace PepperDash.Essentials.Core } } - /// - /// Gets the methods on a device - /// - /// - /// - public static string GetMethods(string deviceObjectPath) - { - var obj = FindObjectOnPath(deviceObjectPath); - if (obj == null) - return "{ \"error\":\"No Device\"}"; + /// + /// Gets the methods on a device + /// + /// + /// + public static string GetMethods(string deviceObjectPath) + { + var obj = FindObjectOnPath(deviceObjectPath); + if (obj == null) + return "{ \"error\":\"No Device\"}"; - // Package up method names using helper objects - Type t = obj.GetType(); - var methods = t.GetMethods() - .Where(m => !m.IsSpecialName) - .Select(p => new MethodNameParams(p)); - return JsonConvert.SerializeObject(methods, Formatting.Indented); - } + // Package up method names using helper objects + Type t = obj.GetType(); + var methods = t.GetMethods() + .Where(m => !m.IsSpecialName) + .Select(p => new MethodNameParams(p)); + return JsonConvert.SerializeObject(methods, Formatting.Indented); + } - public static string GetApiMethods(string deviceObjectPath) - { - var obj = FindObjectOnPath(deviceObjectPath); - if (obj == null) - return "{ \"error\":\"No Device\"}"; + public static string GetApiMethods(string deviceObjectPath) + { + var obj = FindObjectOnPath(deviceObjectPath); + if (obj == null) + return "{ \"error\":\"No Device\"}"; - // Package up method names using helper objects - Type t = obj.GetType(); - var methods = t.GetMethods() - .Where(m => !m.IsSpecialName) - .Where(m => m.GetCustomAttributes(typeof(ApiAttribute), true).Any()) - .Select(p => new MethodNameParams(p)); - return JsonConvert.SerializeObject(methods, Formatting.Indented); - } - + // Package up method names using helper objects + Type t = obj.GetType(); + var methods = t.GetMethods() + .Where(m => !m.IsSpecialName) + .Where(m => m.GetCustomAttributes(typeof(ApiAttribute), true).Any()) + .Select(p => new MethodNameParams(p)); + return JsonConvert.SerializeObject(methods, Formatting.Indented); + } - /// - /// Walks down a dotted object path, starting with a Device, and returns the object - /// at the end of the path - /// - public static object FindObjectOnPath(string deviceObjectPath) - { - var path = deviceObjectPath.Split('.'); - var dev = DeviceManager.GetDeviceForKey(path[0]); - if (dev == null) - { - Debug.LogMessage(LogEventLevel.Information, "Device {0} not found", path[0]); - return null; - } + /// + /// Walks down a dotted object path, starting with a Device, and returns the object + /// at the end of the path + /// + public static object FindObjectOnPath(string deviceObjectPath) + { + var path = deviceObjectPath.Split('.'); - // loop through any dotted properties - object obj = dev; - if (path.Length > 1) - { - for (int i = 1; i < path.Length; i++) - { - var objName = path[i]; - string indexStr = null; - var indexOpen = objName.IndexOf('['); - if (indexOpen != -1) - { - var indexClose = objName.IndexOf(']'); - if (indexClose == -1) - { - Debug.LogMessage(LogEventLevel.Information, dev, "ERROR Unmatched index brackets"); - return null; - } - // Get the index and strip quotes if any - indexStr = objName.Substring(indexOpen + 1, indexClose - indexOpen - 1).Replace("\"", ""); - objName = objName.Substring(0, indexOpen); - Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr); - } + var dev = DeviceManager.GetDeviceForKey(path[0]); + if (dev == null) + { + Debug.LogMessage(LogEventLevel.Information, "Device {0} not found", path[0]); + return null; + } - Type oType = obj.GetType(); - var prop = oType.GetProperty(objName); - if (prop == null) - { - Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} not found on {1}", objName, path[i - 1]); - return null; - } - // if there's an index, try to get the property - if (indexStr != null) - { - if (!typeof(ICollection).IsAssignableFrom(prop.PropertyType)) - { - Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} is not collection", objName); - return null; - } - var collection = prop.GetValue(obj, null) as ICollection; - // Get the indexed items "property" - var indexedPropInfo = prop.PropertyType.GetProperty("Item"); - // These are the parameters for the indexing. Only care about one - var indexParams = indexedPropInfo.GetIndexParameters(); - if (indexParams.Length > 0) - { - Debug.LogMessage(LogEventLevel.Information, " Indexed, param type: {0}", indexParams[0].ParameterType.Name); - var properParam = Convert.ChangeType(indexStr, indexParams[0].ParameterType, - System.Globalization.CultureInfo.InvariantCulture); - try - { - obj = indexedPropInfo.GetValue(collection, new object[] { properParam }); - } - // if the index is bad, catch it here. - catch (TargetInvocationException e) - { - if (e.InnerException is ArgumentOutOfRangeException) - Debug.LogMessage(LogEventLevel.Information, " Index Out of range"); - else if (e.InnerException is KeyNotFoundException) - Debug.LogMessage(LogEventLevel.Information, " Key not found"); - return null; - } - } + // loop through any dotted properties + object obj = dev; + if (path.Length > 1) + { + for (int i = 1; i < path.Length; i++) + { + var objName = path[i]; + string indexStr = null; + var indexOpen = objName.IndexOf('['); + if (indexOpen != -1) + { + var indexClose = objName.IndexOf(']'); + if (indexClose == -1) + { + Debug.LogMessage(LogEventLevel.Information, dev, "ERROR Unmatched index brackets"); + return null; + } + // Get the index and strip quotes if any + indexStr = objName.Substring(indexOpen + 1, indexClose - indexOpen - 1).Replace("\"", ""); + objName = objName.Substring(0, indexOpen); + Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr); + } - } - else - obj = prop.GetValue(obj, null); - } - } - return obj; - } + Type oType = obj.GetType(); + var prop = oType.GetProperty(objName); + if (prop == null) + { + Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} not found on {1}", objName, path[i - 1]); + return null; + } + // if there's an index, try to get the property + if (indexStr != null) + { + if (!typeof(ICollection).IsAssignableFrom(prop.PropertyType)) + { + Debug.LogMessage(LogEventLevel.Information, dev, "Property {0} is not collection", objName); + return null; + } + var collection = prop.GetValue(obj, null) as ICollection; + // Get the indexed items "property" + var indexedPropInfo = prop.PropertyType.GetProperty("Item"); + // These are the parameters for the indexing. Only care about one + var indexParams = indexedPropInfo.GetIndexParameters(); + if (indexParams.Length > 0) + { + Debug.LogMessage(LogEventLevel.Information, " Indexed, param type: {0}", indexParams[0].ParameterType.Name); + var properParam = Convert.ChangeType(indexStr, indexParams[0].ParameterType, + System.Globalization.CultureInfo.InvariantCulture); + try + { + obj = indexedPropInfo.GetValue(collection, new object[] { properParam }); + } + // if the index is bad, catch it here. + catch (TargetInvocationException e) + { + if (e.InnerException is ArgumentOutOfRangeException) + Debug.LogMessage(LogEventLevel.Information, " Index Out of range"); + else if (e.InnerException is KeyNotFoundException) + Debug.LogMessage(LogEventLevel.Information, " Key not found"); + return null; + } + } + + } + else + obj = prop.GetValue(obj, null); + } + } + return obj; + } /// /// Sets a property on an object. @@ -308,78 +362,85 @@ namespace PepperDash.Essentials.Core //return JsonConvert.SerializeObject(props, Formatting.Indented); } - - } - public class DeviceActionWrapper - { - public string DeviceKey { get; set; } - public string MethodName { get; set; } - public object[] Params { get; set; } - } + } - public class PropertyNameType - { - object Parent; + public class DeviceActionWrapper + { + public string DeviceKey { get; set; } + public string MethodName { get; set; } + public object[] Params { get; set; } + } - [JsonIgnore] - public PropertyInfo PropInfo { get; private set; } - public string Name { get { return PropInfo.Name; } } - public string Type { get { return PropInfo.PropertyType.Name; } } - public string Value { get + public class PropertyNameType + { + private object Parent; + + [JsonIgnore] + public PropertyInfo PropInfo { get; private set; } + public string Name { get { return PropInfo.Name; } } + public string Type { get { return PropInfo.PropertyType.Name; } } + public string Value { - if (PropInfo.CanRead) + get { - try + if (PropInfo.CanRead) { - return PropInfo.GetValue(Parent, null).ToString(); + try + { + return PropInfo.GetValue(Parent, null).ToString(); + } + catch (Exception) + { + return null; + } } - catch (Exception) - { + else return null; - } } - else - return null; - } } + } public bool CanRead { get { return PropInfo.CanRead; } } public bool CanWrite { get { return PropInfo.CanWrite; } } - public PropertyNameType(PropertyInfo info, object parent) - { - PropInfo = info; + public PropertyNameType(PropertyInfo info, object parent) + { + PropInfo = info; Parent = parent; - } - } + } + } - public class MethodNameParams - { - [JsonIgnore] - public MethodInfo MethodInfo { get; private set; } + public class MethodNameParams + { + [JsonIgnore] + public MethodInfo MethodInfo { get; private set; } - public string Name { get { return MethodInfo.Name; } } - public IEnumerable Params { get { - return MethodInfo.GetParameters().Select(p => - new NameType { Name = p.Name, Type = p.ParameterType.Name }); - } } + public string Name { get { return MethodInfo.Name; } } + public IEnumerable Params + { + get + { + return MethodInfo.GetParameters().Select(p => + new NameType { Name = p.Name, Type = p.ParameterType.Name }); + } + } - public MethodNameParams(MethodInfo info) - { - MethodInfo = info; - } - } + public MethodNameParams(MethodInfo info) + { + MethodInfo = info; + } + } - public class NameType - { - public string Name { get; set; } - public string Type { get; set; } - } + public class NameType + { + public string Name { get; set; } + public string Type { get; set; } + } - [AttributeUsage(AttributeTargets.All)] - public class ApiAttribute : Attribute - { + [AttributeUsage(AttributeTargets.All)] + public class ApiAttribute : Attribute + { - } + } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs b/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs index e92d11c5..0552faeb 100644 --- a/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs +++ b/src/PepperDash.Essentials.Core/Devices/DeviceManager.cs @@ -1,50 +1,49 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Crestron.SimplSharp; +using Crestron.SimplSharp; using Crestron.SimplSharpPro; - using PepperDash.Core; using Serilog.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; namespace PepperDash.Essentials.Core { - public static class DeviceManager - { + public static class DeviceManager + { public static event EventHandler AllDevicesActivated; public static event EventHandler AllDevicesRegistered; - private static readonly CCriticalSection DeviceCriticalSection = new CCriticalSection(); - private static readonly CEvent AllowAddDevicesCEvent = new CEvent(false, true); - //public static List Devices { get { return _Devices; } } - //static List _Devices = new List(); + private static readonly CCriticalSection DeviceCriticalSection = new CCriticalSection(); + private static readonly CEvent AllowAddDevicesCEvent = new CEvent(false, true); - static readonly Dictionary Devices = new Dictionary(StringComparer.OrdinalIgnoreCase); + //public static List Devices { get { return _Devices; } } + //static List _Devices = new List(); - /// - /// Returns a copy of all the devices in a list - /// - public static List AllDevices { get { return new List(Devices.Values); } } + private static readonly Dictionary Devices = new Dictionary(StringComparer.OrdinalIgnoreCase); - public static bool AddDeviceEnabled; + /// + /// Returns a copy of all the devices in a list + /// + public static List AllDevices { get { return new List(Devices.Values); } } - public static void Initialize(CrestronControlSystem cs) - { - AddDeviceEnabled = true; - CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, "devcommstatus", "Lists the communication status of all devices", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, "devfb", "Lists current feedbacks", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ListDevices, "devlist", "Lists current managed devices", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, "devjson", "", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s)), "devprops", "", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s)), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s)), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator); + public static bool AddDeviceEnabled; + + public static void Initialize(CrestronControlSystem cs) + { + AddDeviceEnabled = true; + CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, "devcommstatus", "Lists the communication status of all devices", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, "devfb", "Lists current feedbacks", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(ListDevices, "devlist", "Lists current managed devices", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, "devjson", "", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s)), "devprops", "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s)), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s)), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(SimulateComReceiveOnDevice, "devsimreceive", "Simulates incoming data on a com device", ConsoleAccessLevelEnum.AccessOperator); @@ -52,77 +51,77 @@ namespace PepperDash.Essentials.Core CrestronConsole.AddNewConsoleCommand(s => DisableAllDeviceStreamDebugging(), "disableallstreamdebug", "disables stream debugging on all devices", ConsoleAccessLevelEnum.AccessOperator); } - /// - /// Calls activate steps on all Device class items - /// - public static void ActivateAll() - { - try - { + /// + /// Calls activate steps on all Device class items + /// + public static void ActivateAll() + { + try + { OnAllDevicesRegistered(); - DeviceCriticalSection.Enter(); + DeviceCriticalSection.Enter(); AddDeviceEnabled = false; - // PreActivate all devices - Debug.LogMessage(LogEventLevel.Information,"****PreActivation starting...****"); - foreach (var d in Devices.Values) - { - try - { - if (d is Device) - (d as Device).PreActivate(); - } - catch (Exception e) - { + // PreActivate all devices + Debug.LogMessage(LogEventLevel.Information, "****PreActivation starting...****"); + foreach (var d in Devices.Values) + { + try + { + if (d is Device) + (d as Device).PreActivate(); + } + catch (Exception e) + { Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PreActivation failure: {0}", e.Message, d.Key); Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace); - } - } + } + } Debug.LogMessage(LogEventLevel.Information, "****PreActivation complete****"); - Debug.LogMessage(LogEventLevel.Information, "****Activation starting...****"); + Debug.LogMessage(LogEventLevel.Information, "****Activation starting...****"); - // Activate all devices - foreach (var d in Devices.Values) - { - try - { - if (d is Device) - (d as Device).Activate(); - } - catch (Exception e) - { + // Activate all devices + foreach (var d in Devices.Values) + { + try + { + if (d is Device) + (d as Device).Activate(); + } + catch (Exception e) + { Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} Activation failure: {0}", e.Message, d.Key); Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace); - } - } + } + } Debug.LogMessage(LogEventLevel.Information, "****Activation complete****"); Debug.LogMessage(LogEventLevel.Information, "****PostActivation starting...****"); - // PostActivate all devices - foreach (var d in Devices.Values) - { - try - { - if (d is Device) - (d as Device).PostActivate(); - } - catch (Exception e) - { - Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key); - Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace); - } - } + // PostActivate all devices + foreach (var d in Devices.Values) + { + try + { + if (d is Device) + (d as Device).PostActivate(); + } + catch (Exception e) + { + Debug.LogMessage(LogEventLevel.Information, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key); + Debug.LogMessage(LogEventLevel.Debug, d, "Stack Trace: {0}", e.StackTrace); + } + } Debug.LogMessage(LogEventLevel.Information, "****PostActivation complete****"); OnAllDevicesActivated(); - } - finally - { + } + finally + { DeviceCriticalSection.Leave(); - } - } + } + } private static void OnAllDevicesActivated() { @@ -142,77 +141,76 @@ namespace PepperDash.Essentials.Core } } - /// - /// Calls activate on all Device class items - /// - public static void DeactivateAll() - { - try - { - DeviceCriticalSection.Enter(); - foreach (var d in Devices.Values.OfType()) - { - d.Deactivate(); - } - } - finally - { - DeviceCriticalSection.Leave(); - } - } + /// + /// Calls activate on all Device class items + /// + public static void DeactivateAll() + { + try + { + DeviceCriticalSection.Enter(); + foreach (var d in Devices.Values.OfType()) + { + d.Deactivate(); + } + } + finally + { + DeviceCriticalSection.Leave(); + } + } - //static void ListMethods(string devKey) - //{ - // var dev = GetDeviceForKey(devKey); - // if(dev != null) - // { - // 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)); - // foreach (var m in methods) - // { - // sb.Append(string.Format("{0}(", m.Name)); - // var pars = m.GetParameters(); - // foreach (var p in pars) - // sb.Append(string.Format("({1}){0} ", p.Name, p.ParameterType.Name)); - // sb.AppendLine(")"); - // } - // CrestronConsole.ConsoleCommandResponse(sb.ToString()); - // } - //} + //static void ListMethods(string devKey) + //{ + // var dev = GetDeviceForKey(devKey); + // if(dev != null) + // { + // 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)); + // foreach (var m in methods) + // { + // sb.Append(string.Format("{0}(", m.Name)); + // var pars = m.GetParameters(); + // foreach (var p in pars) + // sb.Append(string.Format("({1}){0} ", p.Name, p.ParameterType.Name)); + // sb.AppendLine(")"); + // } + // CrestronConsole.ConsoleCommandResponse(sb.ToString()); + // } + //} - private static void ListDevices(string s) - { - Debug.LogMessage(LogEventLevel.Information, "{0} Devices registered with Device Manager:", Devices.Count); - var sorted = Devices.Values.ToList(); - sorted.Sort((a, b) => a.Key.CompareTo(b.Key)); + private static void ListDevices(string s) + { + Debug.LogMessage(LogEventLevel.Information, "{0} Devices registered with Device Manager:", Devices.Count); + var sorted = Devices.Values.ToList(); + sorted.Sort((a, b) => a.Key.CompareTo(b.Key)); - foreach (var d in sorted) - { - var name = d is IKeyName ? (d as IKeyName).Name : "---"; - Debug.LogMessage(LogEventLevel.Information, " [{0}] {1}", d.Key, name); - } - } + foreach (var d in sorted) + { + var name = d is IKeyName ? (d as IKeyName).Name : "---"; + Debug.LogMessage(LogEventLevel.Information, " [{0}] {1}", d.Key, name); + } + } - private static void ListDeviceFeedbacks(string devKey) - { - var dev = GetDeviceForKey(devKey); - if (dev == null) - { - Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey); - return; - } - var statusDev = dev as IHasFeedback; - if (statusDev == null) - { - Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey); - return; - } - statusDev.DumpFeedbacksToConsole(true); - } + private static void ListDeviceFeedbacks(string devKey) + { + var dev = GetDeviceForKey(devKey); + if (dev == null) + { + Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey); + return; + } + if (!(dev is IHasFeedback statusDev)) + { + Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey); + return; + } + statusDev.DumpFeedbacksToConsole(true); + } - //static void ListDeviceCommands(string devKey) + //static void ListDeviceCommands(string devKey) //{ // var dev = GetDeviceForKey(devKey); // if (dev == null) @@ -223,134 +221,132 @@ namespace PepperDash.Essentials.Core // Debug.LogMessage(LogEventLevel.Information, "This needs to be reworked. Stay tuned.", devKey); //} - private static void ListDeviceCommStatuses(string input) - { - var sb = new StringBuilder(); - foreach (var dev in Devices.Values.OfType()) - { - sb.Append(string.Format("{0}: {1}\r", dev, - dev.CommunicationMonitor.Status)); - } - CrestronConsole.ConsoleCommandResponse(sb.ToString()); - } + private static void ListDeviceCommStatuses(string input) + { + + foreach (var dev in Devices.Values.OfType()) + { + CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}{Environment.NewLine}"); + } + } - //static void DoDeviceCommand(string command) + //static void DoDeviceCommand(string command) //{ // Debug.LogMessage(LogEventLevel.Information, "Not yet implemented. Stay tuned"); //} - public static void AddDevice(IKeyed newDev) - { - try - { - if (!DeviceCriticalSection.TryEnter()) - { - Debug.LogMessage(LogEventLevel.Information, "Currently unable to add devices to Device Manager. Please try again"); - return; - } - // Check for device with same key - //var existingDevice = _Devices.FirstOrDefault(d => d.Key.Equals(newDev.Key, StringComparison.OrdinalIgnoreCase)); - ////// If it exists, remove or warn?? - //if (existingDevice != null) + public static void AddDevice(IKeyed newDev) + { + try + { + if (!DeviceCriticalSection.TryEnter()) + { + Debug.LogMessage(LogEventLevel.Information, "Currently unable to add devices to Device Manager. Please try again"); + return; + } + // Check for device with same key + //var existingDevice = _Devices.FirstOrDefault(d => d.Key.Equals(newDev.Key, StringComparison.OrdinalIgnoreCase)); + ////// If it exists, remove or warn?? + //if (existingDevice != null) - if (!AddDeviceEnabled) - { - Debug.LogMessage(LogEventLevel.Information, "All devices have been activated. Adding new devices is not allowed."); - return; - } + if (!AddDeviceEnabled) + { + Debug.LogMessage(LogEventLevel.Information, "All devices have been activated. Adding new devices is not allowed."); + return; + } - if (Devices.ContainsKey(newDev.Key)) - { - Debug.LogMessage(LogEventLevel.Information, newDev, "WARNING: A device with this key already exists. Not added to manager"); - return; - } - Devices.Add(newDev.Key, newDev); - //if (!(_Devices.Contains(newDev))) - // _Devices.Add(newDev); - } - finally - { - DeviceCriticalSection.Leave(); - } - } + if (Devices.ContainsKey(newDev.Key)) + { + Debug.LogMessage(LogEventLevel.Information, newDev, "WARNING: A device with this key already exists. Not added to manager"); + return; + } + Devices.Add(newDev.Key, newDev); + //if (!(_Devices.Contains(newDev))) + // _Devices.Add(newDev); + } + finally + { + DeviceCriticalSection.Leave(); + } + } - public static void AddDevice(IEnumerable devicesToAdd) - { - try - { - if (!DeviceCriticalSection.TryEnter()) - { - Debug.LogMessage(LogEventLevel.Information, - "Currently unable to add devices to Device Manager. Please try again"); - return; - } - if (!AddDeviceEnabled) - { - Debug.LogMessage(LogEventLevel.Information, - "All devices have been activated. Adding new devices is not allowed."); - return; - } + public static void AddDevice(IEnumerable devicesToAdd) + { + try + { + if (!DeviceCriticalSection.TryEnter()) + { + Debug.LogMessage(LogEventLevel.Information, + "Currently unable to add devices to Device Manager. Please try again"); + return; + } + if (!AddDeviceEnabled) + { + Debug.LogMessage(LogEventLevel.Information, + "All devices have been activated. Adding new devices is not allowed."); + return; + } - foreach (var dev in devicesToAdd) - { - try - { - Devices.Add(dev.Key, dev); - } - catch (ArgumentException ex) - { - Debug.LogMessage(LogEventLevel.Information, "Error adding device with key {0} to Device Manager: {1}\r\nStack Trace: {2}", - dev.Key, ex.Message, ex.StackTrace); - } - } - } - finally - { - DeviceCriticalSection.Leave(); - } - } + foreach (var dev in devicesToAdd) + { + try + { + Devices.Add(dev.Key, dev); + } + catch (ArgumentException ex) + { + Debug.LogMessage(LogEventLevel.Information, "Error adding device with key {0} to Device Manager: {1}\r\nStack Trace: {2}", + dev.Key, ex.Message, ex.StackTrace); + } + } + } + finally + { + DeviceCriticalSection.Leave(); + } + } - public static void RemoveDevice(IKeyed newDev) - { - try - { + public static void RemoveDevice(IKeyed newDev) + { + try + { DeviceCriticalSection.Enter(); - if (newDev == null) - return; - if (Devices.ContainsKey(newDev.Key)) - Devices.Remove(newDev.Key); - //if (_Devices.Contains(newDev)) - // _Devices.Remove(newDev); - else - Debug.LogMessage(LogEventLevel.Information, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key); - } - finally - { - DeviceCriticalSection.Leave(); - } - } + if (newDev == null) + return; + if (Devices.ContainsKey(newDev.Key)) + Devices.Remove(newDev.Key); + //if (_Devices.Contains(newDev)) + // _Devices.Remove(newDev); + else + Debug.LogMessage(LogEventLevel.Information, "Device manager: Device '{0}' does not exist in manager. Cannot remove", newDev.Key); + } + finally + { + DeviceCriticalSection.Leave(); + } + } - public static IEnumerable GetDeviceKeys() - { - //return _Devices.Select(d => d.Key).ToList(); - return Devices.Keys; - } + public static IEnumerable GetDeviceKeys() + { + //return _Devices.Select(d => d.Key).ToList(); + return Devices.Keys; + } - public static IEnumerable GetDevices() - { - //return _Devices.Select(d => d.Key).ToList(); - return Devices.Values; - } + public static IEnumerable GetDevices() + { + //return _Devices.Select(d => d.Key).ToList(); + return Devices.Values; + } - public static IKeyed GetDeviceForKey(string key) - { - //return _Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); - if (key != null && Devices.ContainsKey(key)) - return Devices[key]; + public static IKeyed GetDeviceForKey(string key) + { + //return _Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); + if (key != null && Devices.ContainsKey(key)) + return Devices[key]; - return null; - } + return null; + } /// /// Console handler that simulates com port data receive @@ -367,8 +363,7 @@ namespace PepperDash.Essentials.Core } //Debug.LogMessage(LogEventLevel.Verbose, "**** {0} - {1} ****", match.Groups[1].Value, match.Groups[2].Value); - var com = GetDeviceForKey(match.Groups[1].Value) as ComPortController; - if (com == null) + if (!(GetDeviceForKey(match.Groups[1].Value) is ComPortController com)) { CrestronConsole.ConsoleCommandResponse("'{0}' is not a comm port device", match.Groups[1].Value); return; @@ -423,16 +418,15 @@ namespace PepperDash.Essentials.Core var deviceKey = args[0]; var setting = args[1]; - var timeout= String.Empty; + var timeout = String.Empty; if (args.Length >= 3) { timeout = args[2]; } - var device = GetDeviceForKey(deviceKey) as IStreamDebugging; - if (device == null) + if (!(GetDeviceForKey(deviceKey) is IStreamDebugging device)) { CrestronConsole.ConsoleCommandResponse("Unable to get device with key: {0}", deviceKey); return; @@ -479,13 +473,11 @@ namespace PepperDash.Essentials.Core { foreach (var device in AllDevices) { - var streamDevice = device as IStreamDebugging; - - if (streamDevice != null) + if (device is IStreamDebugging streamDevice) { streamDevice.StreamDebugging.SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting.Off); } } } - } + } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/LevelControlListItem.cs b/src/PepperDash.Essentials.Core/Devices/LevelControlListItem.cs index d18dacc5..18da1130 100644 --- a/src/PepperDash.Essentials.Core/Devices/LevelControlListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/LevelControlListItem.cs @@ -65,8 +65,8 @@ namespace PepperDash.Essentials.Core [Flags] public enum eLevelControlType { - Level = 0, - Mute = 1, + Level = 1, + Mute = 2, LevelAndMute = Level | Mute, } } diff --git a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs index b0372b51..6de2f35d 100644 --- a/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs +++ b/src/PepperDash.Essentials.Core/Devices/SourceListItem.cs @@ -1,128 +1,121 @@ - - -using System; -using System.Collections.Generic; -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Crestron.SimplSharpPro; - -using Newtonsoft.Json; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Linq; using PepperDash.Core; +using System.Collections.Generic; namespace PepperDash.Essentials.Core { - /// - /// - /// - public enum eSourceListItemType - { - Route, Off, Other, SomethingAwesomerThanThese - } + /// + /// + /// + public enum eSourceListItemType + { + Route, Off, Other, SomethingAwesomerThanThese + } - /// - /// Represents an item in a source list - can be deserialized into. - /// - public class SourceListItem - { - [JsonProperty("sourceKey")] - public string SourceKey { get; set; } + /// + /// Represents an item in a source list - can be deserialized into. + /// + public class SourceListItem + { + [JsonProperty("sourceKey")] + public string SourceKey { get; set; } - /// - /// Returns the source Device for this, if it exists in DeviceManager - /// - [JsonIgnore] - public Device SourceDevice - { - get - { - if (_SourceDevice == null) - _SourceDevice = DeviceManager.GetDeviceForKey(SourceKey) as Device; - return _SourceDevice; - } - } - Device _SourceDevice; + /// + /// Returns the source Device for this, if it exists in DeviceManager + /// + [JsonIgnore] + public Device SourceDevice + { + get + { + if (_SourceDevice == null) + _SourceDevice = DeviceManager.GetDeviceForKey(SourceKey) as Device; + return _SourceDevice; + } + } - /// - /// Gets either the source's Name or this AlternateName property, if - /// defined. If source doesn't exist, returns "Missing source" - /// - [JsonProperty("preferredName")] - public string PreferredName - { - get - { - if (string.IsNullOrEmpty(Name)) - { - if (SourceDevice == null) - return "---"; - return SourceDevice.Name; - } - return Name; - } - } + private Device _SourceDevice; - /// - /// A name that will override the source's name on the UI - /// - [JsonProperty("name")] - public string Name { get; set; } + /// + /// Gets either the source's Name or this AlternateName property, if + /// defined. If source doesn't exist, returns "Missing source" + /// + [JsonProperty("preferredName")] + public string PreferredName + { + get + { + if (string.IsNullOrEmpty(Name)) + { + if (SourceDevice == null) + return "---"; + return SourceDevice.Name; + } + return Name; + } + } + + /// + /// A name that will override the source's name on the UI + /// + [JsonProperty("name")] + public string Name { get; set; } /// /// Specifies and icon for the source list item /// [JsonProperty("icon")] - public string Icon { get; set; } + public string Icon { get; set; } /// /// Alternate icon /// [JsonProperty("altIcon")] - public string AltIcon { get; set; } + public string AltIcon { get; set; } /// /// Indicates if the item should be included in the source list /// [JsonProperty("includeInSourceList")] - public bool IncludeInSourceList { get; set; } + public bool IncludeInSourceList { get; set; } /// /// Used to specify the order of the items in the source list when displayed /// [JsonProperty("order")] - public int Order { get; set; } + public int Order { get; set; } /// /// The key of the device for volume control /// [JsonProperty("volumeControlKey")] - public string VolumeControlKey { get; set; } + public string VolumeControlKey { get; set; } /// /// The type of source list item /// [JsonProperty("type")] - [JsonConverter(typeof(StringEnumConverter))] - public eSourceListItemType Type { get; set; } + [JsonConverter(typeof(StringEnumConverter))] + public eSourceListItemType Type { get; set; } /// /// The list of routes to execute for this source list item /// [JsonProperty("routeList")] - public List RouteList { get; set; } + public List RouteList { get; set; } /// /// Indicates if this source should be disabled for sharing to the far end call participants via codec content /// [JsonProperty("disableCodecSharing")] - public bool DisableCodecSharing { get; set; } + public bool DisableCodecSharing { get; set; } /// /// Indicates if this source should be disabled for routing to a shared output /// [JsonProperty("disableRoutedSharing")] - public bool DisableRoutedSharing { get; set; } + public bool DisableRoutedSharing { get; set; } [JsonProperty("destinations")] public List Destinations { get; set; } @@ -156,10 +149,10 @@ namespace PepperDash.Essentials.Core [JsonProperty("disableSimpleRouting")] public bool DisableSimpleRouting { get; set; } - public SourceListItem() - { - Icon = "Blank"; - } + public SourceListItem() + { + Icon = "Blank"; + } public override string ToString() { @@ -167,23 +160,23 @@ namespace PepperDash.Essentials.Core } } - public class SourceRouteListItem - { - [JsonProperty("sourceKey")] - public string SourceKey { get; set; } + 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("destinationKey")] + public string DestinationKey { get; set; } [JsonProperty("destinationPortKey")] public string DestinationPortKey { get; set; } - [JsonProperty("type")] - public eRoutingSignalType Type { get; set; } - } + [JsonProperty("type")] + public eRoutingSignalType Type { get; set; } + } /// /// Defines the valid destination types for SourceListItems in a room @@ -193,7 +186,12 @@ namespace PepperDash.Essentials.Core defaultDisplay, leftDisplay, rightDisplay, + centerDisplay, programAudio, - codecContent + codecContent, + frontLeftDisplay, + frontRightDisplay, + rearLeftDisplay, + rearRightDisplay, } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/PartitionSensor/EssentialsPartitionController.cs b/src/PepperDash.Essentials.Core/PartitionSensor/EssentialsPartitionController.cs index 225abb76..aca4f008 100644 --- a/src/PepperDash.Essentials.Core/PartitionSensor/EssentialsPartitionController.cs +++ b/src/PepperDash.Essentials.Core/PartitionSensor/EssentialsPartitionController.cs @@ -1,9 +1,5 @@ -using System; +using PepperDash.Core; using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using PepperDash.Core; namespace PepperDash.Essentials.Core { @@ -26,6 +22,11 @@ namespace PepperDash.Essentials.Core { get { + if (IsInAutoMode) + { + return _partitionSensor.PartitionPresentFeedback.BoolValue; + } + return _partitionPresent; } set @@ -73,11 +74,11 @@ namespace PepperDash.Essentials.Core PartitionPresentFeedback.FireUpdate(); } - void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) + private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) { if (IsInAutoMode) { - PartitionPresentFeedback.FireUpdate(); + PartitionPresent = e.BoolValue; } } @@ -87,6 +88,8 @@ namespace PepperDash.Essentials.Core public void SetAutoMode() { + Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Auto Mode", this); + IsInAutoMode = true; if (PartitionPresentFeedback != null) { @@ -101,11 +104,16 @@ namespace PepperDash.Essentials.Core { _partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange; _partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange; + PartitionPresent = _partitionSensor.PartitionPresentFeedback.BoolValue; } + + PartitionPresentFeedback.FireUpdate(); } public void SetManualMode() { + Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Setting {Key} to Manual Mode", this); + IsInAutoMode = false; if (PartitionPresentFeedback != null) { @@ -119,7 +127,10 @@ namespace PepperDash.Essentials.Core if (_partitionSensor != null) { _partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange; + PartitionPresent = _partitionSensor.PartitionPresentFeedback.BoolValue; } + + PartitionPresentFeedback.FireUpdate(); } diff --git a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs index dfc1066a..11086821 100644 --- a/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs +++ b/src/PepperDash.Essentials.Core/Room/Combining/EssentialsRoomCombiner.cs @@ -1,11 +1,12 @@ -using System; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Core.Logging; +using Serilog.Events; +using System; using System.Collections.Generic; using System.Linq; -using Crestron.SimplSharp; - -using PepperDash.Core; -using Serilog.Events; -using Newtonsoft.Json; +using System.Threading; +using System.Threading.Tasks; namespace PepperDash.Essentials.Core { @@ -35,7 +36,7 @@ namespace PepperDash.Essentials.Core } set { - if(value == _isInAutoMode) + if (value == _isInAutoMode) { return; } @@ -49,6 +50,8 @@ namespace PepperDash.Essentials.Core private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s + private Mutex _scenarioChange = new Mutex(); + public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props) : base(key) { @@ -93,7 +96,7 @@ namespace PepperDash.Essentials.Core }); } - void CreateScenarios() + private void CreateScenarios() { foreach (var scenarioConfig in _propertiesConfig.Scenarios) { @@ -102,21 +105,29 @@ namespace PepperDash.Essentials.Core } } - void SetRooms() + private void SetRooms() { _rooms = new List(); foreach (var roomKey in _propertiesConfig.RoomKeys) { - var room = DeviceManager.GetDeviceForKey(roomKey) as IEssentialsRoom; - if (room != null) + var room = DeviceManager.GetDeviceForKey(roomKey); + + if (DeviceManager.GetDeviceForKey(roomKey) is IEssentialsRoom essentialsRoom) { - _rooms.Add(room); + _rooms.Add(essentialsRoom); } } + + var rooms = DeviceManager.AllDevices.OfType().Cast(); + + foreach (var room in rooms) + { + room.Deactivate(); + } } - void SetupPartitionStateProviders() + private void SetupPartitionStateProviders() { foreach (var pConfig in _propertiesConfig.Partitions) { @@ -130,18 +141,18 @@ namespace PepperDash.Essentials.Core } } - void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) + private void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e) { StartDebounceTimer(); } - void StartDebounceTimer() + private void StartDebounceTimer() { // default to 500ms for manual mode var time = 500; // if in auto mode, debounce the scenario change - if(IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000; + if (IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000; if (_scenarioChangeDebounceTimer == null) { @@ -156,7 +167,7 @@ namespace PepperDash.Essentials.Core /// /// Determines the current room combination scenario based on the state of the partition sensors /// - void DetermineRoomCombinationScenario() + private void DetermineRoomCombinationScenario() { if (_scenarioChangeDebounceTimer != null) { @@ -164,14 +175,20 @@ namespace PepperDash.Essentials.Core _scenarioChangeDebounceTimer = null; } + this.LogInformation("Determining Combination Scenario"); + var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) => { + this.LogDebug("Checking scenario {scenarioKey}", s.Key); // iterate the partition states foreach (var partitionState in s.PartitionStates) { + this.LogDebug("checking PartitionState {partitionStateKey}", partitionState.PartitionKey); // get the partition by key var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey)); + this.LogDebug("Expected State: {partitionPresent} Actual State: {partitionState}", partitionState.PartitionPresent, partition.PartitionPresentFeedback.BoolValue); + if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue) { // the partition can't be found or the state doesn't match @@ -184,10 +201,41 @@ namespace PepperDash.Essentials.Core if (currentScenario != null) { - CurrentScenario = currentScenario; + this.LogInformation("Found combination Scenario {scenarioKey}", currentScenario.Key); + ChangeScenario(currentScenario); } } + private async Task ChangeScenario(IRoomCombinationScenario newScenario) + { + + + if (newScenario == _currentScenario) + { + return; + } + + // Deactivate the old scenario first + if (_currentScenario != null) + { + Debug.LogMessage(LogEventLevel.Information, "Deactivating scenario {currentScenario}", this, _currentScenario.Name); + await _currentScenario.Deactivate(); + } + + _currentScenario = newScenario; + + // Activate the new scenario + if (_currentScenario != null) + { + Debug.LogMessage(LogEventLevel.Debug, $"Current Scenario: {_currentScenario.Name}", this); + await _currentScenario.Activate(); + } + + RoomCombinationScenarioChanged?.Invoke(this, new EventArgs()); + + + } + #region IEssentialsRoomCombiner Members public event EventHandler RoomCombinationScenarioChanged; @@ -198,33 +246,6 @@ namespace PepperDash.Essentials.Core { return _currentScenario; } - private set - { - if (value != _currentScenario) - { - // Deactivate the old scenario first - if (_currentScenario != null) - { - _currentScenario.Deactivate(); - } - - _currentScenario = value; - - // Activate the new scenario - if (_currentScenario != null) - { - _currentScenario.Activate(); - - Debug.LogMessage(LogEventLevel.Debug, $"Current Scenario: {_currentScenario.Name}", this); - } - - var handler = RoomCombinationScenarioChanged; - if (handler != null) - { - handler(this, new EventArgs()); - } - } - } } public BoolFeedback IsInAutoModeFeedback { get; private set; } @@ -232,16 +253,35 @@ namespace PepperDash.Essentials.Core public void SetAutoMode() { IsInAutoMode = true; + + foreach (var partition in Partitions) + { + partition.SetAutoMode(); + } + + DetermineRoomCombinationScenario(); } public void SetManualMode() { IsInAutoMode = false; + + foreach (var partition in Partitions) + { + partition.SetManualMode(); + } } public void ToggleMode() { - IsInAutoMode = !IsInAutoMode; + if (IsInAutoMode) + { + SetManualMode(); + } + else + { + SetAutoMode(); + } } public List RoomCombinationScenarios { get; private set; } diff --git a/src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs b/src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs index 0d4a40cc..fefdc2da 100644 --- a/src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs +++ b/src/PepperDash.Essentials.Core/Room/Combining/IEssentialsRoomCombiner.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Newtonsoft.Json; using PepperDash.Core; @@ -87,12 +88,12 @@ namespace PepperDash.Essentials.Core /// /// Activates this room combination scenario /// - void Activate(); + Task Activate(); /// /// Deactivates this room combination scenario /// - void Deactivate(); + Task Deactivate(); /// /// The state of the partitions that would activate this scenario diff --git a/src/PepperDash.Essentials.Core/Room/Combining/RoomCombinationScenario.cs b/src/PepperDash.Essentials.Core/Room/Combining/RoomCombinationScenario.cs index 9bc0d8ec..f36b807c 100644 --- a/src/PepperDash.Essentials.Core/Room/Combining/RoomCombinationScenario.cs +++ b/src/PepperDash.Essentials.Core/Room/Combining/RoomCombinationScenario.cs @@ -1,22 +1,16 @@ - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; - +using Newtonsoft.Json; using PepperDash.Core; - -using Newtonsoft.Json; +using PepperDash.Core.Logging; using Serilog.Events; +using System.Collections.Generic; +using System.Threading.Tasks; namespace PepperDash.Essentials.Core { /// /// Represents a room combination scenario /// - public class RoomCombinationScenario: IRoomCombinationScenario, IKeyName + public class RoomCombinationScenario : IRoomCombinationScenario, IKeyName { private RoomCombinationScenarioConfig _config; @@ -40,7 +34,7 @@ namespace PepperDash.Essentials.Core get { return _isActive; } set { - if(value == _isActive) + if (value == _isActive) { return; } @@ -76,33 +70,43 @@ namespace PepperDash.Essentials.Core IsActiveFeedback = new BoolFeedback(() => _isActive); } - public void Activate() + public async Task Activate() { - Debug.LogMessage(LogEventLevel.Debug, "Activating Scenario: '{0}' with {1} action(s) defined", Name, activationActions.Count); + this.LogInformation("Activating Scenario {name} with {activationActionCount} action(s) defined", Name, activationActions.Count); + + List tasks = new List(); if (activationActions != null) { foreach (var action in activationActions) { - DeviceJsonApi.DoDeviceAction(action); + this.LogInformation("Running Activation action {@action}", action); + tasks.Add(DeviceJsonApi.DoDeviceActionAsync(action)); } } + await Task.WhenAll(tasks); + IsActive = true; } - public void Deactivate() + public async Task Deactivate() { - Debug.LogMessage(LogEventLevel.Debug, "Deactivating Scenario: '{0}' with {1} action(s) defined", Name, deactivationActions.Count); + this.LogInformation("Deactivating Scenario {name} with {deactivationActionCount} action(s) defined", Name, deactivationActions.Count); + + List tasks = new List(); if (deactivationActions != null) { foreach (var action in deactivationActions) { - DeviceJsonApi.DoDeviceAction(action); + this.LogInformation("Running deactivation action {actionDeviceKey}:{actionMethod}", action.DeviceKey, action.MethodName); + tasks.Add( DeviceJsonApi.DoDeviceActionAsync(action)); } } + await Task.WhenAll(tasks); + IsActive = false; } diff --git a/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomConfig.cs b/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomConfig.cs index 32e1feda..3f457176 100644 --- a/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomConfig.cs +++ b/src/PepperDash.Essentials.Core/Room/Config/EssentialsRoomConfig.cs @@ -200,6 +200,9 @@ namespace PepperDash.Essentials.Room.Config public string DestinationListKey { get; set; } [JsonProperty("audioControlPointListKey")] public string AudioControlPointListKey { get; set; } + [JsonProperty("cameraListKey")] + public string CameraListKey { get; set; } + [JsonProperty("defaultSourceItem")] public string DefaultSourceItem { get; set; } diff --git a/src/PepperDash.Essentials.Core/Room/EssentialsRoomBase.cs b/src/PepperDash.Essentials.Core/Room/EssentialsRoomBase.cs index be86b70f..232c02ed 100644 --- a/src/PepperDash.Essentials.Core/Room/EssentialsRoomBase.cs +++ b/src/PepperDash.Essentials.Core/Room/EssentialsRoomBase.cs @@ -133,7 +133,28 @@ namespace PepperDash.Essentials.Core } } - + private string _cameraListKey; + public string CameraListKey + { + get + { + if (string.IsNullOrEmpty(_cameraListKey)) + { + return _defaultListKey; + } + else + { + return _cameraListKey; + } + } + protected set + { + if (value != _cameraListKey) + { + _cameraListKey = value; + } + } + } /// /// Timer used for informing the UIs of a shutdown diff --git a/src/PepperDash.Essentials.Core/Room/IEssentialsRoom.cs b/src/PepperDash.Essentials.Core/Room/IEssentialsRoom.cs index 4bf51376..d452c0b9 100644 --- a/src/PepperDash.Essentials.Core/Room/IEssentialsRoom.cs +++ b/src/PepperDash.Essentials.Core/Room/IEssentialsRoom.cs @@ -31,6 +31,8 @@ namespace PepperDash.Essentials.Core string AudioControlPointListKey { get; } + string CameraListKey { get; } + SecondsCountdownTimer ShutdownPromptTimer { get; } int ShutdownPromptSeconds { get; } int ShutdownVacancySeconds { get; } diff --git a/src/PepperDash.Essentials.Core/Routing/Extensions.cs b/src/PepperDash.Essentials.Core/Routing/Extensions.cs index a0eba868..6d3a98fd 100644 --- a/src/PepperDash.Essentials.Core/Routing/Extensions.cs +++ b/src/PepperDash.Essentials.Core/Routing/Extensions.cs @@ -1,8 +1,9 @@ -using PepperDash.Core; -using Serilog.Events; +using Serilog.Events; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using Debug = PepperDash.Core.Debug; namespace PepperDash.Essentials.Core @@ -20,21 +21,36 @@ namespace PepperDash.Essentials.Core /// 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 - /// + /// public static void ReleaseAndMakeRoute(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, string destinationPortKey = "", string sourcePortKey = "") { + // Remove this line before committing!!!!! + var frame = new StackFrame(1, true); + Debug.LogMessage(LogEventLevel.Information, "ReleaseAndMakeRoute Called from {method} with params {destinationKey}:{sourceKey}:{signalType}:{destinationPortKey}:{sourcePortKey}", frame.GetMethod().Name, destination.Key, source.Key, signalType.ToString(), destinationPortKey, 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); } + public static void RemoveRouteRequestForDestination(string destinationKey) + { + Debug.LogMessage(LogEventLevel.Information, "Removing route request for {destination}", null, destinationKey); + + var result = RouteRequests.Remove(destinationKey); + + var messageTemplate = result ? "Route Request for {destination} removed" : "Route Request for {destination} not found"; + + Debug.LogMessage(LogEventLevel.Information, messageTemplate, null, destinationKey); + } + private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort = null, RoutingOutputPort sourcePort = null) { if (destination == null) throw new ArgumentNullException(nameof(destination)); if (source == null) throw new ArgumentNullException(nameof(source)); - if (destinationPort == null) Debug.LogMessage(LogEventLevel.Verbose, "Destination port is null"); - if (sourcePort == null) Debug.LogMessage(LogEventLevel.Verbose, "Source port is null"); + if (destinationPort == null) Debug.LogMessage(LogEventLevel.Information, "Destination port is null"); + if (sourcePort == null) Debug.LogMessage(LogEventLevel.Information, "Source port is null"); var routeRequest = new RouteRequest { @@ -57,7 +73,7 @@ namespace PepperDash.Essentials.Core 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); + Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is cooling down and already has a routing request stored. Storing new route request to route to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key); return; } @@ -71,17 +87,17 @@ namespace PepperDash.Essentials.Core 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); + Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is cooling down. Storing route request to route to source key: {sourceKey}", 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); + Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key); } - destination.ReleaseRoute(); + destination.ReleaseRoute(destinationPort?.Key ?? string.Empty); RunRouteRequest(routeRequest); } @@ -109,14 +125,21 @@ namespace PepperDash.Essentials.Core videoRoute?.ExecuteRoutes(); } + public static void ReleaseRoute(this IRoutingInputs destination) + { + ReleaseRoute(destination, string.Empty); + } + /// /// Will release the existing route on the destination, if it is found in /// RouteDescriptorCollection.DefaultCollection /// - /// - public static void ReleaseRoute(this IRoutingInputs destination) + /// + public static void ReleaseRoute(this IRoutingInputs destination, string inputPortKey) { + Debug.LogMessage(LogEventLevel.Information, "Release route for {inputPortKey}", destination, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey); + if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling) { var coolingDevice = destination as IWarmingCooling; @@ -126,7 +149,7 @@ namespace PepperDash.Essentials.Core RouteRequests.Remove(destination.Key); - var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination); + var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination, inputPortKey); if (current != null) { Debug.LogMessage(LogEventLevel.Debug, "Releasing current route: {0}", destination, current.Source.Key); @@ -146,7 +169,7 @@ namespace PepperDash.Essentials.Core // if it's a single signal type, find the route if (!signalType.HasFlag(eRoutingSignalType.AudioVideo)) { - var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, signalType); + var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, signalType); Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key, signalType); if (!destination.GetRouteToSource(source, null, null, signalType, 0, singleTypeRouteDescriptor, destinationPort, sourcePort)) @@ -163,14 +186,14 @@ namespace PepperDash.Essentials.Core Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key); - var audioRouteDescriptor = new RouteDescriptor(source, destination, eRoutingSignalType.Audio); + var audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Audio); var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, audioRouteDescriptor, destinationPort, sourcePort); if (!audioSuccess) Debug.LogMessage(LogEventLevel.Debug, "Cannot find audio route to {0}", destination, source.Key); - var videoRouteDescriptor = new RouteDescriptor(source, destination, eRoutingSignalType.Video); + var videoRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Video); var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, videoRouteDescriptor, destinationPort, sourcePort); diff --git a/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs b/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs index ebacc809..fa486c07 100644 --- a/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs +++ b/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs @@ -14,19 +14,27 @@ namespace PepperDash.Essentials.Core public class RouteDescriptor { public IRoutingInputs Destination { get; private set; } + + public RoutingInputPort InputPort { get; private set; } + public IRoutingOutputs Source { get; private set; } public eRoutingSignalType SignalType { get; private set; } public List Routes { get; private set; } - public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) + public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType):this(source,destination, null, signalType) { - Destination = destination; - Source = source; - SignalType = signalType; - Routes = new List(); } + public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, RoutingInputPort inputPort, eRoutingSignalType signalType) + { + Destination = destination; + Source = source; + SignalType = signalType; + InputPort = inputPort; + Routes = new List(); + } + /// /// Executes all routes described in this collection. Typically called via /// extension method IRoutingInputs.ReleaseAndMakeRoute() @@ -60,10 +68,12 @@ namespace PepperDash.Essentials.Core /// public void ReleaseRoutes() { - foreach (var route in Routes) + foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting)) { - if (route.SwitchingDevice is IRouting) + if (route.SwitchingDevice is IRouting switchingDevice) { + switchingDevice.ExecuteSwitch(null, route.OutputPort.Selector, SignalType); + // 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); diff --git a/src/PepperDash.Essentials.Core/Routing/RouteDescriptorCollection.cs b/src/PepperDash.Essentials.Core/Routing/RouteDescriptorCollection.cs index c9e3da37..6c4a5df5 100644 --- a/src/PepperDash.Essentials.Core/Routing/RouteDescriptorCollection.cs +++ b/src/PepperDash.Essentials.Core/Routing/RouteDescriptorCollection.cs @@ -1,8 +1,7 @@ -using System.Collections.Generic; -using System.Linq; - -using PepperDash.Core; +using PepperDash.Core; using Serilog.Events; +using System.Collections.Generic; +using System.Linq; namespace PepperDash.Essentials.Core @@ -33,7 +32,13 @@ namespace PepperDash.Essentials.Core /// public void AddRouteDescriptor(RouteDescriptor descriptor) { - if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination)) + if (descriptor == null) + { + return; + } + + if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination) + && RouteDescriptors.Any(t => t.Destination == descriptor.Destination && t.InputPort != null && descriptor.InputPort != null && t.InputPort.Key == descriptor.InputPort.Key)) { Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination, "Route to [{0}] already exists in global routes table", descriptor.Source.Key); @@ -48,18 +53,33 @@ namespace PepperDash.Essentials.Core /// null if no RouteDescriptor for a destination exists public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination) { + Debug.LogMessage(LogEventLevel.Debug, "Getting route descriptor", destination); + return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination); } + public RouteDescriptor GetRouteDescriptorForDestinationAndInputPort(IRoutingInputs destination, string inputPortKey) + { + Debug.LogMessage(LogEventLevel.Debug, "Getting route descriptor for {inputPortKey}", destination, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey); + return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination && rd.InputPort != null && rd.InputPort.Key == inputPortKey); + } + /// /// Returns the RouteDescriptor for a given destination AND removes it from collection. /// Returns null if no route with the provided destination exists. /// - public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination) + public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination, string inputPortKey = "") { - var descr = GetRouteDescriptorForDestination(destination); + Debug.LogMessage(LogEventLevel.Debug, "Removing route descriptor for {inputPortKey}", destination, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey); + + var descr = string.IsNullOrEmpty(inputPortKey) + ? GetRouteDescriptorForDestination(destination) + : GetRouteDescriptorForDestinationAndInputPort(destination, inputPortKey); if (descr != null) RouteDescriptors.Remove(descr); + + Debug.LogMessage(LogEventLevel.Debug, "Found route descriptor {routeDescriptor}", destination, descr); + return descr; } } diff --git a/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs b/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs index 35169474..0f51d174 100644 --- a/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs +++ b/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs @@ -1,4 +1,7 @@ -namespace PepperDash.Essentials.Core +using PepperDash.Core; +using Serilog.Events; + +namespace PepperDash.Essentials.Core { public class RouteRequest { @@ -11,37 +14,21 @@ public void HandleCooldown(object sender, FeedbackEventArgs args) { - var coolingDevice = sender as IWarmingCooling; + Debug.LogMessage(LogEventLevel.Information, "Handling cooldown route request: {destination}:{destinationPort} -> {source}:{sourcePort} {type}", null, Destination.Key, DestinationPort.Key, Source.Key, SourcePort.Key, SignalType.ToString()); - if (args.BoolValue == false) + if (args.BoolValue == true) { - Destination.ReleaseAndMakeRoute(Source, SignalType); + return; + } - if (sender == null) return; + Debug.LogMessage(LogEventLevel.Information, "Cooldown complete. Making route from {destination} to {source}", Destination.Key, Source.Key); + Destination.ReleaseAndMakeRoute(Source, SignalType, DestinationPort?.Key ?? string.Empty, SourcePort?.Key ?? string.Empty); + if (sender is IWarmingCooling coolingDevice) + { + Debug.LogMessage(LogEventLevel.Debug, "Unsubscribing from cooling feedback for {destination}", null, Destination.Key); coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown; } } } - - /*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; - } - } - }*/ } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Devices.Common/Codec/ICiscoCodecCameraConfig.cs b/src/PepperDash.Essentials.Devices.Common/Codec/ICiscoCodecCameraConfig.cs new file mode 100644 index 00000000..fefb4b09 --- /dev/null +++ b/src/PepperDash.Essentials.Devices.Common/Codec/ICiscoCodecCameraConfig.cs @@ -0,0 +1,31 @@ +using Crestron.SimplSharpPro; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + /// + /// Describes a cisco codec device that can allow configuration of cameras + /// + public interface ICiscoCodecCameraConfig + { + void SetCameraAssignedSerialNumber(uint cameraId, string serialNumber); + + void SetCameraName(uint videoConnectorId, string name); + + void SetInputSourceType(uint videoConnectorId, eCiscoCodecInputSourceType sourceType); + } + + public enum eCiscoCodecInputSourceType + { + PC, + camera, + document_camera, + mediaplayer, + other, + whiteboard + } +}