Added a layer of abstraction between RGB.NET and Artemis

This means Artemis can add extra functionality
It also avoid having to reference RGB.NET for basic rendering
Implemented rendering scaling
This commit is contained in:
SpoinkyNL committed 2019-10-30 23:20:06 +01:00
1 parent 4126f5622e
commit f72f145c7e
41 files changed
+725 -494

No files matched your search

+4 -4
View File
@@ -52,11 +52,11 @@ namespace Artemis.Core.Services
await Task.Run(() => _pluginService.CopyBuiltInPlugins());
await Task.Run(() => _pluginService.LoadPlugins());
var surfaceConfig = _surfaceService.ActiveSurfaceConfiguration;
var surfaceConfig = _surfaceService.ActiveSurface;
if (surfaceConfig != null)
_logger.Information("Initialized with active surface configuration {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.Guid);
_logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.Guid);
else
_logger.Information("Initialized without an active surface configuration");
_logger.Information("Initialized without an active surface entity");
OnInitialized();
}
@@ -84,7 +84,7 @@ namespace Artemis.Core.Services
g.Clear(Color.Black);
foreach (var module in modules)
module.Render(args.DeltaTime, _rgbService.Surface, g);
module.Render(args.DeltaTime, _surfaceService.ActiveSurface, g);
}
OnFrameRendered(new FrameEventArgs(modules, _rgbService.GraphicsDecorator.GetBitmap(), args.DeltaTime, _rgbService.Surface));
@@ -1,5 +1,8 @@
namespace Artemis.Core.Services.Interfaces
{
/// <summary>
/// A singleton service
/// </summary>
public interface IArtemisService
{
}
@@ -0,0 +1,9 @@
namespace Artemis.Core.Services.Interfaces
{
/// <summary>
/// A singleton service that cannot be used by plugins
/// </summary>
public interface IProtectedArtemisService
{
}
}
+1 -1
View File
@@ -102,7 +102,7 @@ namespace Artemis.Core.Services
// Create a child kernel and app domain that will only contain the plugins
_childKernel = new ChildKernel(_kernel);
// Load the plugin assemblies into the plugin context
var pluginDirectory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins"));
foreach (var subDirectory in pluginDirectory.EnumerateDirectories())
+22 -25
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Artemis.Core.Events;
using Artemis.Core.Plugins.Models;
using Artemis.Core.RGB.NET;
using Artemis.Core.Services.Interfaces;
using Artemis.Core.Services.Storage;
@@ -20,15 +21,18 @@ namespace Artemis.Core.Services
private readonly ILogger _logger;
private readonly TimerUpdateTrigger _updateTrigger;
private ListLedGroup _background;
private PluginSetting<double> _renderScaleSetting;
internal RgbService(ILogger logger)
internal RgbService(ILogger logger, ISettingsService settingsService)
{
_logger = logger;
_renderScaleSetting = settingsService.GetSetting("RenderScale", 1.0);
Surface = RGBSurface.Instance;
// Let's throw these for now
Surface.Exception += SurfaceOnException;
_renderScaleSetting.SettingChanged += RenderScaleSettingOnSettingChanged;
_loadedDevices = new List<IRGBDevice>();
_updateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / 25};
Surface.RegisterUpdateTrigger(_updateTrigger);
@@ -39,16 +43,7 @@ namespace Artemis.Core.Services
public GraphicsDecorator GraphicsDecorator { get; private set; }
public IReadOnlyCollection<IRGBDevice> LoadedDevices
{
get
{
lock (_loadedDevices)
{
return _loadedDevices.AsReadOnly();
}
}
}
public IReadOnlyCollection<IRGBDevice> LoadedDevices => _loadedDevices.AsReadOnly();
public void AddDeviceProvider(IRGBDeviceProvider deviceProvider)
{
@@ -56,23 +51,20 @@ namespace Artemis.Core.Services
if (deviceProvider.Devices == null)
{
_logger.Warning("Device provider {deviceProvider} has no devices", deviceProvider.GetType().Name);
_logger.Warning("RgbDevice provider {deviceProvider} has no devices", deviceProvider.GetType().Name);
return;
}
lock (_loadedDevices)
foreach (var surfaceDevice in deviceProvider.Devices)
{
foreach (var surfaceDevice in deviceProvider.Devices)
if (!_loadedDevices.Contains(surfaceDevice))
{
if (!_loadedDevices.Contains(surfaceDevice))
{
_loadedDevices.Add(surfaceDevice);
OnDeviceLoaded(new DeviceEventArgs(surfaceDevice));
}
else
{
OnDeviceReloaded(new DeviceEventArgs(surfaceDevice));
}
_loadedDevices.Add(surfaceDevice);
OnDeviceLoaded(new DeviceEventArgs(surfaceDevice));
}
else
{
OnDeviceReloaded(new DeviceEventArgs(surfaceDevice));
}
}
}
@@ -85,6 +77,11 @@ namespace Artemis.Core.Services
Surface.Dispose();
}
private void RenderScaleSettingOnSettingChanged(object sender, EventArgs e)
{
UpdateGraphicsDecorator();
}
private void SurfaceOnException(ExceptionEventArgs args)
{
throw args.Exception;
@@ -108,7 +105,7 @@ namespace Artemis.Core.Services
// Apply the application wide brush and decorator
_background = new ListLedGroup(Surface.Leds) {Brush = new SolidColorBrush(new Color(255, 255, 255, 255))};
GraphicsDecorator = new GraphicsDecorator(_background, 0.25);
GraphicsDecorator = new GraphicsDecorator(_background, _renderScaleSetting.Value);
_background.Brush.RemoveAllDecorators();
_background.Brush.AddDecorator(GraphicsDecorator);
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities;
using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json;
namespace Artemis.Core.Services
{
/// <inheritdoc />
public class SettingsService : ISettingsService
{
private readonly PluginInfo _buildInPluginInfo;
private readonly IPluginSettingRepository _pluginSettingRepository;
private readonly Dictionary<string, PluginSettingEntity> _settingEntities;
internal SettingsService(IPluginSettingRepository pluginSettingRepository)
{
_pluginSettingRepository = pluginSettingRepository;
_buildInPluginInfo = new PluginInfo {Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core"};
_settingEntities = pluginSettingRepository.GetByPluginGuid(_buildInPluginInfo.Guid).ToDictionary(se => se.Name);
}
public PluginSetting<T> GetSetting<T>(string name, T defaultValue = default(T))
{
lock (_settingEntities)
{
if (_settingEntities.ContainsKey(name))
return new PluginSetting<T>(_buildInPluginInfo, _pluginSettingRepository, _settingEntities[name]);
var settingEntity = new PluginSettingEntity {Name = name, PluginGuid = _buildInPluginInfo.Guid, Value = JsonConvert.SerializeObject(defaultValue)};
_pluginSettingRepository.Add(settingEntity);
_pluginSettingRepository.Save();
_settingEntities.Add(name, settingEntity);
return new PluginSetting<T>(_buildInPluginInfo, _pluginSettingRepository, _settingEntities[name]);
}
}
}
/// <summary>
/// <para>A wrapper around plugin settings for internal use.</para>
/// <para>Do not inject into a plugin, for plugins inject <see cref="PluginSettings" /> instead.</para>
/// </summary>
public interface ISettingsService : IProtectedArtemisService
{
PluginSetting<T> GetSetting<T>(string name, T defaultValue = default(T));
}
}
@@ -9,43 +9,43 @@ namespace Artemis.Core.Services.Storage
public interface ISurfaceService : IArtemisService
{
/// <summary>
/// Gets the currently active surface configuration, to change config use <see cref="SetActiveSurfaceConfiguration" />
/// Gets the currently active surface entity, to change config use <see cref="SetActiveSurfaceConfiguration" />
/// </summary>
SurfaceConfiguration ActiveSurfaceConfiguration { get; }
Surface ActiveSurface { get; }
/// <summary>
/// Gets a read-only list of all surface configurations
/// </summary>
ReadOnlyCollection<SurfaceConfiguration> SurfaceConfigurations { get; }
ReadOnlyCollection<Surface> SurfaceConfigurations { get; }
/// <summary>
/// Creates a new surface configuration with the supplied name
/// Creates a new surface entity with the supplied name
/// </summary>
/// <param name="name">The name for the new surface configuration</param>
/// <param name="name">The name for the new surface entity</param>
/// <returns></returns>
SurfaceConfiguration CreateSurfaceConfiguration(string name);
Surface CreateSurfaceConfiguration(string name);
/// <summary>
/// Sets the provided configuration as active and applies it to the surface
/// Sets the provided entity as active and applies it to the surface
/// </summary>
/// <param name="surfaceConfiguration">The configuration to activate and apply</param>
void SetActiveSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration);
/// <param name="surface">The entity to activate and apply</param>
void SetActiveSurfaceConfiguration(Surface surface);
/// <summary>
/// Saves the provided surface configuration to permanent storage and if config is active, applies it to the surface
/// Saves the provided surface entity to permanent storage and if config is active, applies it to the surface
/// </summary>
/// <param name="surfaceConfiguration">The configuration to save (and apply if active)</param>
/// <param name="surface">The entity to save (and apply if active)</param>
/// <param name="includeDevices">Whether to also save devices. If false, devices changes won't be applied either</param>
void UpdateSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration, bool includeDevices);
void UpdateSurfaceConfiguration(Surface surface, bool includeDevices);
/// <summary>
/// Deletes the supplied surface configuration, surface configuration may not be the active surface configuration
/// Deletes the supplied surface entity, surface entity may not be the active surface entity
/// </summary>
/// <param name="surfaceConfiguration">The surface configuration to delete, may not be the active surface configuration</param>
void DeleteSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration);
/// <param name="surface">The surface entity to delete, may not be the active surface entity</param>
void DeleteSurfaceConfiguration(Surface surface);
/// <summary>
/// Occurs when the active device configuration has been changed
/// Occurs when the active device entity has been changed
/// </summary>
event EventHandler<SurfaceConfigurationEventArgs> ActiveSurfaceConfigurationChanged;
}
@@ -4,7 +4,9 @@ using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.Events;
using Artemis.Core.Exceptions;
using Artemis.Core.Extensions;
using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Repositories.Interfaces;
using RGB.NET.Core;
@@ -16,45 +18,37 @@ namespace Artemis.Core.Services.Storage
{
private readonly ILogger _logger;
private readonly IRgbService _rgbService;
private readonly List<SurfaceConfiguration> _surfaceConfigurations;
private readonly List<Surface> _surfaceConfigurations;
private readonly ISurfaceRepository _surfaceRepository;
private readonly PluginSetting<double> _renderScaleSetting;
public SurfaceService(ILogger logger, ISurfaceRepository surfaceRepository, IRgbService rgbService)
internal SurfaceService(ILogger logger, ISurfaceRepository surfaceRepository, IRgbService rgbService, ISettingsService settingsService)
{
_logger = logger;
_surfaceRepository = surfaceRepository;
_rgbService = rgbService;
_surfaceConfigurations = new List<SurfaceConfiguration>();
_surfaceConfigurations = new List<Surface>();
_renderScaleSetting = settingsService.GetSetting("RenderScale", 1.0);
LoadFromRepository();
_rgbService.DeviceLoaded += RgbServiceOnDeviceLoaded;
_renderScaleSetting.SettingChanged += RenderScaleSettingOnSettingChanged;
}
public SurfaceConfiguration ActiveSurfaceConfiguration { get; private set; }
public ReadOnlyCollection<SurfaceConfiguration> SurfaceConfigurations
{
get
{
lock (_surfaceConfigurations)
{
return _surfaceConfigurations.AsReadOnly();
}
}
}
public SurfaceConfiguration CreateSurfaceConfiguration(string name)
public Surface ActiveSurface { get; private set; }
public ReadOnlyCollection<Surface> SurfaceConfigurations => _surfaceConfigurations.AsReadOnly();
public Surface CreateSurfaceConfiguration(string name)
{
// Create a blank config
var configuration = new SurfaceConfiguration(name);
var configuration = new Surface(_rgbService.Surface, name, _renderScaleSetting.Value);
// Add all current devices
foreach (var rgbDevice in _rgbService.LoadedDevices)
{
var deviceId = GetDeviceId(rgbDevice);
configuration.DeviceConfigurations.Add(new SurfaceDeviceConfiguration(rgbDevice, deviceId, configuration));
}
foreach (var rgbDevice in _rgbService.LoadedDevices)
configuration.Devices.Add(new Device(rgbDevice, configuration));
lock (_surfaceConfigurations)
{
@@ -64,67 +58,67 @@ namespace Artemis.Core.Services.Storage
}
}
public void SetActiveSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration)
public void SetActiveSurfaceConfiguration(Surface surface)
{
if (ActiveSurfaceConfiguration == surfaceConfiguration)
if (ActiveSurface == surface)
return;
// Set the new configuration
ActiveSurfaceConfiguration = surfaceConfiguration;
// Set the new entity
ActiveSurface = surface;
// Ensure only the new configuration is marked as active
// Ensure only the new entity is marked as active
lock (_surfaceConfigurations)
{
// Mark only the new surfaceConfiguration as active
// Mark only the new surface as active
foreach (var configuration in _surfaceConfigurations)
{
configuration.IsActive = configuration == ActiveSurfaceConfiguration;
configuration.IsActive = configuration == ActiveSurface;
configuration.ApplyToEntity();
}
_surfaceRepository.Save();
}
// Apply the active surface configuration to the devices
if (ActiveSurfaceConfiguration != null)
// Apply the active surface entity to the devices
if (ActiveSurface != null)
{
foreach (var deviceConfiguration in ActiveSurfaceConfiguration.DeviceConfigurations)
deviceConfiguration.ApplyToDevice();
foreach (var device in ActiveSurface.Devices)
device.ApplyToRgbDevice();
}
// Update the RGB service's graphics decorator to work with the new surface configuration
// Update the RGB service's graphics decorator to work with the new surface entity
_rgbService.UpdateGraphicsDecorator();
OnActiveSurfaceConfigurationChanged(new SurfaceConfigurationEventArgs(ActiveSurfaceConfiguration));
OnActiveSurfaceConfigurationChanged(new SurfaceConfigurationEventArgs(ActiveSurface));
}
public void UpdateSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration, bool includeDevices)
public void UpdateSurfaceConfiguration(Surface surface, bool includeDevices)
{
surfaceConfiguration.ApplyToEntity();
surface.ApplyToEntity();
if (includeDevices)
{
foreach (var deviceConfiguration in surfaceConfiguration.DeviceConfigurations)
foreach (var deviceConfiguration in surface.Devices)
{
deviceConfiguration.ApplyToEntity();
if (surfaceConfiguration.IsActive)
deviceConfiguration.ApplyToDevice();
deviceConfiguration.ApplyToConfiguration();
if (surface.IsActive)
deviceConfiguration.ApplyToRgbDevice();
}
}
_surfaceRepository.Save();
_rgbService.UpdateGraphicsDecorator();
OnSurfaceConfigurationUpdated(new SurfaceConfigurationEventArgs(surfaceConfiguration));
OnSurfaceConfigurationUpdated(new SurfaceConfigurationEventArgs(surface));
}
public void DeleteSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration)
public void DeleteSurfaceConfiguration(Surface surface)
{
if (surfaceConfiguration == ActiveSurfaceConfiguration)
throw new ArtemisCoreException($"Cannot delete surface configuration '{surfaceConfiguration.Name}' because it is active.");
if (surface == ActiveSurface)
throw new ArtemisCoreException($"Cannot delete surface entity '{surface.Name}' because it is active.");
lock (_surfaceConfigurations)
{
var entity = surfaceConfiguration.SurfaceEntity;
surfaceConfiguration.Destroy();
_surfaceConfigurations.Remove(surfaceConfiguration);
var entity = surface.SurfaceEntity;
surface.Destroy();
_surfaceConfigurations.Remove(surface);
_surfaceRepository.Remove(entity);
_surfaceRepository.Save();
@@ -138,10 +132,17 @@ namespace Artemis.Core.Services.Storage
lock (_surfaceConfigurations)
{
foreach (var surfaceConfiguration in _surfaceConfigurations)
MatchDeviceConfiguration(e.Device, surfaceConfiguration);
AddDeviceIfMissing(e.Device, surfaceConfiguration);
}
UpdateSurfaceConfiguration(ActiveSurfaceConfiguration, true);
UpdateSurfaceConfiguration(ActiveSurface, true);
}
private void RenderScaleSettingOnSettingChanged(object sender, EventArgs e)
{
foreach (var surfaceConfiguration in SurfaceConfigurations)
surfaceConfiguration.UpdateScale(_renderScaleSetting.Value);
}
#endregion
@@ -153,12 +154,9 @@ namespace Artemis.Core.Services.Storage
var configs = _surfaceRepository.GetAll();
foreach (var surfaceEntity in configs)
{
// Create the surface configuration
var surfaceConfiguration = new SurfaceConfiguration(surfaceEntity);
// For each loaded device, match a device configuration
var devices = _rgbService.LoadedDevices;
foreach (var rgbDevice in devices)
MatchDeviceConfiguration(rgbDevice, surfaceConfiguration);
// Create the surface entity
var surfaceConfiguration = new Surface(_rgbService.Surface, surfaceEntity, _renderScaleSetting.Value);
// Finally, add the surface config to the collection
lock (_surfaceConfigurations)
{
@@ -170,39 +168,44 @@ namespace Artemis.Core.Services.Storage
var active = SurfaceConfigurations.FirstOrDefault(c => c.IsActive);
if (active != null)
SetActiveSurfaceConfiguration(active);
else
{
active = SurfaceConfigurations.FirstOrDefault();
if (active != null)
SetActiveSurfaceConfiguration(active);
else
SetActiveSurfaceConfiguration(CreateSurfaceConfiguration("Default"));
}
}
#endregion
#region Utilities
private void MatchDeviceConfiguration(IRGBDevice rgbDevice, SurfaceConfiguration surfaceConfiguration)
private void AddDeviceIfMissing(IRGBDevice rgbDevice, Surface surface)
{
var deviceId = GetDeviceId(rgbDevice);
var deviceConfig = surfaceConfiguration.DeviceConfigurations.FirstOrDefault(d => d.DeviceName == rgbDevice.DeviceInfo.DeviceName &&
d.DeviceModel == rgbDevice.DeviceInfo.Model &&
d.DeviceManufacturer == rgbDevice.DeviceInfo.Manufacturer &&
d.DeviceId == deviceId);
var deviceHashCode = rgbDevice.GetDeviceHashCode();
var device = surface.Devices.FirstOrDefault(d => d.Configuration.DeviceHashCode == deviceHashCode);
if (deviceConfig == null)
if (device != null)
return;
// Find an existing device config and use that
var existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceHashCode == deviceHashCode);
if (existingDeviceConfig != null)
device = new Device(rgbDevice,surface, existingDeviceConfig);
// Fall back on creating a new device
else
{
_logger.Information("No active surface config found for {deviceInfo}, device ID: {deviceId}. Adding a new entry.", rgbDevice.DeviceInfo, deviceId);
deviceConfig = new SurfaceDeviceConfiguration(rgbDevice, deviceId, surfaceConfiguration);
surfaceConfiguration.DeviceConfigurations.Add(deviceConfig);
_logger.Information(
"No device config found for {deviceInfo}, device hash: {deviceHashCode}. Adding a new entry.",
rgbDevice.DeviceInfo,
deviceHashCode
);
device = new Device(rgbDevice, surface);
}
deviceConfig.Device = rgbDevice;
deviceConfig.ApplyToDevice();
}
private int GetDeviceId(IRGBDevice rgbDevice)
{
return _rgbService.LoadedDevices
.Where(d => d.DeviceInfo.DeviceName == rgbDevice.DeviceInfo.DeviceName &&
d.DeviceInfo.Model == rgbDevice.DeviceInfo.Model &&
d.DeviceInfo.Manufacturer == rgbDevice.DeviceInfo.Manufacturer)
.ToList()
.IndexOf(rgbDevice) + 1;
surface.Devices.Add(device);
}
#endregion