diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs
index e052f274..46ac77f2 100644
--- a/PepperDashEssentials/ControlSystem.cs
+++ b/PepperDashEssentials/ControlSystem.cs
@@ -25,6 +25,7 @@ namespace PepperDash.Essentials
{
HttpLogoServer LogoServer;
+
public ControlSystem()
: base()
{
@@ -46,6 +47,8 @@ namespace PepperDash.Essentials
ConsoleAccessLevelEnum.AccessOperator);
}
+ CrestronConsole.AddNewConsoleCommand(PluginLoader.ReportAssemblyVersions, "reportversions", "Reports the versions of the loaded assemblies", ConsoleAccessLevelEnum.AccessOperator);
+
// CrestronConsole.AddNewConsoleCommand(S => { ConfigWriter.WriteConfigFile(null); }, "writeconfig", "writes the current config to a file", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>
{
@@ -78,10 +81,12 @@ namespace PepperDash.Essentials
GoWithLoad();
}
+
+
///
/// Determines if the program is running on a processor (appliance) or server (VC-4).
///
- /// Sets Global.FilePathPrefix based on platform
+ /// Sets Global.FilePathPrefix and Global.ApplicationDirectoryPathPrefix based on platform
///
public void DeterminePlatform()
{
@@ -108,7 +113,7 @@ namespace PepperDash.Essentials
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on 3-series Appliance", Global.AssemblyVersion);
// Check if User/ProgramX exists
- if (Directory.Exists(directoryPrefix + dirSeparator + "User"
+ if (Directory.Exists(Global.ApplicationDirectoryPathPrefix + dirSeparator + "User"
+ dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber)))
{
Debug.Console(0, @"User/program{0} directory found", InitialParametersClass.ApplicationNumber);
@@ -158,11 +163,13 @@ namespace PepperDash.Essentials
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials load from configuration");
+ PluginLoader.AddProgramAssemblies();
+
var filesReady = SetupFilesystem();
if (filesReady)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Checking for plugins");
- LoadPlugins();
+ PluginLoader.LoadPlugins();
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Folder structure verified. Loading config...");
if (!ConfigReader.LoadConfig2())
@@ -197,118 +204,7 @@ namespace PepperDash.Essentials
}
- ///
- /// Initial simple implementation. Reads user/programXX/plugins folder and
- /// use
- ///
- void LoadPlugins()
- {
- var dir = Global.FilePathPrefix + "plugins";
- if (Directory.Exists(dir))
- {
- // TODO Clear out or create localPlugins folder (maybe in program slot folder)
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Plugins directory found, checking for factory plugins");
- var di = new DirectoryInfo(dir);
- var zFiles = di.GetFiles("*.cplz");
- foreach (var fi in zFiles)
- {
- Debug.Console(0, "Found cplz: {0}. Unzipping into plugins directory", fi.Name);
- var result = CrestronZIP.Unzip(fi.FullName, di.FullName);
- Debug.Console(0, "UnZip Result: {0}", result.ToString());
- fi.Delete();
- }
- var files = di.GetFiles("*.dll");
- Dictionary assyList = new Dictionary();
- foreach (FileInfo fi in files)
- {
- // TODO COPY plugin to loadedPlugins folder
- // TODO LOAD that loadedPlugins dll file
- try
- {
- var assy = Assembly.LoadFrom(fi.FullName);
- var ver = assy.GetName().Version;
- var verStr = string.Format("{0}.{1}.{2}.{3}", ver.Major, ver.Minor, ver.Build, ver.Revision);
- assyList.Add(fi.FullName, assy);
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loaded plugin file '{0}', version {1}", fi.FullName, verStr);
- }
- catch
- {
- Debug.Console(2, "Assembly {0} is not a custom assembly", fi.FullName);
- continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here
- }
- }
- foreach (var assy in assyList)
- {
- // iteratate this assembly's classes, looking for "LoadPlugin()" methods
- try
- {
- var types = assy.Value.GetTypes();
- foreach (var type in types)
- {
- try
- {
- var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static);
- var loadPlugin = methods.FirstOrDefault(m => m.Name.Equals("LoadPlugin"));
- if (loadPlugin != null)
- {
- Debug.Console(2, "LoadPlugin method found in {0}", type.Name);
-
- var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static);
-
- var minimumVersion = fields.FirstOrDefault(p => p.Name.Equals("MinimumEssentialsFrameworkVersion"));
- if (minimumVersion != null)
- {
- Debug.Console(2, "MinimumEssentialsFrameworkVersion found");
-
- var minimumVersionString = minimumVersion.GetValue(null) as string;
-
- if (!string.IsNullOrEmpty(minimumVersionString))
- {
- var passed = Global.IsRunningMinimumVersionOrHigher(minimumVersionString);
-
- if (!passed)
- {
- Debug.Console(0, Debug.ErrorLogLevel.Error, "Plugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin", minimumVersionString);
- continue;
- }
- else
- {
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Passed plugin passed dependency check (required version {0})", minimumVersionString);
- }
- }
- else
- {
- Debug.Console(0, Debug.ErrorLogLevel.Warning, "MinimumEssentialsFrameworkVersion found but not set. Loading plugin, but your mileage may vary.");
- }
- }
- else
- {
- Debug.Console(0, Debug.ErrorLogLevel.Warning, "MinimumEssentialsFrameworkVersion not found. Loading plugin, but your mileage may vary.");
- }
-
- Debug.Console(0, Debug.ErrorLogLevel.Notice, "Adding plugin: {0}", assy.Key);
- loadPlugin.Invoke(null, null);
- }
- }
- catch
- {
- Debug.Console(2, "Load Plugin not found. {0} is not a plugin assembly", assy.Value.FullName);
- continue;
- }
-
- }
- }
- catch
- {
- Debug.Console(2, "Assembly {0} is not a custom assembly. Types cannot be loaded.", assy.Value.FullName);
- continue;
- }
- }
- // plugin dll will be loaded. Any classes in plugin should have a static constructor
- // that registers that class with the Core.DeviceFactory
- }
- }
+
///
/// Verifies filesystem is set up. IR, SGD, and programX folders
diff --git a/PepperDashEssentials/HttpApiHandler.cs b/PepperDashEssentials/HttpApiHandler.cs
deleted file mode 100644
index 0a757999..00000000
--- a/PepperDashEssentials/HttpApiHandler.cs
+++ /dev/null
@@ -1,266 +0,0 @@
-//using System;
-//using System.Collections.Generic;
-//using System.Linq;
-//using System.Text;
-//using Crestron.SimplSharp;
-//using Crestron.SimplSharp.CrestronIO;
-//using Crestron.SimplSharp.Net.Http;
-
-//using Newtonsoft.Json;
-//using Newtonsoft.Json.Linq;
-
-//using PepperDash.Essentials.Core;
-//using PepperDash.Essentials.Core.Http;
-//using PepperDash.Core;
-
-//namespace PepperDash.Essentials
-//{
-// public class EssentialsHttpApiHandler
-// {
-// string ConfigPath;
-// string PresetsPathPrefix;
-// EssentialsHttpServer Server;
-
-// ///
-// ///
-// ///
-// /// HTTP server to attach to
-// /// The full path to configuration file
-// /// The folder prefix for the presets path, eq "\HTML\presets\"
-// public EssentialsHttpApiHandler(EssentialsHttpServer server, string configPath, string presetsPathPrefix)
-// {
-// if (server == null) throw new ArgumentNullException("server");
-// Server = server;
-// ConfigPath = configPath;
-// PresetsPathPrefix = presetsPathPrefix;
-// server.ApiRequest += Server_ApiRequest;
-// }
-
-
-// void Server_ApiRequest(object sender, Crestron.SimplSharp.Net.Http.OnHttpRequestArgs args)
-// {
-// try
-// {
-// var path = args.Request.Path.ToLower();
-
-// if (path == "/api/config")
-// HandleApiConfig(args);
-// else if (path.StartsWith("/api/presetslist/"))
-// HandleApiPresetsList(args);
-// else if (path == "/api/presetslists")
-// HandleApiGetPresetsLists(args.Request, args.Response);
-// else
-// {
-// args.Response.Code = 404;
-// return;
-// }
-// args.Response.Header.SetHeaderValue("Access-Control-Allow-Origin", "*");
-// args.Response.Header.SetHeaderValue("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
-// }
-// catch (Exception e)
-// {
-// Debug.Console(1, "Uncaught HTTP server error: \n{0}", e);
-// args.Response.Code = 500;
-// }
-// }
-
-// ///
-// /// GET will return the running configuration. POST will attempt to take in a new config
-// /// and restart the program.
-// ///
-// void HandleApiConfig(OnHttpRequestArgs args)
-// {
-// var request = args.Request;
-// if (request.Header.RequestType == "GET")
-// {
-// if (File.Exists(ConfigPath))
-// {
-// Debug.Console(2, "Sending config:{0}", ConfigPath);
-// args.Response.Header.ContentType = EssentialsHttpServer.GetContentType(new FileInfo(ConfigPath).Extension);
-// args.Response.ContentStream = new FileStream(ConfigPath, FileMode.Open, FileAccess.Read);
-// }
-// }
-// else if (request.Header.RequestType == "POST")
-// {
-// Debug.Console(2, "Post type: '{0}'", request.Header.ContentType);
-
-// // Make sure we're receiving at least good json
-// Debug.Console(1, "Receving new config");
-// if (GetContentStringJson(args) == null)
-// return;
-
-// //---------------------------- try to move these into common method
-// // Move current file aside
-// var bakPath = ConfigPath + ".bak";
-// if (File.Exists(bakPath))
-// File.Delete(bakPath);
-// File.Move(ConfigPath, bakPath);
-
-// // Write the file
-// using (FileStream fs = File.Open(ConfigPath, FileMode.OpenOrCreate))
-// using (StreamWriter sw = new StreamWriter(fs))
-// {
-// try
-// {
-// sw.Write(args.Request.ContentString);
-// }
-// catch (Exception e)
-// {
-// string err = string.Format("Error writing received config file:\r{0}", e);
-// CrestronConsole.PrintLine(err);
-// ErrorLog.Warn(err);
-// // Put file back
-// File.Move(ConfigPath + ".bak", ConfigPath);
-// args.Response.Code = 500;
-// return;
-// }
-// }
-
-// // If client says "yeah, restart" and has a good token
-// // Restart program
-// string consoleResponse = null;
-// var restart = CrestronConsole.SendControlSystemCommand("progreset -p:" +
-// InitialParametersClass.ApplicationNumber, ref consoleResponse);
-// if (!restart) Debug.Console(0, "CAN'T DO THAT YO: {0}", consoleResponse);
-// }
-// }
-
-// void HandleApiPresetsList(OnHttpRequestArgs args)
-// {
-// var listPath = PresetsPathPrefix + args.Request.Path.Remove(0, 17);
-// Debug.Console(2, "Checking for preset list '{0}'", listPath);
-
-// if (args.Request.Header.RequestType == "GET")
-// {
-// if (File.Exists(listPath))
-// {
-// Debug.Console(2, "Sending presets file:{0}", listPath);
-// args.Response.Header.ContentType = EssentialsHttpServer.GetContentType(new FileInfo(listPath).Extension);
-// args.Response.ContentStream = new FileStream(listPath, FileMode.Open, FileAccess.Read);
-// }
-// }
-// else if (args.Request.Header.RequestType == "POST")
-// {
-// // Make sure we're receiving at least good json
-// Debug.Console(1, "Receving new presets");
-// if (GetContentStringJson(args) == null)
-// return;
-
-// //---------------------------- try to move these into common method
-// // Move current file aside
-// var bakPath = listPath + ".new";
-// Debug.Console(2, "Moving presets file to {0}", bakPath);
-// if(File.Exists(bakPath))
-// File.Delete(bakPath);
-// File.Move(listPath, bakPath);
-
-// Debug.Console(2, "Writing new file");
-// // Write the file
-// using (FileStream fs = File.OpenWrite(listPath))
-// using (StreamWriter sw = new StreamWriter(fs))
-// {
-// try
-// {
-// Debug.Console(2, "Writing {1}, {0} bytes", args.Request.ContentString.Length, listPath);
-// sw.Write(args.Request.ContentString);
-// }
-// catch (Exception e)
-// {
-// string err = string.Format("Error writing received presets file:\r{0}", e);
-// CrestronConsole.PrintLine(err);
-// ErrorLog.Warn(err);
-// // Put file back
-// File.Move(listPath + ".bak", listPath);
-// args.Response.Code = 500;
-// return;
-// }
-// }
-// }
-// }
-
-
-// void HandleApiGetPresetsLists(HttpServerRequest request, HttpServerResponse response)
-// {
-// if (request.Header.RequestType != "GET")
-// {
-// response.Code = 404; // This should be a 405 with an allow header
-// return;
-// }
-
-// if (Directory.Exists(PresetsPathPrefix))
-// {
-// //CrestronConsole.PrintLine("Parsing presets directory");
-// List files = Directory.GetFiles(PresetsPathPrefix, "*.json")
-// .ToList().Select(f => Path.GetFileName(f)).ToList();
-// if (files.Count > 0)
-// files.Sort();
-// var json = JsonConvert.SerializeObject(files);
-// response.Header.ContentType = "application/json";
-// response.ContentString = json;
-// }
-
-// // //CrestronConsole.PrintLine("Found {0} files", files.Count);
-// // JObject jo = new JObject();
-// // JArray ja = new JArray();
-
-// // foreach (var filename in files)
-// // {
-// // try
-// // {
-// // using (StreamReader sr = new StreamReader(filename))
-// // {
-// // JObject tempJo = JObject.Parse(sr.ReadToEnd());
-// // if (tempJo.Value("content").Equals("presetsList"))
-// // {
-// // var jItem = new JObject(); // make a new object
-// // jItem.Add("Name", tempJo["name"]);
-// // jItem.Add("File", filename);
-// // jItem.Add("Url", Uri.EscapeUriString(new Uri(
-// // filename.Replace("\\html", "")
-// // .Replace("\\HTML", "")
-// // .Replace('\\', '/'), UriKind.Relative).ToString()));
-// // ja.Add(jItem); // add to array
-// // }
-// // else
-// // CrestronConsole.PrintLine("Cannot use presets file '{0}'", filename);
-// // }
-// // }
-// // catch
-// // {
-// // // ignore failures - maybe delete them
-// // CrestronConsole.PrintLine("Unable to read presets file '{0}'", filename);
-// // }
-// // }
-// // jo.Add("PresetChannelLists", ja);
-// // //CrestronConsole.PrintLine(jo.ToString());
-// // response.Header.ContentType = "application/json";
-// // response.ContentString = jo.ToString();
-// //}
-// //else
-// // CrestronConsole.PrintLine("No presets files in directory");
-// }
-
-// ///
-// /// Simply does what it says
-// ///
-// JObject GetContentStringJson(OnHttpRequestArgs args)
-// {
-// //var content = args.Request.ContentString;
-// //Debug.Console(1, "{0}", content);
-
-// try
-// {
-// // just see if it parses properly
-// return JObject.Parse(args.Request.ContentString);
-// }
-// catch (Exception e)
-// {
-// string err = string.Format("JSON Error reading config file:\r{0}", e);
-// CrestronConsole.PrintLine(err);
-// ErrorLog.Warn(err);
-// args.Response.Code = 400; // Bad request
-// return null;
-// }
-// }
-// }
-//}
\ No newline at end of file
diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj
index 612bdf10..0bd07ecf 100644
--- a/PepperDashEssentials/PepperDashEssentials.csproj
+++ b/PepperDashEssentials/PepperDashEssentials.csproj
@@ -166,7 +166,7 @@
-
+
diff --git a/PepperDashEssentials/PluginLoading/PluginLoading.cs b/PepperDashEssentials/PluginLoading/PluginLoading.cs
new file mode 100644
index 00000000..032bea2e
--- /dev/null
+++ b/PepperDashEssentials/PluginLoading/PluginLoading.cs
@@ -0,0 +1,444 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Crestron.SimplSharp.Reflection;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+
+namespace PepperDash.Essentials
+{
+ ///
+ /// Deals with loading plugins at runtime
+ ///
+ public static class PluginLoader
+ {
+ ///
+ /// The complete list of loaded assemblies. Includes Essentials Framework assemblies and plugins
+ ///
+ public static List LoadedAssemblies { get; private set; }
+
+ ///
+ /// The list of assemblies loaded from the plugins folder
+ ///
+ static List LoadedPluginFolderAssemblies;
+
+ ///
+ /// The directory to look in for .cplz plugin packages
+ ///
+ static string _pluginDirectory = Global.FilePathPrefix + "plugins";
+
+ ///
+ /// The directory where plugins will be moved to and loaded from
+ ///
+ static string _loadedPluginsDirectoryPath = _pluginDirectory + Global.DirectorySeparator + "loadedAssemblies";
+
+ // The temp directory where .cplz archives will be unzipped to
+ static string _tempDirectory = _pluginDirectory + Global.DirectorySeparator + "temp";
+
+
+ static PluginLoader()
+ {
+ LoadedAssemblies = new List();
+ LoadedPluginFolderAssemblies = new List();
+ }
+
+ ///
+ /// Retrieves all the loaded assemblies from the program directory
+ ///
+ public static void AddProgramAssemblies()
+ {
+ Debug.Console(2, "Getting Assemblies loaded with Essentials");
+ // Get the loaded assembly filenames
+ var appDi = new DirectoryInfo(Global.ApplicationDirectoryPathPrefix);
+ var assemblyFiles = appDi.GetFiles("*.dll");
+
+ Debug.Console(2, "Found {0} Assemblies", assemblyFiles.Length);
+
+ foreach (var fi in assemblyFiles)
+ {
+ string version = string.Empty;
+ Assembly assembly = null;
+
+ switch (fi.Name)
+ {
+ case ("PepperDashEssentials.dll"):
+ {
+ version = Global.AssemblyVersion;
+ assembly = Assembly.GetExecutingAssembly();
+ break;
+ }
+ case ("PepperDash_Core.dll"):
+ {
+ version = PepperDash.Core.Debug.PepperDashCoreVersion;
+ break;
+ }
+ }
+
+ LoadedAssemblies.Add(new LoadedAssembly(fi.Name, version, assembly));
+ }
+
+ if (Debug.Level > 1)
+ {
+ Debug.Console(2, "Loaded Assemblies:");
+
+ foreach (var assembly in LoadedAssemblies)
+ {
+ Debug.Console(2, "Assembly: {0}", assembly.Name);
+ }
+ }
+ }
+
+ ///
+ /// Loads an assembly via Reflection and adds it to the list of loaded assemblies
+ ///
+ ///
+ static LoadedAssembly LoadAssembly(string filePath)
+ {
+ Debug.Console(2, "Attempting to load {0}", filePath);
+ var assembly = Assembly.LoadFrom(filePath);
+ if (assembly != null)
+ {
+ var assyVersion = GetAssemblyVersion(assembly);
+
+ var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly);
+ LoadedAssemblies.Add(loadedAssembly);
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loaded assembly '{0}', version {1}", loadedAssembly.Name, loadedAssembly.Version);
+ return loadedAssembly;
+ }
+ else
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Unable to load assembly: '{0}'", filePath);
+ }
+
+ return null;
+
+ }
+
+ ///
+ /// Attempts to get the assembly informational version and if not possible gets the version
+ ///
+ ///
+ ///
+ static string GetAssemblyVersion(Assembly assembly)
+ {
+ var ver = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
+ if (ver != null && ver.Length > 0)
+ {
+ // Get the AssemblyInformationalVersion
+ AssemblyInformationalVersionAttribute verAttribute = ver[0] as AssemblyInformationalVersionAttribute;
+ return verAttribute.InformationalVersion;
+ }
+ else
+ {
+ // Get the AssemblyVersion
+ var version = assembly.GetName().Version;
+ var verStr = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
+ return verStr;
+ }
+ }
+
+ ///
+ /// Checks if the filename matches an already loaded assembly file's name
+ ///
+ ///
+ /// True if file already matches loaded assembly file.
+ public static bool CheckIfAssemblyLoaded(string name)
+ {
+ Debug.Console(2, "Checking if assembly: {0} is loaded...", name);
+ var loadedAssembly = LoadedAssemblies.FirstOrDefault(s => s.Name.Equals(name));
+
+ if (loadedAssembly != null)
+ {
+ Debug.Console(2, "Assembly already loaded.");
+ return true;
+ }
+ else
+ {
+ Debug.Console(2, "Assembly not loaded.");
+ return false;
+ }
+ }
+
+ ///
+ /// Used by console command to report the currently loaded assemblies and versions
+ ///
+ ///
+ public static void ReportAssemblyVersions(string command)
+ {
+ Debug.Console(0, "Loaded Assemblies:");
+ foreach (var assembly in LoadedAssemblies)
+ {
+ Debug.Console(0, "{0} Version: {1}", assembly.Name, assembly.Version);
+ }
+ }
+
+ ///
+ /// Moves any .dll assemblies not already loaded from the plugins folder to loadedPlugins folder
+ ///
+ static void MoveDllAssemblies()
+ {
+ Debug.Console(0, "Looking for .dll assemblies from plugins folder...");
+
+ var pluginDi = new DirectoryInfo(_pluginDirectory);
+ var pluginFiles = pluginDi.GetFiles("*.dll");
+
+ if (pluginFiles.Length > 0)
+ {
+ if (!Directory.Exists(_loadedPluginsDirectoryPath))
+ {
+ Directory.CreateDirectory(_loadedPluginsDirectoryPath);
+ }
+ }
+
+ foreach (var pluginFile in pluginFiles)
+ {
+ try
+ {
+ Debug.Console(0, "Found .dll: {0}", pluginFile.Name);
+
+ if (!CheckIfAssemblyLoaded(pluginFile.Name))
+ {
+ string filePath = string.Empty;
+
+ filePath = _loadedPluginsDirectoryPath + Global.DirectorySeparator + pluginFile.Name;
+
+ // Check if there is a previous file in the loadedPlugins directory and delete
+ if (File.Exists(filePath))
+ {
+ Debug.Console(0, "Found existing file in loadedPlugins: {0} Deleting and moving new file to replace it", filePath);
+ File.Delete(filePath);
+ }
+
+ // Move the file
+ File.Move(pluginFile.FullName, filePath);
+ Debug.Console(2, "Moved {0} to {1}", pluginFile.FullName, filePath);
+ }
+ else
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Skipping assembly: {0}. There is already an assembly with that name loaded.", pluginFile.FullName);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(2, "Error with plugin file {0} . Exception: {1}", pluginFile.FullName, e);
+ continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here
+ }
+ }
+
+ Debug.Console(0, "Done with .dll assemblies");
+ }
+
+ ///
+ /// Unzips each .cplz archive into the temp directory and moves any unloaded files into loadedPlugins
+ ///
+ static void UnzipAndMoveCplzArchives()
+ {
+ Debug.Console(0, "Looking for .cplz archives from plugins folder...");
+ var di = new DirectoryInfo(_pluginDirectory);
+ var zFiles = di.GetFiles("*.cplz");
+
+ if (zFiles.Length > 0)
+ {
+ if (!Directory.Exists(_loadedPluginsDirectoryPath))
+ {
+ Directory.CreateDirectory(_loadedPluginsDirectoryPath);
+ }
+ }
+
+ foreach (var zfi in zFiles)
+ {
+ Directory.CreateDirectory(_tempDirectory);
+ var tempDi = new DirectoryInfo(_tempDirectory);
+
+ Debug.Console(0, "Found cplz: {0}. Unzipping into temp plugins directory", zfi.Name);
+ var result = CrestronZIP.Unzip(zfi.FullName, tempDi.FullName);
+ Debug.Console(0, "UnZip Result: {0}", result.ToString());
+
+ var tempFiles = tempDi.GetFiles("*.dll");
+ foreach (var tempFile in tempFiles)
+ {
+ try
+ {
+ if (!CheckIfAssemblyLoaded(tempFile.Name))
+ {
+ string filePath = string.Empty;
+
+ filePath = _loadedPluginsDirectoryPath + Global.DirectorySeparator + tempFile.Name;
+
+ // Check if there is a previous file in the loadedPlugins directory and delete
+ if (File.Exists(filePath))
+ {
+ Debug.Console(0, "Found existing file in loadedPlugins: {0} Deleting and moving new file to replace it", filePath);
+ File.Delete(filePath);
+ }
+
+ // Move the file
+ File.Move(tempFile.FullName, filePath);
+ Debug.Console(2, "Moved {0} to {1}", tempFile.FullName, filePath);
+ }
+ else
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Skipping assembly: {0}. There is already an assembly with that name loaded.", tempFile.FullName);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(2, "Assembly {0} is not a custom assembly. Exception: {1}", tempFile.FullName, e);
+ continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here
+ }
+ }
+
+ // Delete the .cplz and the temp directory
+ Directory.Delete(_tempDirectory, true);
+ zfi.Delete();
+ }
+
+ Debug.Console(0, "Done with .cplz archives");
+ }
+
+ ///
+ /// Attempts to load the assemblies from the loadedPlugins folder
+ ///
+ static void LoadPluginAssemblies()
+ {
+ Debug.Console(0, "Loading assemblies from loadedPlugins folder...");
+ var pluginDi = new DirectoryInfo(_loadedPluginsDirectoryPath);
+ var pluginFiles = pluginDi.GetFiles("*.dll");
+
+ Debug.Console(2, "Found {0} plugin assemblies to load", pluginFiles.Length);
+
+ foreach (var pluginFile in pluginFiles)
+ {
+ var loadedAssembly = LoadAssembly(pluginFile.FullName);
+
+ LoadedPluginFolderAssemblies.Add(loadedAssembly);
+ }
+
+ Debug.Console(0, "All Plugins Loaded.");
+ }
+
+ ///
+ /// Iterate the loaded assemblies and try to call the LoadPlugin method
+ ///
+ static void LoadCustomPluginTypes()
+ {
+ Debug.Console(0, "Loading Custom Plugin Types...");
+ foreach (var loadedAssembly in LoadedPluginFolderAssemblies)
+ {
+ // iteratate this assembly's classes, looking for "LoadPlugin()" methods
+ try
+ {
+ var assy = loadedAssembly.Assembly;
+ var types = assy.GetTypes();
+ foreach (var type in types)
+ {
+ try
+ {
+ var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static);
+ var loadPlugin = methods.FirstOrDefault(m => m.Name.Equals("LoadPlugin"));
+ if (loadPlugin != null)
+ {
+ Debug.Console(2, "LoadPlugin method found in {0}", type.Name);
+
+ var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static);
+
+ var minimumVersion = fields.FirstOrDefault(p => p.Name.Equals("MinimumEssentialsFrameworkVersion"));
+ if (minimumVersion != null)
+ {
+ Debug.Console(2, "MinimumEssentialsFrameworkVersion found");
+
+ var minimumVersionString = minimumVersion.GetValue(null) as string;
+
+ if (!string.IsNullOrEmpty(minimumVersionString))
+ {
+ var passed = Global.IsRunningMinimumVersionOrHigher(minimumVersionString);
+
+ if (!passed)
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Error, "Plugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin", minimumVersionString);
+ continue;
+ }
+ else
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Passed plugin passed dependency check (required version {0})", minimumVersionString);
+ }
+ }
+ else
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Warning, "MinimumEssentialsFrameworkVersion found but not set. Loading plugin, but your mileage may vary.");
+ }
+ }
+ else
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Warning, "MinimumEssentialsFrameworkVersion not found. Loading plugin, but your mileage may vary.");
+ }
+
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Adding plugin: {0}", loadedAssembly.Name);
+ loadPlugin.Invoke(null, null);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(2, "Load Plugin not found. {0} is not a plugin assembly. Exception: {1}", loadedAssembly.Name, e);
+ continue;
+ }
+
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(2, "Error Loading Assembly: {0} Exception: (1) ", loadedAssembly.Name, e);
+ continue;
+ }
+ }
+ // plugin dll will be loaded. Any classes in plugin should have a static constructor
+ // that registers that class with the Core.DeviceFactory
+ Debug.Console(0, "Done Loading Custom Plugin Types.");
+ }
+
+ ///
+ /// Loads plugins
+ ///
+ public static void LoadPlugins()
+ {
+ if (Directory.Exists(_pluginDirectory))
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "Plugins directory found, checking for plugins");
+
+ // Deal with any .dll files
+ MoveDllAssemblies();
+
+ // Deal with any .cplz files
+ UnzipAndMoveCplzArchives();
+
+ // Load the assemblies from the loadedPlugins folder into the AppDomain
+ LoadPluginAssemblies();
+
+ // Load the types from any custom plugin assemblies
+ LoadCustomPluginTypes();
+ }
+ }
+
+ }
+
+ ///
+ /// Represents an assembly loaded at runtime and it's associated metadata
+ ///
+ public class LoadedAssembly
+ {
+ public string Name { get; private set; }
+ public string Version { get; private set; }
+ public Assembly Assembly { get; private set; }
+
+ public LoadedAssembly(string name, string version, Assembly assembly)
+ {
+ Name = name;
+ Version = version;
+ Assembly = assembly;
+ }
+ }
+}
\ No newline at end of file
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs
index 0a288944..b3fc7d89 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs
@@ -64,16 +64,23 @@ namespace PepperDash.Essentials.Core.Config
if (configFiles != null)
{
+ Debug.Console(2, "{0} config files found matching pattern", configFiles.Length);
+
if (configFiles.Length > 1)
{
Debug.Console(0, Debug.ErrorLogLevel.Error,
"****Error: Multiple Portal Configuration files present. Please ensure only a single file exists and reset program.****");
return false;
}
- else
+ else if (configFiles.Length == 1)
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Found Portal config file: '{0}'", filePath);
}
+ else
+ {
+ Debug.Console(0, Debug.ErrorLogLevel.Notice, "No config file found.");
+ return false;
+ }
}
else
{
diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs
index 4ef68066..de533ebb 100644
--- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs
+++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs
@@ -98,7 +98,7 @@ namespace PepperDash.Essentials.Core
{
Debug.Console(2, "Comparing running version '{0}' to minimum version '{1}'", AssemblyVersion, minimumVersion);
- var runtimeVersion = Regex.Match(AssemblyVersion, @"^(\d*).(\d*).(\d*)$");
+ var runtimeVersion = Regex.Match(AssemblyVersion, @"^(\d*).(\d*).(\d*).*");
var runtimeVersionMajor = Int16.Parse(runtimeVersion.Groups[1].Value);
var runtimeVersionMinor = Int16.Parse(runtimeVersion.Groups[2].Value);
@@ -107,7 +107,7 @@ namespace PepperDash.Essentials.Core
// Check for beta build version
if (runtimeVersionMajor == 0)
{
- Debug.Console(2, "Running Beta Build. Bypassing Dependency Check.");
+ Debug.Console(2, "Running Local Build. Bypassing Dependency Check.");
return true;
}
diff --git a/essentials-framework/pepperdashcore-builds b/essentials-framework/pepperdashcore-builds
index acebe6b4..15206840 160000
--- a/essentials-framework/pepperdashcore-builds
+++ b/essentials-framework/pepperdashcore-builds
@@ -1 +1 @@
-Subproject commit acebe6b43b28cc3a93f899e9714292a0cc1ab2cc
+Subproject commit 15206840b3e6338f695e4ffba634a72e51ea1be5