chore: remove duplication namespace declaration

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Andrew Welker 2025-07-07 10:06:59 -05:00 committed by Neil Dorin
parent 7f60dcb4cf
commit 5d90fafbd7

View file

@ -4,18 +4,15 @@ using System.Linq;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using System.Reflection; using System.Reflection;
using System.IO; using System.IO;
using Newtonsoft.Json;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using SystemIO = System.IO;
using CrestronIO = System.IO;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials;
namespace PepperDash.Essentials;
/// <summary> /// <summary>
/// Provides functionality for loading and managing plugins and assemblies in the application. /// Provides functionality for loading and managing plugins and assemblies in the application.
/// </summary> /// </summary>
@ -75,19 +72,6 @@ public static class PluginLoader
/// </summary> /// </summary>
private static string TempDirectory => PluginDirectory + Global.DirectorySeparator + "temp"; private static string TempDirectory => PluginDirectory + Global.DirectorySeparator + "temp";
/// <summary>
/// The directory to look in for .cplz plugin packages
/// </summary>
static string _pluginDirectory => Global.FilePathPrefix + "plugins";
/// <summary>
/// The directory where plugins will be moved to and loaded from
/// </summary>
static string _loadedPluginsDirectoryPath => _pluginDirectory + Global.DirectorySeparator + "loadedAssemblies";
// The temp directory where .cplz archives will be unzipped to
static string _tempDirectory => _pluginDirectory + Global.DirectorySeparator + "temp";
/// <summary> /// <summary>
/// Represents a collection of fully qualified type names that are known to be incompatible with the current /// Represents a collection of fully qualified type names that are known to be incompatible with the current
/// application or framework. /// application or framework.
@ -96,8 +80,8 @@ public static class PluginLoader
/// incompatible with the intended usage of the application. These types may represent security risks, unsupported /// incompatible with the intended usage of the application. These types may represent security risks, unsupported
/// features, or legacy APIs that should be avoided.</remarks> /// features, or legacy APIs that should be avoided.</remarks>
private static readonly HashSet<string> KnownIncompatibleTypes = private static readonly HashSet<string> KnownIncompatibleTypes =
[ [
"System.Net.ICertificatePolicy", "System.Net.ICertificatePolicy",
"System.Security.Cryptography.SHA1CryptoServiceProvider", "System.Security.Cryptography.SHA1CryptoServiceProvider",
"System.Web.HttpUtility", "System.Web.HttpUtility",
"System.Configuration.ConfigurationManager", "System.Configuration.ConfigurationManager",
@ -108,7 +92,7 @@ public static class PluginLoader
"System.Security.SecurityManager", "System.Security.SecurityManager",
"System.Security.Permissions.FileIOPermission", "System.Security.Permissions.FileIOPermission",
"System.AppDomain.CreateDomain" "System.AppDomain.CreateDomain"
]; ];
/// <summary> /// <summary>
/// Initializes static members of the <see cref="PluginLoader"/> class. /// Initializes static members of the <see cref="PluginLoader"/> class.
@ -142,7 +126,6 @@ public static class PluginLoader
Debug.LogMessage(LogEventLevel.Verbose, "Found {0} Assemblies", assemblyFiles.Length); Debug.LogMessage(LogEventLevel.Verbose, "Found {0} Assemblies", assemblyFiles.Length);
foreach (var fi in assemblyFiles.Where(fi => fi.Name.Contains("Essentials") || fi.Name.Contains("PepperDash"))) foreach (var fi in assemblyFiles.Where(fi => fi.Name.Contains("Essentials") || fi.Name.Contains("PepperDash")))
{ {
string version = string.Empty; string version = string.Empty;
@ -190,7 +173,6 @@ public static class PluginLoader
} }
} }
/// <summary> /// <summary>
/// Associates the specified assembly with the given name in the loaded assemblies collection. /// Associates the specified assembly with the given name in the loaded assemblies collection.
/// </summary> /// </summary>
@ -314,7 +296,7 @@ public static class PluginLoader
{ {
string fileName = Path.GetFileName(filePath); string fileName = Path.GetFileName(filePath);
Debug.LogMessage(LogEventLevel.Warning, "Assembly '{0}' is not compatible with .NET 8: {1}", fileName, reason); Debug.LogMessage(LogEventLevel.Warning, "Assembly '{0}' is not compatible with .NET 8: {1}", fileName, reason);
var incompatiblePlugin = new IncompatiblePlugin(fileName, reason, requestedBy); var incompatiblePlugin = new IncompatiblePlugin(fileName, reason, requestedBy);
IncompatiblePlugins.Add(incompatiblePlugin); IncompatiblePlugins.Add(incompatiblePlugin);
return null; return null;
@ -335,15 +317,15 @@ public static class PluginLoader
} }
return null; return null;
} }
catch (FileLoadException ex) when (ex.Message.Contains("Assembly with same name is already loaded")) catch(FileLoadException ex) when (ex.Message.Contains("Assembly with same name is already loaded"))
{ {
// Get the assembly name from the file path // Get the assembly name from the file path
string assemblyName = Path.GetFileNameWithoutExtension(filePath); string assemblyName = Path.GetFileNameWithoutExtension(filePath);
// Try to find the already loaded assembly // Try to find the already loaded assembly
var existingAssembly = AppDomain.CurrentDomain.GetAssemblies() var existingAssembly = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.OrdinalIgnoreCase)); .FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.OrdinalIgnoreCase));
if (existingAssembly != null) if (existingAssembly != null)
{ {
Debug.LogMessage(LogEventLevel.Information, "Assembly '{0}' is already loaded, using existing instance", assemblyName); Debug.LogMessage(LogEventLevel.Information, "Assembly '{0}' is already loaded, using existing instance", assemblyName);
@ -352,19 +334,19 @@ public static class PluginLoader
LoadedAssemblies.Add(loadedAssembly); LoadedAssemblies.Add(loadedAssembly);
return loadedAssembly; return loadedAssembly;
} }
Debug.LogMessage(LogEventLevel.Warning, "Assembly with same name already loaded but couldn't find it: {0}", filePath); Debug.LogMessage(LogEventLevel.Warning, "Assembly with same name already loaded but couldn't find it: {0}", filePath);
return null; return null;
} }
catch (Exception ex) catch(Exception ex)
{ {
string fileName = Path.GetFileName(filePath); string fileName = Path.GetFileName(filePath);
// Check if this might be a .NET Framework compatibility issue // Check if this might be a .NET Framework compatibility issue
if (ex.Message.Contains("Could not load type") || if (ex.Message.Contains("Could not load type") ||
ex.Message.Contains("Unable to load one or more of the requested types")) ex.Message.Contains("Unable to load one or more of the requested types"))
{ {
Debug.LogMessage(LogEventLevel.Error, "Error loading assembly {0}: Likely .NET 8 compatibility issue: {1}", Debug.LogMessage(LogEventLevel.Error, "Error loading assembly {0}: Likely .NET 8 compatibility issue: {1}",
fileName, ex.Message); fileName, ex.Message);
IncompatiblePlugins.Add(new IncompatiblePlugin(fileName, ex.Message, requestedBy)); IncompatiblePlugins.Add(new IncompatiblePlugin(fileName, ex.Message, requestedBy));
} }
@ -454,12 +436,12 @@ public static class PluginLoader
{ {
if (plugin.TriggeredBy != "Direct load") if (plugin.TriggeredBy != "Direct load")
{ {
CrestronConsole.ConsoleCommandResponse("{0}: {1} (Required by: {2})" + CrestronEnvironment.NewLine, CrestronConsole.ConsoleCommandResponse("{0}: {1} (Required by: {2})" + CrestronEnvironment.NewLine,
plugin.Name, plugin.Reason, plugin.TriggeredBy); plugin.Name, plugin.Reason, plugin.TriggeredBy);
} }
else else
{ {
CrestronConsole.ConsoleCommandResponse("{0}: {1}" + CrestronEnvironment.NewLine, CrestronConsole.ConsoleCommandResponse("{0}: {1}" + CrestronEnvironment.NewLine,
plugin.Name, plugin.Reason); plugin.Name, plugin.Reason);
} }
} }
@ -626,29 +608,29 @@ public static class PluginLoader
// First, check compatibility of all assemblies before loading any // First, check compatibility of all assemblies before loading any
var assemblyCompatibility = new Dictionary<string, (bool IsCompatible, string Reason, List<string> References)>(); var assemblyCompatibility = new Dictionary<string, (bool IsCompatible, string Reason, List<string> References)>();
foreach (var pluginFile in pluginFiles) foreach (var pluginFile in pluginFiles)
{ {
string fileName = pluginFile.Name; string fileName = pluginFile.Name;
assemblyCompatibility[fileName] = IsPluginCompatibleWithNet8(pluginFile.FullName); assemblyCompatibility[fileName] = IsPluginCompatibleWithNet8(pluginFile.FullName);
} }
// Now load compatible assemblies and track incompatible ones // Now load compatible assemblies and track incompatible ones
foreach (var pluginFile in pluginFiles) foreach (var pluginFile in pluginFiles)
{ {
string fileName = pluginFile.Name; string fileName = pluginFile.Name;
var (isCompatible, reason, _) = assemblyCompatibility[fileName]; var (isCompatible, reason, _) = assemblyCompatibility[fileName];
if (!isCompatible) if (!isCompatible)
{ {
Debug.LogMessage(LogEventLevel.Warning, "Assembly '{0}' is not compatible with .NET 8: {1}", fileName, reason); Debug.LogMessage(LogEventLevel.Warning, "Assembly '{0}' is not compatible with .NET 8: {1}", fileName, reason);
IncompatiblePlugins.Add(new IncompatiblePlugin(fileName, reason, null)); IncompatiblePlugins.Add(new IncompatiblePlugin(fileName, reason, null));
continue; continue;
} }
// Try to load the assembly // Try to load the assembly
var loadedAssembly = LoadAssembly(pluginFile.FullName, null); var loadedAssembly = LoadAssembly(pluginFile.FullName, null);
if (loadedAssembly != null) if (loadedAssembly != null)
{ {
LoadedPluginFolderAssemblies.Add(loadedAssembly); LoadedPluginFolderAssemblies.Add(loadedAssembly);
@ -668,13 +650,13 @@ public static class PluginLoader
private static void LoadCustomPluginTypes() private static void LoadCustomPluginTypes()
{ {
Debug.LogMessage(LogEventLevel.Information, "Loading Custom Plugin Types..."); Debug.LogMessage(LogEventLevel.Information, "Loading Custom Plugin Types...");
foreach (var loadedAssembly in LoadedPluginFolderAssemblies) foreach (var loadedAssembly in LoadedPluginFolderAssemblies)
{ {
// Skip if assembly is null (can happen if we had loading issues) // Skip if assembly is null (can happen if we had loading issues)
if (loadedAssembly == null || loadedAssembly.Assembly == null) if (loadedAssembly == null || loadedAssembly.Assembly == null)
continue; continue;
// iteratate this assembly's classes, looking for "LoadPlugin()" methods // iteratate this assembly's classes, looking for "LoadPlugin()" methods
try try
{ {
@ -689,7 +671,7 @@ public static class PluginLoader
{ {
Debug.LogMessage(LogEventLevel.Error, "Unable to get types for assembly {0}: {1}", Debug.LogMessage(LogEventLevel.Error, "Unable to get types for assembly {0}: {1}",
loadedAssembly.Name, e.Message); loadedAssembly.Name, e.Message);
// Check if any of the loader exceptions are due to missing assemblies // Check if any of the loader exceptions are due to missing assemblies
foreach (var loaderEx in e.LoaderExceptions) foreach (var loaderEx in e.LoaderExceptions)
{ {
@ -698,18 +680,18 @@ public static class PluginLoader
string missingAssembly = fileNotFoundEx.FileName; string missingAssembly = fileNotFoundEx.FileName;
if (!string.IsNullOrEmpty(missingAssembly)) if (!string.IsNullOrEmpty(missingAssembly))
{ {
Debug.LogMessage(LogEventLevel.Warning, "Assembly {0} requires missing dependency: {1}", Debug.LogMessage(LogEventLevel.Warning, "Assembly {0} requires missing dependency: {1}",
loadedAssembly.Name, missingAssembly); loadedAssembly.Name, missingAssembly);
// Add to incompatible plugins with dependency information // Add to incompatible plugins with dependency information
IncompatiblePlugins.Add(new IncompatiblePlugin( IncompatiblePlugins.Add(new IncompatiblePlugin(
Path.GetFileName(missingAssembly), Path.GetFileName(missingAssembly),
$"Missing dependency required by {loadedAssembly.Name}", $"Missing dependency required by {loadedAssembly.Name}",
loadedAssembly.Name)); loadedAssembly.Name));
} }
} }
} }
Debug.LogMessage(LogEventLevel.Verbose, e.StackTrace); Debug.LogMessage(LogEventLevel.Verbose, e.StackTrace);
continue; continue;
} }
@ -718,18 +700,19 @@ public static class PluginLoader
Debug.LogMessage(LogEventLevel.Error, "Unable to get types for assembly {0}: {1}", Debug.LogMessage(LogEventLevel.Error, "Unable to get types for assembly {0}: {1}",
loadedAssembly.Name, e.Message); loadedAssembly.Name, e.Message);
Debug.LogMessage(LogEventLevel.Verbose, e.StackTrace); Debug.LogMessage(LogEventLevel.Verbose, e.StackTrace);
// Add to incompatible plugins if this is likely a .NET 8 compatibility issue // Add to incompatible plugins if this is likely a .NET 8 compatibility issue
if (e.Message.Contains("Could not load type") || if (e.Message.Contains("Could not load type") ||
e.Message.Contains("Unable to load one or more of the requested types")) e.Message.Contains("Unable to load one or more of the requested types"))
{ {
IncompatiblePlugins.Add(new IncompatiblePlugin(loadedAssembly.Name, IncompatiblePlugins.Add(new IncompatiblePlugin(loadedAssembly.Name,
$"Type loading error: {e.Message}", $"Type loading error: {e.Message}",
null)); null));
} }
continue; continue;
} }
foreach (var type in types) foreach (var type in types)
{ {
try try
@ -759,26 +742,26 @@ public static class PluginLoader
Debug.LogMessage(LogEventLevel.Information, "Error Loading assembly {0}: {1}", Debug.LogMessage(LogEventLevel.Information, "Error Loading assembly {0}: {1}",
loadedAssembly.Name, e.Message); loadedAssembly.Name, e.Message);
Debug.LogMessage(LogEventLevel.Verbose, "{0}", e.StackTrace); Debug.LogMessage(LogEventLevel.Verbose, "{0}", e.StackTrace);
// Add to incompatible plugins if this is likely a .NET 8 compatibility issue // Add to incompatible plugins if this is likely a .NET 8 compatibility issue
if (e.Message.Contains("Could not load type") || if (e.Message.Contains("Could not load type") ||
e.Message.Contains("Unable to load one or more of the requested types")) e.Message.Contains("Unable to load one or more of the requested types"))
{ {
IncompatiblePlugins.Add(new IncompatiblePlugin(loadedAssembly.Name, IncompatiblePlugins.Add(new IncompatiblePlugin(loadedAssembly.Name,
$"Assembly loading error: {e.Message}", $"Assembly loading error: {e.Message}",
null)); null));
} }
continue; continue;
} }
} }
// Update incompatible plugins with dependency information // Update incompatible plugins with dependency information
var pluginDependencies = new Dictionary<string, List<string>>(); var pluginDependencies = new Dictionary<string, List<string>>();
// Populate pluginDependencies with relevant data // Populate pluginDependencies with relevant data
// Example: pluginDependencies["PluginA"] = new List<string> { "Dependency1", "Dependency2" }; // Example: pluginDependencies["PluginA"] = new List<string> { "Dependency1", "Dependency2" };
UpdateIncompatiblePluginDependencies(pluginDependencies); UpdateIncompatiblePluginDependencies(pluginDependencies);
// plugin dll will be loaded. Any classes in plugin should have a static constructor // plugin dll will be loaded. Any classes in plugin should have a static constructor
// that registers that class with the Core.DeviceFactory // that registers that class with the Core.DeviceFactory
Debug.LogMessage(LogEventLevel.Information, "Done Loading Custom Plugin Types."); Debug.LogMessage(LogEventLevel.Information, "Done Loading Custom Plugin Types.");
@ -800,15 +783,15 @@ public static class PluginLoader
// If it already has a requestedBy, skip it // If it already has a requestedBy, skip it
if (incompatiblePlugin.TriggeredBy != "Direct load") if (incompatiblePlugin.TriggeredBy != "Direct load")
continue; continue;
// Find plugins that depend on this incompatible plugin // Find plugins that depend on this incompatible plugin
foreach (var plugin in pluginDependencies) foreach (var plugin in pluginDependencies)
{ {
string pluginName = plugin.Key; string pluginName = plugin.Key;
List<string> dependencies = plugin.Value; List<string> dependencies = plugin.Value;
// If this plugin depends on the incompatible plugin // If this plugin depends on the incompatible plugin
if (dependencies.Contains(incompatiblePlugin.Name) || if (dependencies.Contains(incompatiblePlugin.Name) ||
dependencies.Any(d => d.StartsWith(incompatiblePlugin.Name + ","))) dependencies.Any(d => d.StartsWith(incompatiblePlugin.Name + ",")))
{ {
incompatiblePlugin.UpdateTriggeringPlugin(pluginName); incompatiblePlugin.UpdateTriggeringPlugin(pluginName);
@ -853,7 +836,7 @@ public static class PluginLoader
LoadDeviceFactories(deviceFactory); LoadDeviceFactories(deviceFactory);
if (!EssentialsPluginAssemblies.Contains(loadedAssembly)) if(!EssentialsPluginAssemblies.Contains(loadedAssembly))
EssentialsPluginAssemblies.Add(loadedAssembly); EssentialsPluginAssemblies.Add(loadedAssembly);
} }
@ -908,7 +891,7 @@ public static class PluginLoader
// Load the types from any custom plugin assemblies // Load the types from any custom plugin assemblies
LoadCustomPluginTypes(); LoadCustomPluginTypes();
} }
// Report on incompatible plugins // Report on incompatible plugins
if (IncompatiblePlugins.Count > 0) if (IncompatiblePlugins.Count > 0)
{ {
@ -917,12 +900,12 @@ public static class PluginLoader
{ {
if (plugin.TriggeredBy != "Direct load") if (plugin.TriggeredBy != "Direct load")
{ {
Debug.LogMessage(LogEventLevel.Warning, " - {0}: {1} (Required by: {2})", Debug.LogMessage(LogEventLevel.Warning, " - {0}: {1} (Required by: {2})",
plugin.Name, plugin.Reason, plugin.TriggeredBy); plugin.Name, plugin.Reason, plugin.TriggeredBy);
} }
else else
{ {
Debug.LogMessage(LogEventLevel.Warning, " - {0}: {1}", Debug.LogMessage(LogEventLevel.Warning, " - {0}: {1}",
plugin.Name, plugin.Reason); plugin.Name, plugin.Reason);
} }
} }