using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; namespace RGB.NET.Core; /// /// Represents the abstract base implementation for a . /// public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider { #region Properties & Fields private readonly double _defaultUpdateRateHardLimit; /// public bool IsInitialized { get; protected set; } /// public bool ThrowsExceptions { get; protected set; } /// public virtual IEnumerable Devices { get; protected set; } = Enumerable.Empty(); /// /// Gets the dictionary containing the registered update triggers. /// Normally should be used to access them. /// protected Dictionary UpdateTriggerMapping { get; } = new(); /// public IReadOnlyList<(int id, IDeviceUpdateTrigger trigger)> UpdateTriggers => new ReadOnlyCollection<(int id, IDeviceUpdateTrigger trigger)>(UpdateTriggerMapping.Select(x => (x.Key, x.Value)).ToList()); #endregion #region Events /// public event EventHandler? Exception; #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The update rate hard limit all update triggers for this device provider are initialized with. protected AbstractRGBDeviceProvider(double defaultUpdateRateHardLimit = 0) { this._defaultUpdateRateHardLimit = defaultUpdateRateHardLimit; } #endregion #region Methods /// public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false) { ThrowsExceptions = throwExceptions; try { Reset(); InitializeSDK(); Devices = new ReadOnlyCollection(GetLoadedDevices(loadFilter).ToList()); foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values) updateTrigger.Start(); IsInitialized = true; } catch (DeviceProviderException) { Reset(); throw; } catch (Exception ex) { Reset(); Throw(ex, true); return false; } return true; } /// /// Loads devices and returns a filtered list of them. /// /// /// The underlying loading of the devices happens in . /// /// -flags to filter the device with. /// The filtered collection of loaded devices. protected virtual IEnumerable GetLoadedDevices(RGBDeviceType loadFilter) { List devices = new(); foreach (IRGBDevice device in LoadDevices()) { try { if (loadFilter.HasFlag(device.DeviceInfo.DeviceType)) devices.Add(device); else device.Dispose(); } catch (Exception ex) { Throw(ex); } } return devices; } /// /// Initializes the underlying SDK. /// protected abstract void InitializeSDK(); /// /// Loads all devices this device provider is capable of loading. /// /// /// Filtering happens in . /// /// A collection of loaded devices. protected abstract IEnumerable LoadDevices(); /// /// Gets the mapped to the specified id or a new one if the id wasn't requested before. /// /// /// The creation of the update trigger happens in . /// /// The id of the update trigger. /// The update rate hard limit to be set in the update trigger. /// The update trigger mapped to the specified id. protected virtual IDeviceUpdateTrigger GetUpdateTrigger(int id = -1, double? updateRateHardLimit = null) { if (!UpdateTriggerMapping.TryGetValue(id, out IDeviceUpdateTrigger? updaeTrigger)) UpdateTriggerMapping[id] = (updaeTrigger = CreateUpdateTrigger(id, updateRateHardLimit ?? _defaultUpdateRateHardLimit)); return updaeTrigger; } /// /// Creates a update trigger with the specified id and the specified update rate hard limit. /// /// The id of the update trigger. /// The update rate hard limit tobe set in the update trigger. /// The newly created update trigger. protected virtual IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new DeviceUpdateTrigger(updateRateHardLimit); /// /// Resets the device provider and disposes all devices and update triggers. /// protected virtual void Reset() { foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values) updateTrigger.Dispose(); foreach (IRGBDevice device in Devices) device.Dispose(); Devices = Enumerable.Empty(); UpdateTriggerMapping.Clear(); IsInitialized = false; } /// /// Triggers the -event and throws the specified exception if is true and it is not overriden in the event. /// /// The exception to throw. /// Indicates if the exception is critical for device provider to work correctly. protected virtual void Throw(Exception ex, bool isCritical = false) { ExceptionEventArgs args = new(ex, isCritical, ThrowsExceptions); try { OnException(args); } catch { /* we don't want to throw due to bad event handlers */ } if (args.Throw) throw new DeviceProviderException(ex, isCritical); } /// /// Throws the event. /// /// The parameters passed to the event. protected virtual void OnException(ExceptionEventArgs args) => Exception?.Invoke(this, args); /// public virtual void Dispose() { Reset(); GC.SuppressFinalize(this); } #endregion }