/// <summary> /// Each channel is run in a different thread to prevent interference with other channels. /// </summary> void _ThreadLoop() { Action action; while (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !_Closing) { while (_Actions.Count > 0) { lock (_Actions) { action = _Actions.Dequeue(); } try { action(); } catch (Exception ex) { _LastError = ICEController.WriteICEEventError("A queued action '" + action.Method.Name + "' for channel '" + Name + " (" + _GUID + ")' has caused an exception error.", ex); } } Thread.Sleep(0); } }
public void _OnStart() { if (!_CheckInitialized(false)) { _Timer.Dispose(); } if (PluginState == PluginState.Ready || PluginState == PluginState.Paused || PluginState == PluginState.Stopped) { var typeTitle = TypeTitle; try { ICEController.WriteICEEventInfo("(" + typeTitle + ") Starting '" + Name + "' ..."); _Plugin.OnStart(); PluginState = PluginState.Started; ICEController.WriteICEEventInfo("(" + typeTitle + ") '" + Name + "' started."); } catch (Exception ex) { SetErrorMessage(ex, "Error starting ", typeTitle.ToLower(), " '", Name, "' on channel '", Channel.Name, "'."); } } }
/// <summary> /// Returns a list of valid plugin types from the specified assembly. /// </summary> /// <param name="assembly">The assembly to search.</param> /// <param name="suppressErrors">If true, then any exception errors are ignored, and 'null' is returned instead. Any errors are still recorded in the /// event log however.</param> public static Type[] GetICEPluginTypes(this Assembly assembly, bool suppressErrors = true) { if (assembly == null) { throw new ArgumentNullException("assembly"); } try { List <Type> appList = new List <Type>(); var types = assembly.GetTypes(); foreach (var type in types) { if (type.IsClass && !type.IsAbstract && typeof(IPlugin).IsAssignableFrom(type)) { appList.Add(type); } } return(appList.ToArray()); } catch (Exception ex) { var _ex = ICEController.WriteICEEventError("Failed to retrieve plugin types from assembly '" + assembly.FullName + "'.", ex); if (!suppressErrors) { throw _ex; } return(new Type[0]); // (error not desired, so return an empty array) } }
public static bool AddAssembly(string filename, bool throwErrors = false) { if (!File.Exists(filename)) { var _ex = ICEController.WriteICEEventError("The plugin library (Assembly) file '" + filename + "' does not exist."); if (throwErrors) { throw _ex; } return(false); } Assembly assembly; try { assembly = Assembly.ReflectionOnlyLoadFrom(filename); } catch (Exception ex) { var _ex = ICEController.WriteICEEventError("The plugin library (Assembly) file '" + filename + "' is invalid or cannot be loaded for some reason.", ex); if (throwErrors) { throw _ex; } return(false); } return(AddAssembly(assembly, throwErrors)); }
public bool _OnRun() { _CheckInitialized(); var runNext = true; if (PluginState == PluginState.Started || PluginState == PluginState.Paused) { var typeTitle = TypeTitle; try { ICEController.WriteICEEventInfo("(" + typeTitle + ") Running '" + Name + "' ..."); runNext = _Plugin.OnRun(); ICEController.WriteICEEventInfo("(" + typeTitle + ") '" + Name + "' completed."); } catch (Exception ex) { SetErrorMessage(ex, "Error running ", typeTitle.ToLower(), " '", Name, "' on channel '", Channel.Name, "'."); runNext = false; } } return(runNext); }
/// <summary> /// Attempts to load the assembly for this plugin library. Returns true on success, and false otherwise. /// </summary> /// <param name="throwErrors">If true, then an exception is thrown instead of returning false.</param> public bool Load(bool throwErrors = false) { if (_Assembly == null || _Assembly.ReflectionOnly) { if (string.IsNullOrEmpty(_Filename)) { var _ex = ICEController.WriteICEEventError("Cannot load this library: No valid filename was given."); if (throwErrors) { throw _ex; } return(false); } string libFileToLoad = AppDomain.CurrentDomain.BaseDirectory + _Filename; ICEController.WriteICEEventInfo("Loading library '" + libFileToLoad + "'..."); try { _Assembly = Assembly.LoadFile(libFileToLoad); } catch (Exception ex) { var _ex = ICEController.WriteICEEventError("An error occurred trying to load plugin '" + libFileToLoad + "'.", ex); if (throwErrors) { throw _ex; } return(false); } } return(IsLoaded); }
// ------------------------------------------------------------------------------------------------------- public Channels(ICEController controller) { if (controller == null) { throw new ArgumentNullException("controller"); } Controller = controller; }
// ------------------------------------------------------------------------------------------------------- public Channel CreateChannel(string name, string guid = null) { ICEController.WriteICEEventInfo("Creating channel '" + name + "'..."); Channel channel = new Channel(this, name, guid); _Channels.Add(channel); return(channel); }
/// <summary> /// Initializes the plugin. This is where a plugin's constructor logic should go. /// </summary> /// <param name="controller">A reference to an ICE controller instance.</param> /// <param name="pluginWrapper">A reference to the plugin controller (or the plugin itself for derived types), which contains other methods specific to plugins (such as setting up default name=value properties).</param> /// <returns>True if initialization was successful.</returns> public virtual void Initialize(ICEController controller, IPluginController pluginWrapper) { if (_Plugin != null && _Plugin != this) { _Plugin.Initialize(controller, pluginWrapper); } throw new NotImplementedException("You must implement an 'IPlugin.Initialize()' method in your own type, or override the existing one for derived types."); }
// ------------------------------------------------------------------------------------------------------- /// <summary> /// Creates and returns a plugin instance to run on the channel. /// </summary> /// <param name="name">The case-sensitive type name, or full type name (i.e. [Namespace].[Type]), of the plugin to create. Full type names are /// searched first before type-only names.</param> public Plugin <IPlugin> CreateInstance(string name, string guid) { ICEController.WriteICEEventInfo("Creating instance for plugin '" + name + "' on channel '" + Name + "'..."); Plugin <IPlugin> plugin = PluginManager.CreateInstance(this, name, guid); if (plugin != null) { _PluginInstances.Add(plugin); } return(plugin); }
/// <summary> /// Initializes the controller singleton instance, and hooks into 'AppDomain.CurrentDomain.UnhandledException' event for error reporting. /// The 'Instance' property is not valid until this is called. /// </summary> public static ICEController Initialize() { if (_Instance == null) { WriteICEEventInfo("Initializing ICE controller ..."); _Instance = new ICEController(); AppDomain.CurrentDomain.UnhandledException += (_s, _e) => { WriteICEEventError("A non-functional exception error has occurred" + (_e.IsTerminating ? " while terminating." : ".") + " This is usually due to developer or configuration related errors. Please see inner exception information for more details.", _e.ExceptionObject); }; WriteICEEventInfo("ICE controller initialized."); } return(_Instance); }
// ------------------------------------------------------------------------------------------------------- /// <summary> /// Creates and returns a plugin instance to run on a given channel. /// </summary> /// <param name="channel">The channel instance to create the plugin in (required).</param> /// <param name="name">The case-sensitive type name, or full type name (i.e. [Namespace].[Type]), of the plugin to create. Full type names are /// searched first before type-only names.</param> /// <param name="guid">A globally unique ID for this plugin instance (system wide, across all channels). If null or empty, a new GUID will be created automatically.</param> public Plugin <IPlugin> CreateInstance(Channel channel, string instanceName, string guid = null) { if (channel == null) { throw new ArgumentNullException("A channel reference is required for plugin instances."); } if (string.IsNullOrEmpty(guid)) { guid = Guid.NewGuid().ToString("N"); } if (channel.GetPluginInstance(instanceName) != null) { throw new InvalidOperationException("There is already an existing plugin instance with the name '" + instanceName + "'."); } Plugin <IPlugin> pluginController = null; try { ICEController.WriteICEEventInfo("Creating instance for plugin '" + PluginType.FullName + "' on channel '" + channel.Name + "'..."); var pluginInstance = PluginType.Assembly.CreateInstance(PluginType.FullName) as IPlugin; pluginController = pluginInstance as Plugin <IPlugin>; if (pluginInstance != null && pluginController == null) { // ... this plugin instance does not derive from the plugin<T> controller class, so we need to wrap it in one ... pluginController = new Plugin <IPlugin>(); pluginController._Plugin = pluginInstance; } if (pluginController != null) { pluginController._Configure(channel, Library, instanceName, pluginController.ActualPlugin, guid); } } catch (Exception ex) { throw ICEController.WriteICEEventError("Error creating plugin type '" + PluginType.FullName + "'.", ex); } return(pluginController); }
public void _OnClosing() { _CheckInitialized(); var typeTitle = TypeTitle; try { ICEController.WriteICEEventInfo("(" + typeTitle + ") Closing '" + Name + "' ..."); _Plugin.OnClosing(); PluginState = PluginState.Uninitialized; ICEController.WriteICEEventInfo("(" + typeTitle + ") '" + Name + "' ready for termination."); } catch (Exception ex) { SetErrorMessage(ex, "Error closing ", typeTitle.ToLower(), " '", Name, "' on channel '", Channel.Name, "'."); } }
public void _OnTick() { _CheckInitialized(); if (PluginState == PluginState.Started) { var typeTitle = TypeTitle; try { ICEController.WriteICEEventInfo("(" + typeTitle + ") Timer elapsed for '" + Name + "' ..."); _Plugin.OnTick(); ICEController.WriteICEEventInfo("(" + typeTitle + ") '" + Name + "' completed."); } catch (Exception ex) { SetErrorMessage(ex, "Error in timer method for ", typeTitle.ToLower(), " '", Name, "' on channel '", Channel.Name, "'."); } } }
public virtual void Stop() { _CheckInitialized(); if (_PluginState == PluginState.Started || _PluginState == PluginState.Paused) { var typeTitle = TypeTitle; try { ICEController.WriteICEEventInfo("(" + typeTitle + ") Stopping '" + _Name + "' ..."); _Plugin.OnStop(); _PluginState = PluginState.Stopped; ICEController.WriteICEEventInfo("(" + typeTitle + ") '" + _Name + "' stopped."); } catch (Exception ex) { SetErrorMessage(ex, "Error stopping ", typeTitle.ToLower(), " '", _Name, "' on channel '", _Channel.Name, "'."); } } }
public void _OnPause() { _CheckInitialized(); if (PluginState == PluginState.Started) { var typeTitle = TypeTitle; try { ICEController.WriteICEEventInfo("(" + typeTitle + ") Pausing '" + Name + "' ..."); _Plugin.OnPause(); PluginState = PluginState.Paused; ICEController.WriteICEEventInfo("(" + typeTitle + ") '" + Name + "' paused."); } catch (Exception ex) { SetErrorMessage(ex, "Error pausing ", typeTitle.ToLower(), " '", Name, "' on channel '", Channel.Name, "'."); } } }
// ------------------------------------------------------------------------------------------------------- public void Initialize() { if (!IsInitialized) { var typeTitle = TypeTitle; try { ICEController.WriteICEEventInfo("(" + typeTitle + ") '" + Name + "' startup properties:", GetNameValues(), "Initializing ..."); LoadData(); _Plugin.Initialize(Channel.Controller, this); PluginState = PluginState.Ready; ICEController.WriteICEEventInfo("(" + typeTitle + ") '" + Name + "' initialized.", "Properties after initialization:", GetNameValues()); } catch (Exception ex) { SetErrorMessage(ex, "Error initializing ", typeTitle.ToLower(), " '", Name, "' on channel '", Channel.Name, "'."); } } }
/// <summary> /// Adds an assembly to the assembly list, returning 'false' if any errors occur, or if the assembly was already added. /// <para>Warning: This method recursively traverses the assembly types, and all dependent types, in real-time, and will add any other associated /// applications, components, and controls also.</para> /// </summary> /// <param name="assembly">The assembly to add, which represents a library of plugin types.</param> /// <param name="throwErrors">If true, any errors will throw an exception instead of returning false.</param> public static bool AddAssembly(Assembly assembly, bool throwErrors = false) { if (GetLibrary(assembly) == null) { try { if (assembly.GetICEPluginTypes(true).Length == 0) { return(false); // (there are no valid plugins types in this assembly) } var library = AddLibrary(new Library(assembly)); var associatedAssemblies = assembly.GetAssociatedAssemblies(); // (get a list of assemblies other than the given 'assembly') if (associatedAssemblies != null) { foreach (var asm in associatedAssemblies) { AddAssembly(asm, throwErrors); } } } catch (Exception ex) { var _ex = ICEController.WriteICEEventError("Failed to read types in plugin assembly '" + assembly.FullName + "' (a DLL may be missing).", ex); if (throwErrors) { throw _ex; } return(false); } return(true); } return(false); }
/// <summary> /// Joins all given parameters into one error message. /// </summary> /// <param name="exception">An optional exception object, or 'null' if none.</param> /// <param name="strings">The strings to use as a single error message.</param> public void SetErrorMessage(Exception innerException, params string[] strings) { _Error = ICEController.WriteICEEventError(strings.Length > 0 ? String.Join("", strings) : "An ICE plugin error has occurred.", innerException); }
/// <summary> /// Joins all given parameters into one error message. /// </summary> /// <param name="strings">The strings to use as a single error message.</param> public void SetErrorMessage(params string[] strings) { _Error = ICEController.WriteICEEventError(strings.Length > 0 ? String.Join("", strings) : "An unspecified ICE plugin error has occurred."); }