diff --git a/PepperDash.Essentials.sln b/PepperDash.Essentials.sln index 4a9ffd83..40da3b9b 100644 --- a/PepperDash.Essentials.sln +++ b/PepperDash.Essentials.sln @@ -3,13 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33213.308 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.Devices.Common", "src\PepperDash.Essentials.Devices.Common\PepperDash.Essentials.Devices.Common.csproj", "{53E204B7-97DD-441D-A96C-721DF014DF82}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials.Devices.Common", "src\PepperDash.Essentials.Devices.Common\PepperDash.Essentials.Devices.Common.csproj", "{53E204B7-97DD-441D-A96C-721DF014DF82}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.DM", "src\PepperDash.Essentials.DM\PepperDash.Essentials.DM.csproj", "{08EB4B98-9B4D-455A-81E0-4F913E08ADB5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials", "src\PepperDash.Essentials\PepperDash.Essentials.csproj", "{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials", "src\PepperDash.Essentials\PepperDash.Essentials.csproj", "{CB3B11BA-625C-4D35-B663-FDC5BE9A230E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash.Essentials.Core", "src\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj", "{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PepperDash.Essentials.Core", "src\PepperDash.Essentials.Core\PepperDash.Essentials.Core.csproj", "{3D192FED-8FFC-4CB5-B5F7-BA307ABA254B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,10 +19,6 @@ Global {53E204B7-97DD-441D-A96C-721DF014DF82}.Debug|Any CPU.Build.0 = Debug|Any CPU {53E204B7-97DD-441D-A96C-721DF014DF82}.Release|Any CPU.ActiveCfg = Release|Any CPU {53E204B7-97DD-441D-A96C-721DF014DF82}.Release|Any CPU.Build.0 = Release|Any CPU - {08EB4B98-9B4D-455A-81E0-4F913E08ADB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08EB4B98-9B4D-455A-81E0-4F913E08ADB5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08EB4B98-9B4D-455A-81E0-4F913E08ADB5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08EB4B98-9B4D-455A-81E0-4F913E08ADB5}.Release|Any CPU.Build.0 = Release|Any CPU {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Debug|Any CPU.Build.0 = Debug|Any CPU {CB3B11BA-625C-4D35-B663-FDC5BE9A230E}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/outputPepperDash.Essentials.2.0.0-local.cpz b/outputPepperDash.Essentials.2.0.0-local.cpz new file mode 100644 index 00000000..cd80132e Binary files /dev/null and b/outputPepperDash.Essentials.2.0.0-local.cpz differ diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 81304ace..fe9bec08 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -19,8 +19,8 @@ - - + + diff --git a/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs b/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs index bc8af721..1846a512 100644 --- a/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs +++ b/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs @@ -121,6 +121,41 @@ namespace PepperDash.Essentials.Core #endregion } + public abstract class ProcessorExtensionDeviceFactory : IProcessorExtensionDeviceFactory where T: EssentialsDevice + { + #region IProcessorExtensionDeviceFactory Members + + /// + /// A list of strings that can be used in the type property of a DeviceConfig object to build an instance of this device + /// + public List TypeNames { get; protected set; } + + /// + /// Loads an item to the ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods dictionary for each entry in the TypeNames list + /// + public void LoadFactories() + { + foreach (var typeName in TypeNames) + { + //Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); + var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[]; + string description = descriptionAttribute[0].Description; + var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[]; + ProcessorExtensionDeviceFactory.AddFactoryForType(typeName.ToLower(), description, typeof(T), BuildDevice); + } + } + + /// + /// The method that will build the device + /// + /// The device config + /// An instance of the device + public abstract EssentialsDevice BuildDevice(DeviceConfig dc); + + #endregion + + } + /// /// Devices the basic needs for a Device Factory /// diff --git a/src/PepperDash.Essentials.Core/Factory/DeviceFactory.cs b/src/PepperDash.Essentials.Core/Factory/DeviceFactory.cs index d53aef19..f1c77e74 100644 --- a/src/PepperDash.Essentials.Core/Factory/DeviceFactory.cs +++ b/src/PepperDash.Essentials.Core/Factory/DeviceFactory.cs @@ -1,20 +1,11 @@ extern alias Full; - +using Crestron.SimplSharp.Reflection; +using Full.Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.GeneralIO; -using Crestron.SimplSharp.Reflection; -using PepperDash.Core; -using Full.Newtonsoft.Json.Linq; -using Full.Newtonsoft.Json; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.CrestronIO; -using PepperDash.Essentials.Core.Touchpanels; namespace PepperDash.Essentials.Core { diff --git a/src/PepperDash.Essentials.Core/Factory/IProcessorExtensionDeviceFactory.cs b/src/PepperDash.Essentials.Core/Factory/IProcessorExtensionDeviceFactory.cs new file mode 100644 index 00000000..a50ab16d --- /dev/null +++ b/src/PepperDash.Essentials.Core/Factory/IProcessorExtensionDeviceFactory.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PepperDash.Essentials.Core +{ + public interface IProcessorExtensionDeviceFactory + { + /// + /// Loads all the extension factories to the ProcessorExtensionDeviceFactory + /// + void LoadFactories(); + } +} diff --git a/src/PepperDash.Essentials.Core/Factory/ProcessorExtensionDeviceFactory.cs b/src/PepperDash.Essentials.Core/Factory/ProcessorExtensionDeviceFactory.cs new file mode 100644 index 00000000..a5b57ad3 --- /dev/null +++ b/src/PepperDash.Essentials.Core/Factory/ProcessorExtensionDeviceFactory.cs @@ -0,0 +1,155 @@ +extern alias Full; +using Crestron.SimplSharp.Reflection; +using Full.Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PepperDash.Essentials.Core +{ + + public class ProcessorExtensionDeviceFactory + { + public ProcessorExtensionDeviceFactory() { + var assy = Assembly.GetExecutingAssembly(); + PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy); + + var extensions = assy.GetTypes().Where(ct => typeof(IProcessorExtensionDeviceFactory) + .IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract); + + if (extensions != null ) + { + foreach ( var extension in extensions ) + { + try + { + var factory = (IProcessorExtensionDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(extension); + factory.LoadFactories(); + } + catch( Exception e ) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to load extension device: '{1}' ProcessorExtensionDeviceFactory: {0}", e, extension.Name); + } + } + } + } + + /// + /// A dictionary of factory methods, keyed by config types, added by plugins. + /// These methods are looked up and called by GetDevice in this class. + /// + static Dictionary ProcessorExtensionFactoryMethods = + new Dictionary(StringComparer.OrdinalIgnoreCase); + + + /// + /// Adds a plugin factory method + /// + /// + /// + public static void AddFactoryForType(string extensionName, Func method) + { + //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, new DeviceFactoryWrapper() { FactoryMethod = method }); + } + + public static void AddFactoryForType(string extensionName, string description, CType cType, Func method) + { + //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + + if (ProcessorExtensionFactoryMethods.ContainsKey(extensionName)) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to add extension device: '{0}'. Already exists in ProcessorExtensionDeviceFactory", extensionName); + return; + } + + var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method }; + ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper); + } + + private static void CheckForSecrets(IEnumerable obj) + { + foreach (var prop in obj.Where(prop => prop.Value as Full.Newtonsoft.Json.Linq.JObject != null)) + { + if (prop.Name.ToLower() == "secret") + { + var secret = GetSecret(prop.Children().First().ToObject()); + //var secret = GetSecret(JsonConvert.DeserializeObject(prop.Children().First().ToString())); + prop.Parent.Replace(secret); + } + var recurseProp = prop.Value as Full.Newtonsoft.Json.Linq.JObject; + if (recurseProp == null) return; + CheckForSecrets(recurseProp.Properties()); + } + } + + private static string GetSecret(SecretsPropertiesConfig data) + { + var secretProvider = SecretsManager.GetSecretProviderByKey(data.Provider); + if (secretProvider == null) return null; + var secret = secretProvider.GetSecret(data.Key); + if (secret != null) return (string)secret.Value; + Debug.Console(1, + "Unable to retrieve secret {0}{1} - Make sure you've added it to the secrets provider", + data.Provider, data.Key); + return String.Empty; + } + + /// + /// The factory method for processor extension devices. Also iterates the Factory methods that have + /// been loaded from plugins + /// + /// + /// + public static IKeyed GetExtensionDevice(DeviceConfig dc) + { + try + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading '{0}' from Essentials Core", dc.Type); + + var localDc = new DeviceConfig(dc); + + var key = localDc.Key; + var name = localDc.Name; + var type = localDc.Type; + var properties = localDc.Properties; + //var propRecurse = properties; + + var typeName = localDc.Type.ToLower(); + + var jObject = properties as Full.Newtonsoft.Json.Linq.JObject; + if (jObject != null) + { + var jProp = jObject.Properties(); + + CheckForSecrets(jProp); + } + + Debug.Console(2, "typeName = {0}", typeName); + // Check for types that have been added by plugin dlls. + return !ProcessorExtensionFactoryMethods.ContainsKey(typeName) ? null : ProcessorExtensionFactoryMethods[typeName].FactoryMethod(localDc); + } + catch (Exception ex) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Exception occurred while creating device {0}: {1}", dc.Key, ex.Message); + + Debug.Console(2, "{0}", ex.StackTrace); + + if (ex.InnerException == null) + { + return null; + } + + Debug.Console(0, Debug.ErrorLogLevel.Error, "Inner exception while creating device {0}: {1}", dc.Key, + ex.InnerException.Message); + Debug.Console(2, "{0}", ex.InnerException.StackTrace); + return null; + } + } + + } + +} diff --git a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj index 7360df12..db87a84d 100644 --- a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj +++ b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj @@ -27,7 +27,7 @@ - + Full diff --git a/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj b/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj index 0837b2df..96ef4e5d 100644 --- a/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj +++ b/src/PepperDash.Essentials.Devices.Common/PepperDash.Essentials.Devices.Common.csproj @@ -27,7 +27,7 @@ - + Full diff --git a/src/PepperDash.Essentials/Audio/EssentialsVolumeLevelConfig.cs b/src/PepperDash.Essentials/Audio/EssentialsVolumeLevelConfig.cs index 70fcd36f..c1b1cc2c 100644 --- a/src/PepperDash.Essentials/Audio/EssentialsVolumeLevelConfig.cs +++ b/src/PepperDash.Essentials/Audio/EssentialsVolumeLevelConfig.cs @@ -1,18 +1,8 @@ extern alias Full; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Crestron.SimplSharp; -using Full.Newtonsoft.Json; - -using PepperDash.Core; using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Devices.Common.DSP; -using PepperDash.Essentials.DM; +using System; +using System.Text.RegularExpressions; namespace PepperDash.Essentials { @@ -35,45 +25,5 @@ namespace PepperDash.Essentials public string DeviceKey { get; set; } public string Label { get; set; } public int Level { get; set; } - - /// - /// Helper to get the device associated with key - one timer. - /// - public IBasicVolumeWithFeedback GetDevice() - { - // DM output card format: deviceKey--output~number, dm8x8-1--output~4 - var match = Regex.Match(DeviceKey, @"([-_\w]+)--(\w+)~(\d+)"); - if (match.Success) - { - var devKey = match.Groups[1].Value; - var chassis = DeviceManager.GetDeviceForKey(devKey) as DmChassisController; - if (chassis != null) - { - var outputNum = Convert.ToUInt32(match.Groups[3].Value); - if (chassis.VolumeControls.ContainsKey(outputNum)) // should always... - return chassis.VolumeControls[outputNum]; - } - // No volume for some reason. We have failed as developers - return null; - } - - // DSP format: deviceKey--levelName, biampTesira-1--master - match = Regex.Match(DeviceKey, @"([-_\w]+)--(.+)"); - if (match.Success) - { - var devKey = match.Groups[1].Value; - var dsp = DeviceManager.GetDeviceForKey(devKey) as BiampTesiraForteDsp; - if (dsp != null) - { - var levelTag = match.Groups[2].Value; - if (dsp.LevelControlPoints.ContainsKey(levelTag)) // should always... - return dsp.LevelControlPoints[levelTag]; - } - // No volume for some reason. We have failed as developers - return null; - } - - return null; - } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials/ControlSystem.cs b/src/PepperDash.Essentials/ControlSystem.cs index 077a2b1a..04a4d992 100644 --- a/src/PepperDash.Essentials/ControlSystem.cs +++ b/src/PepperDash.Essentials/ControlSystem.cs @@ -1,28 +1,23 @@ extern alias Full; - -using System; -using System.Collections.Generic; -using System.Linq; using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.Reflection; using Crestron.SimplSharpPro; using Crestron.SimplSharpPro.CrestronThread; using Crestron.SimplSharpPro.Diagnostics; -using Crestron.SimplSharp.Reflection; - +using Full.Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.Fusion; -// using PepperDash.Essentials.Core.Web; using PepperDash.Essentials.Devices.Common; -using PepperDash.Essentials.DM; using PepperDash.Essentials.Fusion; using PepperDash.Essentials.Room.Config; - -using Full.Newtonsoft.Json; -using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using System; +using System.Collections.Generic; +using System.Linq; namespace PepperDash.Essentials { @@ -231,9 +226,10 @@ namespace PepperDash.Essentials new Core.DeviceFactory(); new Devices.Common.DeviceFactory(); - new DM.DeviceFactory(); new DeviceFactory(); + new Core.ProcessorExtensionDeviceFactory(); + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials load from configuration"); var filesReady = SetupFilesystem(); @@ -334,13 +330,14 @@ namespace PepperDash.Essentials void Load() { LoadDevices(); - LoadTieLines(); LoadRooms(); LoadLogoServer(); DeviceManager.ActivateAll(); - var mobileControl = GetMobileControlDevice(); + LoadTieLines(); + + var mobileControl = GetMobileControlDevice(); if (mobileControl == null) return; @@ -367,6 +364,7 @@ namespace PepperDash.Essentials foreach (var devConf in ConfigReader.ConfigObject.Devices) { + IKeyed newDev = null; try { @@ -384,47 +382,55 @@ namespace PepperDash.Essentials "WARNING: Config file defines processor type as '{0}' but actual processor is '{1}'! Some ports may not be available", devConf.Type.ToUpper(), Global.ControlSystem.ControllerPrompt.ToUpper()); + //if (newDev == null) + // newDev = PepperDash.Essentials.Core.ProcessorExtensionDeviceFactory.GetExtensionDevice(devConf); + + //if (newDev != null) + //{ + // DeviceManager.AddDevice(newDev); + + // continue; + //} + // Check if the processor is a DMPS model - if (this.ControllerPrompt.IndexOf("dmps", StringComparison.OrdinalIgnoreCase) > -1) - { - Debug.Console(2, "Adding DmpsRoutingController for {0} to Device Manager.", this.ControllerPrompt); + //if (this.ControllerPrompt.IndexOf("dmps", StringComparison.OrdinalIgnoreCase) > -1) + //{ + // Debug.Console(2, "Adding DmpsRoutingController for {0} to Device Manager.", this.ControllerPrompt); - var propertiesConfig = JsonConvert.DeserializeObject(devConf.Properties.ToString()); + // var propertiesConfig = JsonConvert.DeserializeObject(devConf.Properties.ToString()); - if(propertiesConfig == null) - propertiesConfig = new DM.Config.DmpsRoutingPropertiesConfig(); + // if(propertiesConfig == null) + // propertiesConfig = new DM.Config.DmpsRoutingPropertiesConfig(); - DeviceManager.AddDevice(DmpsRoutingController.GetDmpsRoutingController("processor-avRouting", this.ControllerPrompt, propertiesConfig)); - } - else if (this.ControllerPrompt.IndexOf("mpc3", StringComparison.OrdinalIgnoreCase) > -1) - { - Debug.Console(2, "MPC3 processor type detected. Adding Mpc3TouchpanelController."); + // DeviceManager.AddDevice(DmpsRoutingController.GetDmpsRoutingController("processor-avRouting", this.ControllerPrompt, propertiesConfig)); + //} + //else - var butToken = devConf.Properties["buttons"]; - if (butToken != null) - { - var buttons = butToken.ToObject>(); - var tpController = new Essentials.Core.Touchpanels.Mpc3TouchpanelController(devConf.Key, devConf.Name, Global.ControlSystem, buttons); - DeviceManager.AddDevice(tpController); - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Error, "Error: Unable to deserialize buttons collection for device: {0}", devConf.Key); - } + //if (this.ControllerPrompt.IndexOf("mpc3", StringComparison.OrdinalIgnoreCase) > -1) + //{ + // Debug.Console(2, "MPC3 processor type detected. Adding Mpc3TouchpanelController."); + + // var butToken = devConf.Properties["buttons"]; + // if (butToken != null) + // { + // var buttons = butToken.ToObject>(); + // var tpController = new Essentials.Core.Touchpanels.Mpc3TouchpanelController(devConf.Key, devConf.Name, Global.ControlSystem, buttons); + // DeviceManager.AddDevice(tpController); + // } + // else + // { + // Debug.Console(0, Debug.ErrorLogLevel.Error, "Error: Unable to deserialize buttons collection for device: {0}", devConf.Key); + // } - } - else - { - Debug.Console(2, "************Processor is not DMPS type***************"); - } - - + //} + //else + //{ + // Debug.Console(2, "************Processor is not DMPS type***************"); + //} continue; } - // Try local factories first - IKeyed newDev = null; if (newDev == null) newDev = PepperDash.Essentials.Core.DeviceFactory.GetDevice(devConf); diff --git a/src/PepperDash.Essentials/PepperDash.Essentials.csproj b/src/PepperDash.Essentials/PepperDash.Essentials.csproj index 9d1cd4c2..bcc0e152 100644 --- a/src/PepperDash.Essentials/PepperDash.Essentials.csproj +++ b/src/PepperDash.Essentials/PepperDash.Essentials.csproj @@ -46,7 +46,7 @@ - + Full @@ -55,6 +55,5 @@ - \ No newline at end of file