diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 2049ef34..143929c9 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -61,6 +61,8 @@ namespace PepperDash.Essentials CrestronConsole.AddNewConsoleCommand(PepperDash.Essentials.Core.DeviceFactory.GetDeviceFactoryTypes, "gettypes", "Gets the device types that can be built. Accepts a filter string.", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(PepperDash.Essentials.Core.Room.Components.ComponentFactory.GetComponentFactoryTypes, "getcomponenttypes", "Gets the components types that can be built. Accepts a filter string.", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(BridgeHelper.PrintJoinMap, "getjoinmap", "map(s) for bridge or device on bridge [brKey [devKey]]", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(s => @@ -437,7 +439,7 @@ namespace PepperDash.Essentials } else { - + room = new ComponentRoom(roomConfig) } if (room != null && room is EssentialsRoomBase) @@ -472,9 +474,9 @@ namespace PepperDash.Essentials } } - else if (room is ComponentRoom) + else if (room != null && room is ComponentRoom) { - + } else Debug.Console(0, Debug.ErrorLogLevel.Notice, "Notice: Cannot create room from config, key '{0}' - Is this intentional? This may be a valid configuration.", roomConfig.Key); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs index 8d7f2c8d..781101d4 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs @@ -1,113 +1,113 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharp.Reflection; - -using PepperDash.Core; -using PepperDash.Essentials.Core.Config; - -namespace PepperDash.Essentials.Core -{ - /// - /// Defines the basic needs for an EssentialsDevice to enable it to be build by an IDeviceFactory class - /// - [Description("The base Essentials Device Class")] - public abstract class EssentialsDevice : Device - { - protected EssentialsDevice(string key) - : base(key) - { - - } - - protected EssentialsDevice(string key, string name) - : base(key, name) - { - - } - } - - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] - public class DescriptionAttribute : Attribute - { - private string _Description; - - public DescriptionAttribute(string description) - { - Debug.Console(2, "Setting Description: {0}", description); - _Description = description; - } - - public string Description - { - get { return _Description; } - } - } - - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] - public class ConfigSnippetAttribute : Attribute - { - private string _ConfigSnippet; - - public ConfigSnippetAttribute(string configSnippet) - { - Debug.Console(2, "Setting Config Snippet {0}", configSnippet); - _ConfigSnippet = configSnippet; - } - - public string ConfigSnippet - { - get { return _ConfigSnippet; } - } - } - - /// - /// Devices the basic needs for a Device Factory - /// - public abstract class EssentialsDeviceFactory : IDeviceFactory where T:EssentialsDevice - { - #region IDeviceFactory 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 DeviceFactory.FactoryMethods dictionary for each entry in the TypeNames list - /// - public void LoadTypeFactories() - { - 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[]; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Reflection; + +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; + +namespace PepperDash.Essentials.Core +{ + /// + /// Defines the basic needs for an EssentialsDevice to enable it to be build by an IDeviceFactory class + /// + [Description("The base Essentials Device Class")] + public abstract class EssentialsDevice : Device + { + protected EssentialsDevice(string key) + : base(key) + { + + } + + protected EssentialsDevice(string key, string name) + : base(key, name) + { + + } + } + + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public class DescriptionAttribute : Attribute + { + private string _Description; + + public DescriptionAttribute(string description) + { + Debug.Console(2, "Setting Description: {0}", description); + _Description = description; + } + + public string Description + { + get { return _Description; } + } + } + + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public class ConfigSnippetAttribute : Attribute + { + private string _ConfigSnippet; + + public ConfigSnippetAttribute(string configSnippet) + { + Debug.Console(2, "Setting Config Snippet {0}", configSnippet); + _ConfigSnippet = configSnippet; + } + + public string ConfigSnippet + { + get { return _ConfigSnippet; } + } + } + + /// + /// Devices the basic needs for a Device Factory + /// + public abstract class EssentialsDeviceFactory : IDeviceFactory where T:EssentialsDevice + { + #region IDeviceFactory 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 DeviceFactory.FactoryMethods dictionary for each entry in the TypeNames list + /// + public void LoadTypeFactories() + { + 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[]; - DeviceFactory.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 - /// - public abstract class EssentialsPluginDeviceFactory : EssentialsDeviceFactory, IPluginDeviceFactory where T : EssentialsDevice - { - /// - /// Specifies the minimum version of Essentials required for a plugin to run. Must use the format Major.Minor.Build (ex. "1.4.33") - /// - public string MinimumEssentialsFrameworkVersion { get; protected set; } - } + var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[]; + DeviceFactory.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 + /// + public abstract class EssentialsPluginDeviceFactory : EssentialsDeviceFactory, IPluginDeviceFactory where T : EssentialsDevice + { + /// + /// Specifies the minimum version of Essentials required for a plugin to run. Must use the format Major.Minor.Build (ex. "1.4.33") + /// + public string MinimumEssentialsFrameworkVersion { get; protected set; } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Components/ComponentFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Components/ComponentFactory.cs index e48c0a86..fab33c27 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Components/ComponentFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Components/ComponentFactory.cs @@ -3,13 +3,193 @@ using System.Collections.Generic; using System.Linq; using System.Text; using Crestron.SimplSharp; +using Crestron.SimplSharp.Reflection; + +using PepperDash.Core; + using PepperDash.Essentials.Interfaces.Components; namespace PepperDash.Essentials.Core.Room.Components { - public class ComponentFactory + public class ComponentFactoryWrapper + { + public CType CType { get; set; } + public string Description { get; set; } + public Func FactoryMethod { get; set; } + + public ComponentFactoryWrapper() + { + CType = null; + Description = "Not Available"; + } + } + + /// + /// Defines a class that is capable of loading component types + /// + public interface IComponentFactory : IDeviceFactory { } -} \ No newline at end of file + + public static class ComponentFactory + { + public static ComponentFactory() + { + var assy = Assembly.GetExecutingAssembly(); + PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy); + + var types = assy.GetTypes().Where(ct => typeof(IComponentFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract); + + if (types != null) + { + foreach (var type in types) + { + try + { + var factory = (IComponentFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type); + factory.LoadTypeFactories(); + } + catch (Exception e) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to load type: '{1}' ComponentFactory: {0}", e, type.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 FactoryMethods = + new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Adds a plugin factory method + /// + /// + /// + public static void AddFactoryForType(string typeName, Func method) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + ComponentFactory.FactoryMethods.Add(typeName, new ComponentFactoryWrapper() { FactoryMethod = method }); + } + + public static void AddFactoryForType(string typeName, string description, CType cType, Func method) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + + if (FactoryMethods.ContainsKey(typeName)) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to add type: '{0}'. Already exists in ComponentFactory", typeName); + return; + } + + var wrapper = new ComponentFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method }; + ComponentFactory.FactoryMethods.Add(typeName, wrapper); + } + + /// + /// The factory method for Core components. Also iterates the Factory methods that have + /// been loaded from plugins + /// + /// + /// + public static IKeyed GetComponent(RoomComponentConfig dc) + { + var key = dc.Key; + var name = dc.Name; + var type = dc.Type; + var properties = dc.Properties; + + var typeName = dc.Type.ToLower(); + + // Check for types that have been added by plugin dlls. + if (FactoryMethods.ContainsKey(typeName)) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading '{0}' from Essentials Core", dc.Type); + return FactoryMethods[typeName].FactoryMethod(dc); + } + + return null; + } + + /// + /// Prints the type names and associated metadata from the FactoryMethods collection. + /// + /// + public static void GetComponentFactoryTypes(string filter) + { + Dictionary types = new Dictionary(); + + if (!string.IsNullOrEmpty(filter)) + { + types = FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value); + } + else + { + types = FactoryMethods; + } + + Debug.Console(0, "Component Types:"); + + foreach (var type in types.OrderBy(t => t.Key)) + { + var description = type.Value.Description; + var cType = "Not Specified by Plugin"; + + if (type.Value.CType != null) + { + cType = type.Value.CType.FullName; + } + + Debug.Console(0, + @"Type: '{0}' + CType: '{1}' + Description: {2}", type.Key, cType, description); + } + } + + } + + + /// + /// Devices the basic needs for a Device Factory + /// + public abstract class EssentialsComponentFactory : IComponentFactory where T : IComponent + { + #region IComponentFactory 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 ComponentFactory.FactoryMethods dictionary for each entry in the TypeNames list + /// + public void LoadTypeFactories() + { + 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[]; + ComponentFactory.AddFactoryForType(typeName.ToLower(), description, typeof(T), BuildComponent); + } + } + + /// + /// The method that will build the device + /// + /// The device config + /// An instance of the device + public abstract IComponent BuildComponent(RoomComponentConfig dc); + + #endregion + } +} + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Components/ComponentRoom.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Components/ComponentRoom.cs index fd12901c..14daf522 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Components/ComponentRoom.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Components/ComponentRoom.cs @@ -7,6 +7,7 @@ using Crestron.SimplSharp; using PepperDash.Core; using PepperDash.Essentials.Interfaces.Components; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Devices; using Newtonsoft.Json; @@ -51,6 +52,9 @@ namespace PepperDash.Essentials.Core.Room } + /// + /// The config class for a ComponentRoom + /// public class ComponentRoomPropertiesConfig { [JsonProperty("activities")] @@ -60,14 +64,29 @@ namespace PepperDash.Essentials.Core.Room } - public class ComponentRoom : Device, IComponentRoom + + /// + /// A room comprised of component parts built at runtime. + /// + public class ComponentRoom : ReconfigurableDevice, IComponentRoom { + public ComponentRoomPropertiesConfig PropertiesConfig { get; private set; } + public List Components { get; private set; } public List Activities { get; private set; } - public ComponentRoom(string key, string name) - : base(key, name) + public ComponentRoom(DeviceConfig config) + : base(config) { + try + { + PropertiesConfig = JsonConvert.DeserializeObject + (config.Properties.ToString()); + } + catch (Exception e) + { + Debug.Console(1, this, "Error building ComponentRoom: \n{0}", e); + } }