mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-04-14 13:07:18 +00:00
feat: modify factory loading
Updating IDeviceFactory to resolve [FEATURE]-Refactor Plugin loading mechanism #1065. This change should be backwards-compatible with existing plugins that use the EssentialsPluginDeviceFactory<T> class, as the interfaces are implemented by the various base classes. In addition, the correct assembly name is now printed when a type is loaded.
This commit is contained in:
parent
76759d35cc
commit
574e4dfb0f
8 changed files with 1543 additions and 1611 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -10,273 +10,266 @@ using Newtonsoft.Json.Linq;
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
using PepperDash.Essentials.Core.Config;
|
using PepperDash.Essentials.Core.Config;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality for managing and registering device factories, including loading plugin-based factories and
|
||||||
|
/// retrieving devices based on their configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The <see cref="DeviceFactory"/> class is responsible for discovering and registering device factories
|
||||||
|
/// from plugins, as well as providing methods to retrieve devices based on their configuration. It maintains a
|
||||||
|
/// collection of factory methods that are keyed by device type names, allowing for extensibility through plugins. This
|
||||||
|
/// class also handles metadata retrieval and secret management for device configurations.</remarks>
|
||||||
|
public class DeviceFactory
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides functionality for managing and registering device factories, including loading plugin-based factories and
|
/// Initializes a new instance of the <see cref="DeviceFactory"/> class and loads all available device factories
|
||||||
/// retrieving devices based on their configuration.
|
/// from the current assembly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>The <see cref="DeviceFactory"/> class is responsible for discovering and registering device factories
|
/// <remarks>This constructor scans the executing assembly for types that implement the <see
|
||||||
/// from plugins, as well as providing methods to retrieve devices based on their configuration. It maintains a
|
/// cref="IDeviceFactory"/> interface and are not abstract or interfaces. For each valid type, an instance is
|
||||||
/// collection of factory methods that are keyed by device type names, allowing for extensibility through plugins. This
|
/// created and passed to the <c>LoadDeviceFactories</c> method for further processing. If a type cannot be
|
||||||
/// class also handles metadata retrieval and secret management for device configurations.</remarks>
|
/// instantiated, an informational log message is generated, and the process continues with the remaining
|
||||||
public class DeviceFactory
|
/// types.</remarks>
|
||||||
{
|
public DeviceFactory()
|
||||||
/// <summary>
|
{
|
||||||
/// Initializes a new instance of the <see cref="DeviceFactory"/> class and loads all available device factories
|
var programAssemblies = Directory.GetFiles(InitialParametersClass.ProgramDirectory.ToString(), "*.dll");
|
||||||
/// from the current assembly.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This constructor scans the executing assembly for types that implement the <see
|
|
||||||
/// cref="IDeviceFactory"/> interface and are not abstract or interfaces. For each valid type, an instance is
|
|
||||||
/// created and passed to the <c>LoadDeviceFactories</c> method for further processing. If a type cannot be
|
|
||||||
/// instantiated, an informational log message is generated, and the process continues with the remaining
|
|
||||||
/// types.</remarks>
|
|
||||||
public DeviceFactory()
|
|
||||||
{
|
|
||||||
var programAssemblies = Directory.GetFiles(InitialParametersClass.ProgramDirectory.ToString(), "*.dll");
|
|
||||||
|
|
||||||
// Assemblies known to cause load errors that should be skipped
|
foreach(var assembly in programAssemblies)
|
||||||
var assembliesToSkip = new[] { "CrestronOnvif.dll" };
|
|
||||||
|
|
||||||
foreach (var assembly in programAssemblies)
|
|
||||||
{
|
|
||||||
if (assembliesToSkip.Any(a => Path.GetFileName(assembly).Equals(a, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "Skipping assembly: {assemblyName}", Path.GetFileName(assembly));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Assembly.LoadFrom(assembly);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Debug.LogError("Unable to load assembly: {assemblyName} - {message}", assembly, e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
|
|
||||||
|
|
||||||
// Loop through all loaded assemblies that contain at least 1 type that implements IDeviceFactory
|
|
||||||
foreach (var assembly in loadedAssemblies)
|
|
||||||
{
|
|
||||||
Debug.LogDebug("loaded assembly: {assemblyName}", assembly.GetName()?.Name ?? "Unknown");
|
|
||||||
|
|
||||||
PluginLoader.AddLoadedAssembly(assembly.GetName()?.Name ?? "Unknown", assembly);
|
|
||||||
|
|
||||||
var types = assembly.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
|
|
||||||
|
|
||||||
if (types == null || !types.Any())
|
|
||||||
{
|
|
||||||
Debug.LogDebug("No DeviceFactory types found in assembly: {assemblyName}", assembly.GetName().Name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
|
||||||
LoadDeviceFactories(factory);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Debug.LogError("Unable to load type: '{message}' DeviceFactory: {type}", e.Message, type.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Loads device factories from the specified plugin device factory and registers them for use.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This method retrieves metadata from the provided <paramref name="deviceFactory"/>, including
|
|
||||||
/// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type
|
|
||||||
/// names are converted to lowercase for registration.</remarks>
|
|
||||||
/// <param name="deviceFactory">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>
|
|
||||||
private static void LoadDeviceFactories(IDeviceFactory deviceFactory)
|
|
||||||
{
|
|
||||||
foreach (var typeName in deviceFactory.TypeNames)
|
|
||||||
{
|
|
||||||
string description = deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0
|
|
||||||
? descriptionAttribute[0].Description
|
|
||||||
: "No description available";
|
|
||||||
|
|
||||||
AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A dictionary of factory methods, keyed by config types, added by plugins.
|
|
||||||
/// These methods are looked up and called by GetDevice in this class.
|
|
||||||
/// </summary>
|
|
||||||
private static readonly Dictionary<string, DeviceFactoryWrapper> FactoryMethods =
|
|
||||||
new Dictionary<string, DeviceFactoryWrapper>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a factory method for creating instances of a specific type.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This method associates a type name with a factory method, allowing instances of the type to
|
|
||||||
/// be created dynamically. The factory method is stored internally and can be retrieved or invoked as
|
|
||||||
/// needed.</remarks>
|
|
||||||
/// <param name="typeName">The name of the type for which the factory method is being registered. This value cannot be null or empty.</param>
|
|
||||||
/// <param name="method">A delegate that defines the factory method. The delegate takes a <see cref="DeviceConfig"/> parameter and
|
|
||||||
/// returns an instance of <see cref="IKeyed"/>.</param>
|
|
||||||
public static void AddFactoryForType(string typeName, Func<DeviceConfig, IKeyed> method)
|
|
||||||
{
|
|
||||||
FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a factory method for creating instances of a specific device type.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>If a factory method for the specified <paramref name="typeName"/> already exists, the method
|
|
||||||
/// will not overwrite it and will log an informational message instead.</remarks>
|
|
||||||
/// <param name="typeName">The unique name of the device type. This serves as the key for identifying the factory method.</param>
|
|
||||||
/// <param name="description">A brief description of the device type. This is used for informational purposes.</param>
|
|
||||||
/// <param name="Type">The <see cref="Type"/> of the device being registered. This represents the runtime type of the device.</param>
|
|
||||||
/// <param name="method">A factory method that takes a <see cref="DeviceConfig"/> as input and returns an instance of <see
|
|
||||||
/// cref="IKeyed"/>.</param>
|
|
||||||
public static void AddFactoryForType(string typeName, string description, Type Type, Func<DeviceConfig, IKeyed> method)
|
|
||||||
{
|
|
||||||
if (FactoryMethods.ContainsKey(typeName))
|
|
||||||
{
|
|
||||||
Debug.LogInformation("Unable to add type: '{typeName}'. Already exists in DeviceFactory", typeName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
|
|
||||||
|
|
||||||
FactoryMethods.Add(typeName, wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CheckForSecrets(IEnumerable<JProperty> obj)
|
|
||||||
{
|
|
||||||
foreach (var prop in obj.Where(prop => prop.Value as JObject != null))
|
|
||||||
{
|
|
||||||
if (prop.Name.Equals("secret", StringComparison.CurrentCultureIgnoreCase))
|
|
||||||
{
|
|
||||||
var secret = GetSecret(prop.Children().First().ToObject<SecretsPropertiesConfig>());
|
|
||||||
|
|
||||||
prop.Parent.Replace(secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(prop.Value is JObject recurseProp)) continue;
|
|
||||||
|
|
||||||
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.LogMessage(LogEventLevel.Debug,
|
|
||||||
"Unable to retrieve secret {0}{1} - Make sure you've added it to the secrets provider",
|
|
||||||
data.Provider, data.Key);
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates and returns a device instance based on the provided <see cref="DeviceConfig"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This method attempts to create a device using the type specified in the <paramref name="dc"/>
|
|
||||||
/// parameter. If the type corresponds to a registered factory method, the device is created and returned. If the
|
|
||||||
/// type is unrecognized or an exception occurs, the method logs the error and returns <see
|
|
||||||
/// langword="null"/>.</remarks>
|
|
||||||
/// <param name="dc">The configuration object containing the key, name, type, and properties required to create the device.</param>
|
|
||||||
/// <returns>An instance of a device that implements <see cref="IKeyed"/>, or <see langword="null"/> if the device type is
|
|
||||||
/// not recognized or an error occurs during creation.</returns>
|
|
||||||
public static IKeyed GetDevice(DeviceConfig dc)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var localDc = new DeviceConfig(dc);
|
Assembly.LoadFrom(assembly);
|
||||||
|
|
||||||
var key = localDc.Key;
|
|
||||||
var name = localDc.Name;
|
|
||||||
var type = localDc.Type;
|
|
||||||
var properties = localDc.Properties;
|
|
||||||
|
|
||||||
var typeName = localDc.Type.ToLower();
|
|
||||||
|
|
||||||
if (properties is JObject jObject)
|
|
||||||
{
|
|
||||||
var jProp = jObject.Properties();
|
|
||||||
|
|
||||||
CheckForSecrets(jProp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FactoryMethods.TryGetValue(typeName, out var wrapper))
|
|
||||||
{
|
|
||||||
Debug.LogWarning("Device type '{typeName}' not found in DeviceFactory", typeName);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.LogInformation("Loading '{type}' from {assemblyName}", typeName, wrapper.Type.Assembly.FullName);
|
|
||||||
|
|
||||||
// Check for types that have been added by plugin dlls.
|
|
||||||
return wrapper.FactoryMethod(localDc);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Debug.LogError(ex, "Exception occurred while creating device {key}: {message}", dc.Key, ex.Message);
|
Debug.LogError("Unable to load assembly: {assemblyName} - {message}", assembly, e.Message);
|
||||||
Debug.LogDebug(ex, "Exception details: {stackTrace}", ex.StackTrace);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||||
/// Displays a list of device factory types that match the specified filter.
|
|
||||||
/// </summary>
|
// Loop through all loaded assemblies that contain at least 1 type that implements IDeviceFactory
|
||||||
/// <remarks>The method outputs the filtered list of device factory types to the console, including their
|
foreach (var assembly in loadedAssemblies)
|
||||||
/// key, type, and description. If a type is not specified by the plugin, it will be displayed as "Not Specified by
|
|
||||||
/// Plugin."</remarks>
|
|
||||||
/// <param name="filter">A string used to filter the device factory types by their keys. If the filter is null or empty, all device
|
|
||||||
/// factory types are displayed.</param>
|
|
||||||
public static void GetDeviceFactoryTypes(string filter)
|
|
||||||
{
|
{
|
||||||
var types = !string.IsNullOrEmpty(filter)
|
Debug.LogDebug("loaded assembly: {assemblyName}", assembly.GetName().Name);
|
||||||
? FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value)
|
|
||||||
: FactoryMethods;
|
|
||||||
|
|
||||||
CrestronConsole.ConsoleCommandResponse("Device Types:");
|
PluginLoader.AddLoadedAssembly(assembly.GetName().Name, assembly);
|
||||||
|
|
||||||
foreach (var type in types.OrderBy(t => t.Key))
|
var types = assembly.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
|
||||||
|
|
||||||
|
if(types == null || !types.Any())
|
||||||
{
|
{
|
||||||
var description = type.Value.Description;
|
Debug.LogDebug("No DeviceFactory types found in assembly: {assemblyName}", assembly.GetName().Name);
|
||||||
var Type = "Not Specified by Plugin";
|
continue;
|
||||||
|
|
||||||
if (type.Value.Type != null)
|
|
||||||
{
|
|
||||||
Type = type.Value.Type.FullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
CrestronConsole.ConsoleCommandResponse(
|
|
||||||
"Type: '{0}'\r\n" +
|
|
||||||
" Type: '{1}'\r\n" +
|
|
||||||
" Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
foreach (var type in types)
|
||||||
/// Retrieves a dictionary of device factory wrappers, optionally filtered by a specified string.
|
{
|
||||||
/// </summary>
|
try
|
||||||
/// <param name="filter">A string used to filter the dictionary keys. Only entries with keys containing the specified filter will be
|
{
|
||||||
/// included. If <see langword="null"/> or empty, all entries are returned.</param>
|
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
||||||
/// <returns>A dictionary where the keys are strings representing device identifiers and the values are <see
|
LoadDeviceFactories(factory);
|
||||||
/// cref="DeviceFactoryWrapper"/> instances. The dictionary may be empty if no entries match the filter.</returns>
|
}
|
||||||
public static Dictionary<string, DeviceFactoryWrapper> GetDeviceFactoryDictionary(string filter)
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogError("Unable to load type: '{message}' DeviceFactory: {type}", e.Message, type.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads device factories from the specified plugin device factory and registers them for use.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This method retrieves metadata from the provided <paramref name="deviceFactory"/>, including
|
||||||
|
/// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type
|
||||||
|
/// names are converted to lowercase for registration.</remarks>
|
||||||
|
/// <param name="deviceFactory">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>
|
||||||
|
private static void LoadDeviceFactories(IDeviceFactory deviceFactory)
|
||||||
|
{
|
||||||
|
foreach (var typeName in deviceFactory.TypeNames)
|
||||||
{
|
{
|
||||||
return string.IsNullOrEmpty(filter)
|
//Debug.LogMessage(LogEventLevel.Verbose, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
|
||||||
? FactoryMethods
|
var descriptionAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[];
|
||||||
: FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value);
|
string description = descriptionAttribute[0].Description;
|
||||||
|
var snippetAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[];
|
||||||
|
AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary of factory methods, keyed by config types, added by plugins.
|
||||||
|
/// These methods are looked up and called by GetDevice in this class.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Dictionary<string, DeviceFactoryWrapper> FactoryMethods =
|
||||||
|
new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a factory method for creating instances of a specific type.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This method associates a type name with a factory method, allowing instances of the type to
|
||||||
|
/// be created dynamically. The factory method is stored internally and can be retrieved or invoked as
|
||||||
|
/// needed.</remarks>
|
||||||
|
/// <param name="typeName">The name of the type for which the factory method is being registered. This value cannot be null or empty.</param>
|
||||||
|
/// <param name="method">A delegate that defines the factory method. The delegate takes a <see cref="DeviceConfig"/> parameter and
|
||||||
|
/// returns an instance of <see cref="IKeyed"/>.</param>
|
||||||
|
public static void AddFactoryForType(string typeName, Func<DeviceConfig, IKeyed> method)
|
||||||
|
{
|
||||||
|
FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a factory method for creating instances of a specific device type.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>If a factory method for the specified <paramref name="typeName"/> already exists, the method
|
||||||
|
/// will not overwrite it and will log an informational message instead.</remarks>
|
||||||
|
/// <param name="typeName">The unique name of the device type. This serves as the key for identifying the factory method.</param>
|
||||||
|
/// <param name="description">A brief description of the device type. This is used for informational purposes.</param>
|
||||||
|
/// <param name="Type">The <see cref="Type"/> of the device being registered. This represents the runtime type of the device.</param>
|
||||||
|
/// <param name="method">A factory method that takes a <see cref="DeviceConfig"/> as input and returns an instance of <see
|
||||||
|
/// cref="IKeyed"/>.</param>
|
||||||
|
public static void AddFactoryForType(string typeName, string description, Type Type, Func<DeviceConfig, IKeyed> method)
|
||||||
|
{
|
||||||
|
if(FactoryMethods.ContainsKey(typeName))
|
||||||
|
{
|
||||||
|
Debug.LogInformation("Unable to add type: '{typeName}'. Already exists in DeviceFactory", typeName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
|
||||||
|
|
||||||
|
FactoryMethods.Add(typeName, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CheckForSecrets(IEnumerable<JProperty> obj)
|
||||||
|
{
|
||||||
|
foreach (var prop in obj.Where(prop => prop.Value as JObject != null))
|
||||||
|
{
|
||||||
|
if (prop.Name.Equals("secret", StringComparison.CurrentCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
var secret = GetSecret(prop.Children().First().ToObject<SecretsPropertiesConfig>());
|
||||||
|
|
||||||
|
prop.Parent.Replace(secret);
|
||||||
|
}
|
||||||
|
if (prop.Value is not JObject recurseProp) 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.LogMessage(LogEventLevel.Debug,
|
||||||
|
"Unable to retrieve secret {0}{1} - Make sure you've added it to the secrets provider",
|
||||||
|
data.Provider, data.Key);
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and returns a device instance based on the provided <see cref="DeviceConfig"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This method attempts to create a device using the type specified in the <paramref name="dc"/>
|
||||||
|
/// parameter. If the type corresponds to a registered factory method, the device is created and returned. If the
|
||||||
|
/// type is unrecognized or an exception occurs, the method logs the error and returns <see
|
||||||
|
/// langword="null"/>.</remarks>
|
||||||
|
/// <param name="dc">The configuration object containing the key, name, type, and properties required to create the device.</param>
|
||||||
|
/// <returns>An instance of a device that implements <see cref="IKeyed"/>, or <see langword="null"/> if the device type is
|
||||||
|
/// not recognized or an error occurs during creation.</returns>
|
||||||
|
public static IKeyed GetDevice(DeviceConfig dc)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var localDc = new DeviceConfig(dc);
|
||||||
|
|
||||||
|
var key = localDc.Key;
|
||||||
|
var name = localDc.Name;
|
||||||
|
var type = localDc.Type;
|
||||||
|
var properties = localDc.Properties;
|
||||||
|
|
||||||
|
var typeName = localDc.Type.ToLower();
|
||||||
|
|
||||||
|
if (properties is JObject jObject)
|
||||||
|
{
|
||||||
|
var jProp = jObject.Properties();
|
||||||
|
|
||||||
|
CheckForSecrets(jProp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FactoryMethods.TryGetValue(typeName, out var wrapper))
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Device type '{typeName}' not found in DeviceFactory", typeName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.LogInformation("Loading '{type}' from {assemblyName}", typeName, wrapper.Type.Assembly.FullName);
|
||||||
|
|
||||||
|
// Check for types that have been added by plugin dlls.
|
||||||
|
return wrapper.FactoryMethod(localDc);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError(ex, "Exception occurred while creating device {0}: {1}", null, dc.Key, ex.Message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Displays a list of device factory types that match the specified filter.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The method outputs the filtered list of device factory types to the console, including their
|
||||||
|
/// key, type, and description. If a type is not specified by the plugin, it will be displayed as "Not Specified by
|
||||||
|
/// Plugin."</remarks>
|
||||||
|
/// <param name="filter">A string used to filter the device factory types by their keys. If the filter is null or empty, all device
|
||||||
|
/// factory types are displayed.</param>
|
||||||
|
public static void GetDeviceFactoryTypes(string filter)
|
||||||
|
{
|
||||||
|
var types = !string.IsNullOrEmpty(filter)
|
||||||
|
? FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value)
|
||||||
|
: FactoryMethods;
|
||||||
|
|
||||||
|
CrestronConsole.ConsoleCommandResponse("Device Types:");
|
||||||
|
|
||||||
|
foreach (var type in types.OrderBy(t => t.Key))
|
||||||
|
{
|
||||||
|
var description = type.Value.Description;
|
||||||
|
var Type = "Not Specified by Plugin";
|
||||||
|
|
||||||
|
if (type.Value.Type != null)
|
||||||
|
{
|
||||||
|
Type = type.Value.Type.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
CrestronConsole.ConsoleCommandResponse(
|
||||||
|
@"Type: '{0}'
|
||||||
|
Type: '{1}'
|
||||||
|
Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a dictionary of device factory wrappers, optionally filtered by a specified string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filter">A string used to filter the dictionary keys. Only entries with keys containing the specified filter will be
|
||||||
|
/// included. If <see langword="null"/> or empty, all entries are returned.</param>
|
||||||
|
/// <returns>A dictionary where the keys are strings representing device identifiers and the values are <see
|
||||||
|
/// cref="DeviceFactoryWrapper"/> instances. The dictionary may be empty if no entries match the filter.</returns>
|
||||||
|
public static Dictionary<string, DeviceFactoryWrapper> GetDeviceFactoryDictionary(string filter)
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(filter)
|
||||||
|
? FactoryMethods
|
||||||
|
: FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,160 +10,143 @@ using System.Linq;
|
||||||
namespace PepperDash.Essentials.Core;
|
namespace PepperDash.Essentials.Core;
|
||||||
|
|
||||||
|
|
||||||
|
public class ProcessorExtensionDeviceFactory
|
||||||
|
{
|
||||||
|
public ProcessorExtensionDeviceFactory() {
|
||||||
|
var assy = Assembly.GetExecutingAssembly();
|
||||||
|
PluginLoader.AddLoadedAssembly(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)Activator.CreateInstance(extension);
|
||||||
|
factory.LoadFactories();
|
||||||
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Unable to load extension device: '{1}' ProcessorExtensionDeviceFactory: {0}", e, extension.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a ProcessorExtensionDeviceFactory
|
/// A dictionary of factory methods, keyed by config types, added by plugins.
|
||||||
|
/// These methods are looked up and called by GetDevice in this class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ProcessorExtensionDeviceFactory
|
static Dictionary<string, DeviceFactoryWrapper> ProcessorExtensionFactoryMethods =
|
||||||
|
new Dictionary<string, DeviceFactoryWrapper>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a plugin factory method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dc"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static void AddFactoryForType(string extensionName, Func<DeviceConfig, IKeyed> method)
|
||||||
{
|
{
|
||||||
/// <summary>
|
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
|
||||||
/// Constructor
|
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, new DeviceFactoryWrapper() { FactoryMethod = method });
|
||||||
/// </summary>
|
}
|
||||||
public ProcessorExtensionDeviceFactory() {
|
|
||||||
var assy = Assembly.GetExecutingAssembly();
|
|
||||||
PluginLoader.AddLoadedAssembly(assy.GetName().Name, assy);
|
|
||||||
|
|
||||||
var extensions = assy.GetTypes().Where(ct => typeof(IProcessorExtensionDeviceFactory)
|
public static void AddFactoryForType(string extensionName, string description, Type Type, Func<DeviceConfig, IKeyed> method)
|
||||||
.IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
|
{
|
||||||
|
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
|
||||||
|
|
||||||
if (extensions != null )
|
if (ProcessorExtensionFactoryMethods.ContainsKey(extensionName))
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Unable to add extension device: '{0}'. Already exists in ProcessorExtensionDeviceFactory", extensionName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
|
||||||
|
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CheckForSecrets(IEnumerable<Newtonsoft.Json.Linq.JProperty> obj)
|
||||||
|
{
|
||||||
|
foreach (var prop in obj.Where(prop => prop.Value as Newtonsoft.Json.Linq.JObject != null))
|
||||||
|
{
|
||||||
|
if (prop.Name.ToLower() == "secret")
|
||||||
{
|
{
|
||||||
foreach ( var extension in extensions )
|
var secret = GetSecret(prop.Children().First().ToObject<SecretsPropertiesConfig>());
|
||||||
{
|
//var secret = GetSecret(JsonConvert.DeserializeObject<SecretsPropertiesConfig>(prop.Children().First().ToString()));
|
||||||
try
|
prop.Parent.Replace(secret);
|
||||||
{
|
|
||||||
var factory = (IProcessorExtensionDeviceFactory)Activator.CreateInstance(extension);
|
|
||||||
factory.LoadFactories();
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Unable to load extension device: '{1}' ProcessorExtensionDeviceFactory: {0}", e, extension.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var recurseProp = prop.Value as Newtonsoft.Json.Linq.JObject;
|
||||||
|
if (recurseProp == null) return;
|
||||||
|
CheckForSecrets(recurseProp.Properties());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
private static string GetSecret(SecretsPropertiesConfig data)
|
||||||
/// A dictionary of factory methods, keyed by config types, added by plugins.
|
{
|
||||||
/// These methods are looked up and called by GetDevice in this class.
|
var secretProvider = SecretsManager.GetSecretProviderByKey(data.Provider);
|
||||||
/// </summary>
|
if (secretProvider == null) return null;
|
||||||
static Dictionary<string, DeviceFactoryWrapper> ProcessorExtensionFactoryMethods =
|
var secret = secretProvider.GetSecret(data.Key);
|
||||||
new Dictionary<string, DeviceFactoryWrapper>(StringComparer.OrdinalIgnoreCase);
|
if (secret != null) return (string)secret.Value;
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug,
|
||||||
|
"Unable to retrieve secret {0}{1} - Make sure you've added it to the secrets provider",
|
||||||
|
data.Provider, data.Key);
|
||||||
|
return String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// The factory method for processor extension devices. Also iterates the Factory methods that have
|
||||||
/// Adds a plugin factory method
|
/// been loaded from plugins
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="extensionName">name fo extension to add</param>
|
/// <param name="dc"></param>
|
||||||
/// <param name="method">method to add</param>
|
/// <returns></returns>
|
||||||
/// <returns></returns>
|
public static IKeyed GetExtensionDevice(DeviceConfig dc)
|
||||||
public static void AddFactoryForType(string extensionName, Func<DeviceConfig, IKeyed> method)
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
|
Debug.LogMessage(LogEventLevel.Information, "Loading '{0}' from Essentials Core", dc.Type);
|
||||||
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, new DeviceFactoryWrapper() { FactoryMethod = method });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
var localDc = new DeviceConfig(dc);
|
||||||
/// Adds a plugin factory method with type and description
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="extensionName">name of extension to add</param>
|
|
||||||
/// <param name="description">description of extension to add</param>
|
|
||||||
/// <param name="Type">type of extension to add</param>
|
|
||||||
/// <param name="method">method to add</param>
|
|
||||||
public static void AddFactoryForType(string extensionName, string description, Type Type, Func<DeviceConfig, IKeyed> method)
|
|
||||||
{
|
|
||||||
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
|
|
||||||
|
|
||||||
if (ProcessorExtensionFactoryMethods.ContainsKey(extensionName))
|
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 Newtonsoft.Json.Linq.JObject;
|
||||||
|
if (jObject != null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Unable to add extension device: '{0}'. Already exists in ProcessorExtensionDeviceFactory", extensionName);
|
var jProp = jObject.Properties();
|
||||||
return;
|
|
||||||
|
CheckForSecrets(jProp);
|
||||||
}
|
}
|
||||||
|
|
||||||
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
|
Debug.LogMessage(LogEventLevel.Verbose, "typeName = {0}", typeName);
|
||||||
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper);
|
// Check for types that have been added by plugin dlls.
|
||||||
|
return !ProcessorExtensionFactoryMethods.ContainsKey(typeName) ? null : ProcessorExtensionFactoryMethods[typeName].FactoryMethod(localDc);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
private static void CheckForSecrets(IEnumerable<Newtonsoft.Json.Linq.JProperty> obj)
|
|
||||||
{
|
{
|
||||||
foreach (var prop in obj.Where(prop => prop.Value as Newtonsoft.Json.Linq.JObject != null))
|
Debug.LogMessage(LogEventLevel.Information, "Exception occurred while creating device {0}: {1}", dc.Key, ex.Message);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.StackTrace);
|
||||||
|
|
||||||
|
if (ex.InnerException == null)
|
||||||
{
|
{
|
||||||
if (prop.Name.ToLower() == "secret")
|
|
||||||
{
|
|
||||||
var secret = GetSecret(prop.Children().First().ToObject<SecretsPropertiesConfig>());
|
|
||||||
//var secret = GetSecret(JsonConvert.DeserializeObject<SecretsPropertiesConfig>(prop.Children().First().ToString()));
|
|
||||||
prop.Parent.Replace(secret);
|
|
||||||
}
|
|
||||||
var recurseProp = prop.Value as 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.LogMessage(LogEventLevel.Debug,
|
|
||||||
"Unable to retrieve secret {0}{1} - Make sure you've added it to the secrets provider",
|
|
||||||
data.Provider, data.Key);
|
|
||||||
return String.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The factory method for processor extension devices. Also iterates the Factory methods that have
|
|
||||||
/// been loaded from plugins
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dc"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <summary>
|
|
||||||
/// GetExtensionDevice method
|
|
||||||
/// </summary>
|
|
||||||
public static IKeyed GetExtensionDevice(DeviceConfig dc)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "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 Newtonsoft.Json.Linq.JObject;
|
|
||||||
if (jObject != null)
|
|
||||||
{
|
|
||||||
var jProp = jObject.Properties();
|
|
||||||
|
|
||||||
CheckForSecrets(jProp);
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "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.LogMessage(LogEventLevel.Information, "Exception occurred while creating device {0}: {1}", dc.Key, ex.Message);
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.StackTrace);
|
|
||||||
|
|
||||||
if (ex.InnerException == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Inner exception while creating device {0}: {1}", dc.Key,
|
|
||||||
ex.InnerException.Message);
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.InnerException.StackTrace);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Inner exception while creating device {0}: {1}", dc.Key,
|
||||||
|
ex.InnerException.Message);
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.InnerException.StackTrace);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,58 +7,51 @@ using PepperDash.Core;
|
||||||
using PepperDash.Essentials.Core;
|
using PepperDash.Essentials.Core;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Devices.Common
|
namespace PepperDash.Essentials.Devices.Common;
|
||||||
|
|
||||||
|
public class DeviceFactory
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Represents a DeviceFactory
|
public DeviceFactory()
|
||||||
/// </summary>
|
|
||||||
public class DeviceFactory
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
var assy = Assembly.GetExecutingAssembly();
|
||||||
/// Initializes a new instance of the DeviceFactory class
|
PluginLoader.AddLoadedAssembly(assy.GetName().Name, assy);
|
||||||
/// </summary>
|
|
||||||
public DeviceFactory()
|
var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
|
||||||
|
|
||||||
|
if (types != null)
|
||||||
{
|
{
|
||||||
var assy = Assembly.GetExecutingAssembly();
|
foreach (var type in types)
|
||||||
PluginLoader.AddLoadedAssembly(assy.GetName().Name, assy);
|
|
||||||
|
|
||||||
var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
|
|
||||||
|
|
||||||
if (types != null)
|
|
||||||
{
|
{
|
||||||
foreach (var type in types)
|
try
|
||||||
{
|
{
|
||||||
try
|
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
||||||
{
|
LoadDeviceFactories(factory);
|
||||||
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
}
|
||||||
LoadDeviceFactories(factory);
|
catch (Exception e)
|
||||||
}
|
{
|
||||||
catch (Exception e)
|
Debug.LogMessage(LogEventLevel.Information, "Unable to load type: '{1}' DeviceFactory: {0}", e, type.Name);
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Unable to load type: '{1}' DeviceFactory: {0}", e, type.Name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads device factories from the specified plugin device factory and registers them for use.
|
/// Loads device factories from the specified plugin device factory and registers them for use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This method retrieves metadata from the provided <paramref name="deviceFactory"/>, including
|
/// <remarks>This method retrieves metadata from the provided <paramref name="deviceFactory"/>, including
|
||||||
/// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type
|
/// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type
|
||||||
/// names are converted to lowercase for registration.</remarks>
|
/// names are converted to lowercase for registration.</remarks>
|
||||||
/// <param name="deviceFactory">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>
|
/// <param name="deviceFactory">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>
|
||||||
private static void LoadDeviceFactories(IDeviceFactory deviceFactory)
|
private static void LoadDeviceFactories(IDeviceFactory deviceFactory)
|
||||||
|
{
|
||||||
|
foreach (var typeName in deviceFactory.TypeNames)
|
||||||
{
|
{
|
||||||
foreach (var typeName in deviceFactory.TypeNames)
|
//Debug.LogMessage(LogEventLevel.Verbose, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
|
||||||
{
|
var descriptionAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[];
|
||||||
//Debug.LogMessage(LogEventLevel.Verbose, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
|
string description = descriptionAttribute[0].Description;
|
||||||
string description = (deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0)
|
var snippetAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[];
|
||||||
? descriptionAttribute[0].Description
|
Core.DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
||||||
: "No description available";
|
|
||||||
|
|
||||||
Core.DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,61 +4,54 @@ using System.Reflection;
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
using PepperDash.Essentials.Core;
|
using PepperDash.Essentials.Core;
|
||||||
|
|
||||||
namespace PepperDash.Essentials
|
namespace PepperDash.Essentials;
|
||||||
|
|
||||||
|
public class MobileControlFactory
|
||||||
{
|
{
|
||||||
/// <summary>
|
public MobileControlFactory()
|
||||||
/// Factory class for the Mobile Control App Controller
|
|
||||||
/// </summary>
|
|
||||||
public class MobileControlFactory
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
/// Create an instance of the <see cref="MobileControlFactory"/> class.
|
|
||||||
/// </summary>
|
PluginLoader.AddLoadedAssembly(assembly.GetName().Name, assembly);
|
||||||
public MobileControlFactory()
|
|
||||||
|
var types = assembly.GetTypes().Where(t => typeof(IDeviceFactory).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);
|
||||||
|
|
||||||
|
if (types == null)
|
||||||
{
|
{
|
||||||
var assembly = Assembly.GetExecutingAssembly();
|
return;
|
||||||
|
|
||||||
PluginLoader.AddLoadedAssembly(assembly.GetName().Name, assembly);
|
|
||||||
|
|
||||||
var types = assembly.GetTypes().Where(t => typeof(IDeviceFactory).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);
|
|
||||||
|
|
||||||
if (types == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
|
||||||
|
|
||||||
LoadDeviceFactories(factory);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogMessage(ex, "Unable to load type '{type}' DeviceFactory: {factory}", null, type.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
foreach (var type in types)
|
||||||
/// Loads device factories from the specified plugin device factory and registers them for use.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This method retrieves metadata from the provided <paramref name="deviceFactory"/>, including
|
|
||||||
/// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type
|
|
||||||
/// names are converted to lowercase for registration.</remarks>
|
|
||||||
/// <param name="deviceFactory">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>
|
|
||||||
private static void LoadDeviceFactories(IDeviceFactory deviceFactory)
|
|
||||||
{
|
{
|
||||||
foreach (var typeName in deviceFactory.TypeNames)
|
try
|
||||||
{
|
{
|
||||||
string description = (deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0)
|
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
||||||
? descriptionAttribute[0].Description
|
|
||||||
: "No description available"; // Default value if no DescriptionAttribute is found
|
|
||||||
|
|
||||||
DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
LoadDeviceFactories(factory);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(ex, "Unable to load type '{type}' DeviceFactory: {factory}", null, type.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads device factories from the specified plugin device factory and registers them for use.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This method retrieves metadata from the provided <paramref name="deviceFactory"/>, including
|
||||||
|
/// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type
|
||||||
|
/// names are converted to lowercase for registration.</remarks>
|
||||||
|
/// <param name="deviceFactory">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>
|
||||||
|
private static void LoadDeviceFactories(IDeviceFactory deviceFactory)
|
||||||
|
{
|
||||||
|
foreach (var typeName in deviceFactory.TypeNames)
|
||||||
|
{
|
||||||
|
//Debug.LogMessage(LogEventLevel.Verbose, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
|
||||||
|
var descriptionAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[];
|
||||||
|
string description = descriptionAttribute[0].Description;
|
||||||
|
var snippetAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[];
|
||||||
|
Core.DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,58 +7,54 @@ using System.Reflection;
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
using PepperDash.Essentials.Core;
|
using PepperDash.Essentials.Core;
|
||||||
|
|
||||||
namespace PepperDash.Essentials
|
namespace PepperDash.Essentials;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Responsible for loading all of the device types for this library
|
||||||
|
/// </summary>
|
||||||
|
public class DeviceFactory
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Responsible for loading all of the device types for this library
|
public DeviceFactory()
|
||||||
/// </summary>
|
|
||||||
public class DeviceFactory
|
|
||||||
{
|
{
|
||||||
|
var assy = Assembly.GetExecutingAssembly();
|
||||||
|
PluginLoader.AddLoadedAssembly(assy.GetName().Name, assy);
|
||||||
|
|
||||||
/// <summary>
|
var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
|
||||||
/// Initializes a new instance of the DeviceFactory class and loads all device type factories
|
|
||||||
/// </summary>
|
if (types != null)
|
||||||
public DeviceFactory()
|
|
||||||
{
|
{
|
||||||
var assy = Assembly.GetExecutingAssembly();
|
foreach (var type in types)
|
||||||
PluginLoader.AddLoadedAssembly(assy.GetName().Name, assy);
|
|
||||||
|
|
||||||
var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
|
|
||||||
|
|
||||||
if (types != null)
|
|
||||||
{
|
{
|
||||||
foreach (var type in types)
|
try
|
||||||
{
|
{
|
||||||
try
|
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
||||||
{
|
LoadDeviceFactories(factory);
|
||||||
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
}
|
||||||
LoadDeviceFactories(factory);
|
catch (Exception e)
|
||||||
}
|
{
|
||||||
catch (Exception e)
|
Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "Unable to load type: '{exception}' DeviceFactory: {factoryName}", e, type.Name);
|
||||||
{
|
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Error, "Unable to load type: '{exception}' DeviceFactory: {factoryName}", e, type.Name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads device factories from the specified plugin device factory and registers them for use.
|
/// Loads device factories from the specified plugin device factory and registers them for use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This method retrieves metadata from the provided <paramref name="deviceFactory"/>, including
|
/// <remarks>This method retrieves metadata from the provided <paramref name="deviceFactory"/>, including
|
||||||
/// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type
|
/// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type
|
||||||
/// names are converted to lowercase for registration.</remarks>
|
/// names are converted to lowercase for registration.</remarks>
|
||||||
/// <param name="deviceFactory">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>
|
/// <param name="deviceFactory">The plugin device factory that provides the device types, descriptions, and factory methods to be registered.</param>
|
||||||
private static void LoadDeviceFactories(IDeviceFactory deviceFactory)
|
private static void LoadDeviceFactories(IDeviceFactory deviceFactory)
|
||||||
|
{
|
||||||
|
foreach (var typeName in deviceFactory.TypeNames)
|
||||||
{
|
{
|
||||||
foreach (var typeName in deviceFactory.TypeNames)
|
//Debug.LogMessage(LogEventLevel.Verbose, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
|
||||||
{
|
var descriptionAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[];
|
||||||
string description = (deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0)
|
string description = descriptionAttribute[0].Description;
|
||||||
? descriptionAttribute[0].Description
|
var snippetAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[];
|
||||||
: "No description available";
|
Core.DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
||||||
|
|
||||||
Core.DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,29 +27,6 @@
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<DocumentationFile>bin\$(Configuration)\PepperDashEssentials.xml</DocumentationFile>
|
<DocumentationFile>bin\$(Configuration)\PepperDashEssentials.xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Include="Example Configuration\EssentialsHuddleSpaceRoom\configurationFile-HuddleSpace-2-Source.json">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Include="Example Configuration\EssentialsHuddleVtc1Room\configurationFile-mockVideoCodec_din-ap3_-_dm4x1.json">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Include="Example Configuration\SIMPLBridging\configurationFile-dmps3300c-avRouting.json">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Include="Example Configuration\SIMPLBridging\SIMPLBridgeExample_configurationFile.json">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Include="SGD\PepperDash Essentials iPad.sgd">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Include="SGD\PepperDash Essentials TSW-560.sgd">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Include="SGD\PepperDash Essentials TSW-760.sgd">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.21.128" />
|
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.21.128" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue