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
|
|
@ -8,50 +8,40 @@ using PepperDash.Core;
|
|||
using Serilog.Events;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
namespace PepperDash.Essentials.Core;
|
||||
|
||||
public static class DeviceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the devices in the system
|
||||
/// </summary>
|
||||
public static class DeviceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised when all devices have been activated
|
||||
/// </summary>
|
||||
public static event EventHandler<EventArgs> AllDevicesActivated;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when all devices have been registered
|
||||
/// </summary>
|
||||
public static event EventHandler<EventArgs> AllDevicesRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when all devices have been initialized
|
||||
/// </summary>
|
||||
public static event EventHandler<EventArgs> AllDevicesInitialized;
|
||||
|
||||
private static readonly CCriticalSection DeviceCriticalSection = new CCriticalSection();
|
||||
|
||||
private static readonly CEvent AllowAddDevicesCEvent = new CEvent(false, true);
|
||||
//public static List<Device> Devices { get { return _Devices; } }
|
||||
//static List<Device> _Devices = new List<Device>();
|
||||
|
||||
private static readonly Dictionary<string, IKeyed> Devices = new Dictionary<string, IKeyed>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AllDevices
|
||||
/// Returns a copy of all the devices in a list
|
||||
/// </summary>
|
||||
public static List<IKeyed> AllDevices { get { return new List<IKeyed>(Devices.Values); } }
|
||||
public static List<IKeyed> AllDevices => [.. Devices.Values];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AddDeviceEnabled
|
||||
/// </summary>
|
||||
public static bool AddDeviceEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize method
|
||||
/// Initializes the control system by enabling device management and registering console commands.
|
||||
/// </summary>
|
||||
/// <remarks>This method sets up the control system for device management by enabling the addition of
|
||||
/// devices and registering a series of console commands for interacting with devices. These commands allow
|
||||
/// operators to list device statuses, feedbacks, and managed devices, as well as perform actions such as
|
||||
/// simulating communication, debugging device streams, and accessing device properties and methods.</remarks>
|
||||
/// <param name="cs">The <see cref="CrestronControlSystem"/> instance representing the control system to initialize.</param>
|
||||
public static void Initialize(CrestronControlSystem cs)
|
||||
{
|
||||
AddDeviceEnabled = true;
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(ListDeviceCommStatuses, "devcommstatus", "Lists the communication status of all devices",
|
||||
ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(ListDeviceFeedbacks, "devfb", "Lists current feedbacks",
|
||||
|
|
@ -60,9 +50,9 @@ namespace PepperDash.Essentials.Core
|
|||
ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, "devjson", "",
|
||||
ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s).Replace(Environment.NewLine, "\r\n")), "devprops", "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s).Replace(Environment.NewLine, "\r\n")), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s).Replace(Environment.NewLine, "\r\n")), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s)), "devprops", "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s)), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s)), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(SimulateComReceiveOnDevice, "devsimreceive",
|
||||
"Simulates incoming data on a com device", ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
|
|
@ -71,7 +61,7 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// ActivateAll method
|
||||
/// Calls activate steps on all Device class items
|
||||
/// </summary>
|
||||
public static void ActivateAll()
|
||||
{
|
||||
|
|
@ -182,7 +172,7 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeactivateAll method
|
||||
/// Calls activate on all Device class items
|
||||
/// </summary>
|
||||
public static void DeactivateAll()
|
||||
{
|
||||
|
|
@ -200,17 +190,37 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
}
|
||||
|
||||
//static void ListMethods(string devKey)
|
||||
//{
|
||||
// var dev = GetDeviceForKey(devKey);
|
||||
// if(dev != null)
|
||||
// {
|
||||
// var type = dev.GetType().GetType();
|
||||
// var methods = type.GetMethods(BindingFlags.Public|BindingFlags.Instance);
|
||||
// var sb = new StringBuilder();
|
||||
// sb.AppendLine(string.Format("{2} methods on [{0}] ({1}):", dev.Key, type.Name, methods.Length));
|
||||
// foreach (var m in methods)
|
||||
// {
|
||||
// sb.Append(string.Format("{0}(", m.Name));
|
||||
// var pars = m.GetParameters();
|
||||
// foreach (var p in pars)
|
||||
// sb.Append(string.Format("({1}){0} ", p.Name, p.ParameterType.Name));
|
||||
// sb.AppendLine(")");
|
||||
// }
|
||||
// CrestronConsole.ConsoleCommandResponse(sb.ToString());
|
||||
// }
|
||||
//}
|
||||
|
||||
private static void ListDevices(string s)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse($"{Devices.Count} Devices registered with Device Manager:\r\n");
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, "{0} Devices registered with Device Manager:", Devices.Count);
|
||||
var sorted = Devices.Values.ToList();
|
||||
sorted.Sort((a, b) => a.Key.CompareTo(b.Key));
|
||||
|
||||
foreach (var d in sorted)
|
||||
{
|
||||
var name = d is IKeyName ? (d as IKeyName).Name : "---";
|
||||
CrestronConsole.ConsoleCommandResponse($" [{d.Key}] {name}\r\n");
|
||||
Debug.LogMessage(LogEventLevel.Information, " [{0}] {1}", d.Key, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,17 +229,28 @@ namespace PepperDash.Essentials.Core
|
|||
var dev = GetDeviceForKey(devKey);
|
||||
if (dev == null)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse($"Device '{devKey}' not found\r\n");
|
||||
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
|
||||
return;
|
||||
}
|
||||
if (!(dev is IHasFeedback statusDev))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse($"Device '{devKey}' does not have visible feedbacks\r\n");
|
||||
Debug.LogMessage(LogEventLevel.Information, "Device '{0}' does not have visible feedbacks", devKey);
|
||||
return;
|
||||
}
|
||||
statusDev.DumpFeedbacksToConsole(true);
|
||||
}
|
||||
|
||||
//static void ListDeviceCommands(string devKey)
|
||||
//{
|
||||
// var dev = GetDeviceForKey(devKey);
|
||||
// if (dev == null)
|
||||
// {
|
||||
// Debug.LogMessage(LogEventLevel.Information, "Device '{0}' not found", devKey);
|
||||
// return;
|
||||
// }
|
||||
// Debug.LogMessage(LogEventLevel.Information, "This needs to be reworked. Stay tuned.", devKey);
|
||||
//}
|
||||
|
||||
private static void ListDeviceCommStatuses(string input)
|
||||
{
|
||||
|
||||
|
|
@ -239,9 +260,12 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AddDevice method
|
||||
/// </summary>
|
||||
|
||||
//static void DoDeviceCommand(string command)
|
||||
//{
|
||||
// Debug.LogMessage(LogEventLevel.Information, "Not yet implemented. Stay tuned");
|
||||
//}
|
||||
|
||||
public static void AddDevice(IKeyed newDev)
|
||||
{
|
||||
try
|
||||
|
|
@ -280,9 +304,6 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AddDevice method
|
||||
/// </summary>
|
||||
public static void AddDevice(IEnumerable<IKeyed> devicesToAdd)
|
||||
{
|
||||
try
|
||||
|
|
@ -319,9 +340,6 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RemoveDevice method
|
||||
/// </summary>
|
||||
public static void RemoveDevice(IKeyed newDev)
|
||||
{
|
||||
try
|
||||
|
|
@ -342,18 +360,12 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GetDeviceKeys method
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetDeviceKeys()
|
||||
{
|
||||
//return _Devices.Select(d => d.Key).ToList();
|
||||
return Devices.Keys;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GetDevices method
|
||||
/// </summary>
|
||||
public static IEnumerable<IKeyed> GetDevices()
|
||||
{
|
||||
//return _Devices.Select(d => d.Key).ToList();
|
||||
|
|
@ -390,14 +402,10 @@ namespace PepperDash.Essentials.Core
|
|||
|
||||
return (T)Devices[key];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Console handler that simulates com port data receive
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <summary>
|
||||
/// SimulateComReceiveOnDevice method
|
||||
/// </summary>
|
||||
public static void SimulateComReceiveOnDevice(string s)
|
||||
{
|
||||
// devcomsim:1 xyzabc
|
||||
|
|
@ -421,9 +429,6 @@ namespace PepperDash.Essentials.Core
|
|||
/// Prints a list of routing inputs and outputs by device key.
|
||||
/// </summary>
|
||||
/// <param name="s">Device key from which to report data</param>
|
||||
/// <summary>
|
||||
/// GetRoutingPorts method
|
||||
/// </summary>
|
||||
public static void GetRoutingPorts(string s)
|
||||
{
|
||||
var device = GetDeviceForKey(s);
|
||||
|
|
@ -451,17 +456,14 @@ namespace PepperDash.Essentials.Core
|
|||
/// Attempts to set the debug level of a device
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <summary>
|
||||
/// SetDeviceStreamDebugging method
|
||||
/// </summary>
|
||||
public static void SetDeviceStreamDebugging(string s)
|
||||
{
|
||||
if (String.IsNullOrEmpty(s) || s.Contains("?"))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse(
|
||||
"SETDEVICESTREAMDEBUG [{deviceKey}] [OFF |TX | RX | BOTH] [timeOutInMinutes]\r\n" +
|
||||
" {deviceKey} [OFF | TX | RX | BOTH] - Device to set stream debugging on, and which setting to use\r\n" +
|
||||
" timeOutInMinutes - Set timeout for stream debugging. Default is 30 minutes");
|
||||
@"SETDEVICESTREAMDEBUG [{deviceKey}] [OFF |TX | RX | BOTH] [timeOutInMinutes]
|
||||
{deviceKey} [OFF | TX | RX | BOTH] - Device to set stream debugging on, and which setting to use
|
||||
timeOutInMinutes - Set timeout for stream debugging. Default is 30 minutes");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -519,7 +521,7 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// DisableAllDeviceStreamDebugging method
|
||||
/// Sets stream debugging settings to off for all devices
|
||||
/// </summary>
|
||||
public static void DisableAllDeviceStreamDebugging()
|
||||
{
|
||||
|
|
@ -531,5 +533,4 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,19 +10,25 @@ using Newtonsoft.Json.Linq;
|
|||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
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>
|
||||
/// 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>
|
||||
/// Initializes a new instance of the <see cref="DeviceFactory"/> class and loads all available device factories
|
||||
/// from the current assembly.
|
||||
|
|
@ -36,17 +42,8 @@ namespace PepperDash.Essentials.Core
|
|||
{
|
||||
var programAssemblies = Directory.GetFiles(InitialParametersClass.ProgramDirectory.ToString(), "*.dll");
|
||||
|
||||
// Assemblies known to cause load errors that should be skipped
|
||||
var assembliesToSkip = new[] { "CrestronOnvif.dll" };
|
||||
|
||||
foreach (var assembly in programAssemblies)
|
||||
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);
|
||||
|
|
@ -62,13 +59,13 @@ namespace PepperDash.Essentials.Core
|
|||
// 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");
|
||||
Debug.LogDebug("loaded assembly: {assemblyName}", assembly.GetName().Name);
|
||||
|
||||
PluginLoader.AddLoadedAssembly(assembly.GetName()?.Name ?? "Unknown", assembly);
|
||||
PluginLoader.AddLoadedAssembly(assembly.GetName().Name, assembly);
|
||||
|
||||
var types = assembly.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
|
||||
|
||||
if (types == null || !types.Any())
|
||||
if(types == null || !types.Any())
|
||||
{
|
||||
Debug.LogDebug("No DeviceFactory types found in assembly: {assemblyName}", assembly.GetName().Name);
|
||||
continue;
|
||||
|
|
@ -101,10 +98,10 @@ namespace PepperDash.Essentials.Core
|
|||
{
|
||||
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";
|
||||
|
||||
//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[];
|
||||
AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
||||
}
|
||||
}
|
||||
|
|
@ -114,7 +111,7 @@ namespace PepperDash.Essentials.Core
|
|||
/// 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);
|
||||
new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a factory method for creating instances of a specific type.
|
||||
|
|
@ -127,7 +124,7 @@ namespace PepperDash.Essentials.Core
|
|||
/// 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 });
|
||||
FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -142,7 +139,7 @@ namespace PepperDash.Essentials.Core
|
|||
/// cref="IKeyed"/>.</param>
|
||||
public static void AddFactoryForType(string typeName, string description, Type Type, Func<DeviceConfig, IKeyed> method)
|
||||
{
|
||||
if (FactoryMethods.ContainsKey(typeName))
|
||||
if(FactoryMethods.ContainsKey(typeName))
|
||||
{
|
||||
Debug.LogInformation("Unable to add type: '{typeName}'. Already exists in DeviceFactory", typeName);
|
||||
return;
|
||||
|
|
@ -163,9 +160,7 @@ namespace PepperDash.Essentials.Core
|
|||
|
||||
prop.Parent.Replace(secret);
|
||||
}
|
||||
|
||||
if (!(prop.Value is JObject recurseProp)) continue;
|
||||
|
||||
if (prop.Value is not JObject recurseProp) return;
|
||||
CheckForSecrets(recurseProp.Properties());
|
||||
}
|
||||
}
|
||||
|
|
@ -175,7 +170,7 @@ namespace PepperDash.Essentials.Core
|
|||
var secretProvider = SecretsManager.GetSecretProviderByKey(data.Provider);
|
||||
if (secretProvider == null) return null;
|
||||
var secret = secretProvider.GetSecret(data.Key);
|
||||
if (secret != null) return (string)secret.Value;
|
||||
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);
|
||||
|
|
@ -226,8 +221,7 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex, "Exception occurred while creating device {key}: {message}", dc.Key, ex.Message);
|
||||
Debug.LogDebug(ex, "Exception details: {stackTrace}", ex.StackTrace);
|
||||
Debug.LogError(ex, "Exception occurred while creating device {0}: {1}", null, dc.Key, ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -259,9 +253,9 @@ namespace PepperDash.Essentials.Core
|
|||
}
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse(
|
||||
"Type: '{0}'\r\n" +
|
||||
" Type: '{1}'\r\n" +
|
||||
" Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine);
|
||||
@"Type: '{0}'
|
||||
Type: '{1}'
|
||||
Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,5 +272,4 @@ namespace PepperDash.Essentials.Core
|
|||
? FactoryMethods
|
||||
: FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,8 @@ using System.Linq;
|
|||
namespace PepperDash.Essentials.Core;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a ProcessorExtensionDeviceFactory
|
||||
/// </summary>
|
||||
public class ProcessorExtensionDeviceFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public class ProcessorExtensionDeviceFactory
|
||||
{
|
||||
public ProcessorExtensionDeviceFactory() {
|
||||
var assy = Assembly.GetExecutingAssembly();
|
||||
PluginLoader.AddLoadedAssembly(assy.GetName().Name, assy);
|
||||
|
|
@ -53,8 +47,7 @@ namespace PepperDash.Essentials.Core;
|
|||
/// <summary>
|
||||
/// Adds a plugin factory method
|
||||
/// </summary>
|
||||
/// <param name="extensionName">name fo extension to add</param>
|
||||
/// <param name="method">method to add</param>
|
||||
/// <param name="dc"></param>
|
||||
/// <returns></returns>
|
||||
public static void AddFactoryForType(string extensionName, Func<DeviceConfig, IKeyed> method)
|
||||
{
|
||||
|
|
@ -62,13 +55,6 @@ namespace PepperDash.Essentials.Core;
|
|||
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, new DeviceFactoryWrapper() { FactoryMethod = method });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
|
|
@ -117,9 +103,6 @@ namespace PepperDash.Essentials.Core;
|
|||
/// </summary>
|
||||
/// <param name="dc"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// GetExtensionDevice method
|
||||
/// </summary>
|
||||
public static IKeyed GetExtensionDevice(DeviceConfig dc)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -7,16 +7,11 @@ using PepperDash.Core;
|
|||
using PepperDash.Essentials.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common
|
||||
namespace PepperDash.Essentials.Devices.Common;
|
||||
|
||||
public class DeviceFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a DeviceFactory
|
||||
/// </summary>
|
||||
public class DeviceFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DeviceFactory class
|
||||
/// </summary>
|
||||
|
||||
public DeviceFactory()
|
||||
{
|
||||
var assy = Assembly.GetExecutingAssembly();
|
||||
|
|
@ -53,12 +48,10 @@ namespace PepperDash.Essentials.Devices.Common
|
|||
foreach (var typeName in deviceFactory.TypeNames)
|
||||
{
|
||||
//Debug.LogMessage(LogEventLevel.Verbose, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
|
||||
string description = (deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0)
|
||||
? descriptionAttribute[0].Description
|
||||
: "No description available";
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,16 +4,10 @@ using System.Reflection;
|
|||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
namespace PepperDash.Essentials;
|
||||
|
||||
public class MobileControlFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory class for the Mobile Control App Controller
|
||||
/// </summary>
|
||||
public class MobileControlFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of the <see cref="MobileControlFactory"/> class.
|
||||
/// </summary>
|
||||
public MobileControlFactory()
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
|
@ -53,12 +47,11 @@ namespace PepperDash.Essentials
|
|||
{
|
||||
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"; // Default value if no DescriptionAttribute is found
|
||||
|
||||
DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice);
|
||||
}
|
||||
//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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,65 +12,63 @@ using PepperDash.Essentials.Core;
|
|||
using PepperDash.Essentials.Core.Bridges;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Core.Routing;
|
||||
using System.Threading;
|
||||
using Timeout = Crestron.SimplSharp.Timeout;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Main control system class that inherits from CrestronControlSystem and manages program lifecycle
|
||||
/// </summary>
|
||||
public class ControlSystem : CrestronControlSystem, ILoadConfig
|
||||
{
|
||||
HttpLogoServer LogoServer;
|
||||
namespace PepperDash.Essentials;
|
||||
|
||||
private CTimer _startTimer;
|
||||
private CEvent _initializeEvent;
|
||||
/// <summary>
|
||||
/// Represents the main control system for the application, providing initialization, configuration loading, and device
|
||||
/// management functionality.
|
||||
/// </summary>
|
||||
/// <remarks>This class extends <see cref="CrestronControlSystem"/> and serves as the entry point for the control
|
||||
/// system. It manages the initialization of devices, rooms, tie lines, and other system components. Additionally, it
|
||||
/// provides methods for platform determination, configuration loading, and system teardown.</remarks>
|
||||
public class ControlSystem : CrestronControlSystem, ILoadConfig
|
||||
{
|
||||
private HttpLogoServer LogoServer;
|
||||
|
||||
private Timer _startTimer;
|
||||
private ManualResetEventSlim _initializeEvent;
|
||||
private const long StartupTime = 500;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ControlSystem class
|
||||
/// Initializes a new instance of the <see cref="ControlSystem"/> class, setting up the system's global state and
|
||||
/// dependencies.
|
||||
/// </summary>
|
||||
/// <remarks>This constructor configures the control system by initializing key components such as the
|
||||
/// device manager and secrets manager, and sets global properties like the maximum number of user threads and the
|
||||
/// program initialization state. It also adjusts the error log's minimum debug level based on the device
|
||||
/// platform.</remarks>
|
||||
public ControlSystem()
|
||||
: base()
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
Crestron.SimplSharpPro.CrestronThread.Thread.MaxNumberOfUserThreads = 400;
|
||||
|
||||
Thread.MaxNumberOfUserThreads = 400;
|
||||
Global.ControlSystem = this;
|
||||
DeviceManager.Initialize(this);
|
||||
SecretsManager.Initialize();
|
||||
SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true;
|
||||
|
||||
Debug.SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? LogEventLevel.Warning : LogEventLevel.Verbose);
|
||||
|
||||
// AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
|
||||
}
|
||||
|
||||
private System.Reflection.Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
|
||||
catch (Exception e)
|
||||
{
|
||||
var assemblyName = new System.Reflection.AssemblyName(args.Name).Name;
|
||||
if (assemblyName == "PepperDash_Core")
|
||||
{
|
||||
return System.Reflection.Assembly.LoadFrom("PepperDashCore.dll");
|
||||
Debug.LogError(e, "FATAL INITIALIZE ERROR. System is in an inconsistent state");
|
||||
}
|
||||
|
||||
if (assemblyName == "PepperDash_Essentials_Core")
|
||||
{
|
||||
return System.Reflection.Assembly.LoadFrom("PepperDash.Essentials.Core.dll");
|
||||
}
|
||||
|
||||
if (assemblyName == "Essentials Devices Common")
|
||||
{
|
||||
return System.Reflection.Assembly.LoadFrom("PepperDash.Essentials.Devices.Common.dll");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// InitializeSystem method
|
||||
/// Initializes the control system and prepares it for operation.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
/// <remarks>This method ensures that all devices in the system are properly registered and initialized
|
||||
/// before the system is fully operational. If the control system is of a DMPS type, the method waits for all
|
||||
/// devices to activate, allowing HD-BaseT DM endpoints to register before completing initialization. For non-DMPS
|
||||
/// systems, initialization proceeds without waiting.</remarks>
|
||||
public override void InitializeSystem()
|
||||
{
|
||||
// If the control system is a DMPS type, we need to wait to exit this method until all devices have had time to activate
|
||||
|
|
@ -78,22 +76,26 @@ namespace PepperDash.Essentials
|
|||
bool preventInitializationComplete = Global.ControlSystemIsDmpsType;
|
||||
if (preventInitializationComplete)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, "******************* InitializeSystem() Entering **********************");
|
||||
Debug.LogMessage(LogEventLevel.Debug, "******************* Initializing System **********************");
|
||||
|
||||
_startTimer = new Timer(StartSystem, preventInitializationComplete, StartupTime, Timeout.Infinite);
|
||||
|
||||
_initializeEvent = new ManualResetEventSlim(false);
|
||||
|
||||
_startTimer = new CTimer(StartSystem, preventInitializationComplete, StartupTime);
|
||||
_initializeEvent = new CEvent(true, false);
|
||||
DeviceManager.AllDevicesRegistered += (o, a) =>
|
||||
{
|
||||
_initializeEvent.Set();
|
||||
};
|
||||
|
||||
_initializeEvent.Wait(30000);
|
||||
Debug.LogMessage(LogEventLevel.Debug, "******************* InitializeSystem() Exiting **********************");
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, "******************* System Initialization Complete **********************");
|
||||
|
||||
SystemMonitor.ProgramInitialization.ProgramInitializationComplete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_startTimer = new CTimer(StartSystem, preventInitializationComplete, StartupTime);
|
||||
_startTimer = new Timer(StartSystem, preventInitializationComplete, StartupTime, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +135,7 @@ namespace PepperDash.Essentials
|
|||
CrestronConsole.ConsoleCommandResponse
|
||||
("Current running configuration. This is the merged system and template configuration" + CrestronEnvironment.NewLine);
|
||||
CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject
|
||||
(ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented).Replace(Environment.NewLine, "\r\n"));
|
||||
(ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented));
|
||||
}, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(s =>
|
||||
|
|
@ -152,7 +154,6 @@ namespace PepperDash.Essentials
|
|||
CrestronConsole.AddNewConsoleCommand(DeviceManager.GetRoutingPorts,
|
||||
"getroutingports", "Reports all routing ports, if any. Requires a device key", ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
//DeviceManager.AddDevice(new EssentialsWebApi("essentialsWebApi", "Essentials Web API"));
|
||||
|
||||
if (!Debug.DoNotLoadConfigOnNextBoot)
|
||||
{
|
||||
|
|
@ -167,7 +168,9 @@ namespace PepperDash.Essentials
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeterminePlatform method
|
||||
/// Determines if the program is running on a processor (appliance) or server (VC-4).
|
||||
///
|
||||
/// Sets Global.FilePathPrefix and Global.ApplicationDirectoryPathPrefix based on platform
|
||||
/// </summary>
|
||||
public void DeterminePlatform()
|
||||
{
|
||||
|
|
@ -238,7 +241,7 @@ namespace PepperDash.Essentials
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// GoWithLoad method
|
||||
/// Begins the process of loading resources including plugins and configuration data
|
||||
/// </summary>
|
||||
public void GoWithLoad()
|
||||
{
|
||||
|
|
@ -249,13 +252,6 @@ namespace PepperDash.Essentials
|
|||
PluginLoader.AddProgramAssemblies();
|
||||
|
||||
_ = new Core.DeviceFactory();
|
||||
// _ = new Devices.Common.DeviceFactory();
|
||||
// _ = new DeviceFactory();
|
||||
|
||||
// _ = new ProcessorExtensionDeviceFactory();
|
||||
// _ = new MobileControlFactory();
|
||||
|
||||
LoadAssets();
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, "Starting Essentials load from configuration");
|
||||
|
||||
|
|
@ -375,7 +371,7 @@ namespace PepperDash.Essentials
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// LoadDevices method
|
||||
/// Reads all devices from config and adds them to DeviceManager
|
||||
/// </summary>
|
||||
public void LoadDevices()
|
||||
{
|
||||
|
|
@ -436,7 +432,7 @@ namespace PepperDash.Essentials
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// LoadTieLines method
|
||||
/// Helper method to load tie lines. This should run after devices have loaded
|
||||
/// </summary>
|
||||
public void LoadTieLines()
|
||||
{
|
||||
|
|
@ -462,7 +458,7 @@ namespace PepperDash.Essentials
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// LoadRooms method
|
||||
/// Reads all rooms from config and adds them to DeviceManager
|
||||
/// </summary>
|
||||
public void LoadRooms()
|
||||
{
|
||||
|
|
@ -697,5 +693,5 @@ namespace PepperDash.Essentials
|
|||
jsonFile.MoveTo(finalPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,17 +7,14 @@ using System.Reflection;
|
|||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Responsible for loading all of the device types for this library
|
||||
/// </summary>
|
||||
public class DeviceFactory
|
||||
{
|
||||
namespace PepperDash.Essentials;
|
||||
|
||||
/// <summary>
|
||||
/// Responsible for loading all of the device types for this library
|
||||
/// </summary>
|
||||
public class DeviceFactory
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DeviceFactory class and loads all device type factories
|
||||
/// </summary>
|
||||
public DeviceFactory()
|
||||
{
|
||||
var assy = Assembly.GetExecutingAssembly();
|
||||
|
|
@ -53,12 +50,11 @@ namespace PepperDash.Essentials
|
|||
{
|
||||
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";
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,29 +27,6 @@
|
|||
<DebugType>pdbonly</DebugType>
|
||||
<DocumentationFile>bin\$(Configuration)\PepperDashEssentials.xml</DocumentationFile>
|
||||
</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>
|
||||
<PackageReference Include="Crestron.SimplSharp.SDK.Program" Version="2.21.128" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue