diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj
index b672beb96..c5242f460 100644
--- a/src/Artemis.Core/Artemis.Core.csproj
+++ b/src/Artemis.Core/Artemis.Core.csproj
@@ -186,6 +186,7 @@
+
diff --git a/src/Artemis.Core/Events/DeviceConfigurationEventArgs.cs b/src/Artemis.Core/Events/DeviceConfigurationEventArgs.cs
index 36ad829e5..327647ef7 100644
--- a/src/Artemis.Core/Events/DeviceConfigurationEventArgs.cs
+++ b/src/Artemis.Core/Events/DeviceConfigurationEventArgs.cs
@@ -1,18 +1,15 @@
using System;
using Artemis.Core.Models.Surface;
-using RGB.NET.Core;
namespace Artemis.Core.Events
{
public class SurfaceConfigurationEventArgs : EventArgs
{
- public SurfaceConfigurationEventArgs(SurfaceConfiguration surfaceConfiguration, IRGBDevice device)
+ public SurfaceConfigurationEventArgs(SurfaceConfiguration surfaceConfiguration)
{
SurfaceConfiguration = surfaceConfiguration;
- Device = device;
}
public SurfaceConfiguration SurfaceConfiguration { get; }
- public IRGBDevice Device { get; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/SurfaceConfiguration.cs b/src/Artemis.Core/Models/Surface/SurfaceConfiguration.cs
index 4411ebb81..b59e54983 100644
--- a/src/Artemis.Core/Models/Surface/SurfaceConfiguration.cs
+++ b/src/Artemis.Core/Models/Surface/SurfaceConfiguration.cs
@@ -1,19 +1,27 @@
using System.Collections.Generic;
-using System.Linq;
using Artemis.Storage.Entities;
-using RGB.NET.Core;
namespace Artemis.Core.Models.Surface
{
public class SurfaceConfiguration
{
- internal SurfaceConfiguration()
+ internal SurfaceConfiguration(string name)
{
+ SurfaceEntity = new SurfaceEntity();
+ Guid = System.Guid.NewGuid().ToString();
+
+ Name = name;
+ IsActive = false;
+ DeviceConfigurations = new List();
+
+ ApplyToEntity();
}
internal SurfaceConfiguration(SurfaceEntity surfaceEntity)
{
+ SurfaceEntity = surfaceEntity;
Guid = surfaceEntity.Guid;
+
Name = surfaceEntity.Name;
IsActive = surfaceEntity.IsActive;
DeviceConfigurations = new List();
@@ -24,9 +32,27 @@ namespace Artemis.Core.Models.Surface
DeviceConfigurations.Add(new SurfaceDeviceConfiguration(position, this));
}
+ internal SurfaceEntity SurfaceEntity { get; set; }
internal string Guid { get; set; }
+
public string Name { get; set; }
public bool IsActive { get; internal set; }
public List DeviceConfigurations { get; internal set; }
+
+ internal void ApplyToEntity()
+ {
+ SurfaceEntity.Guid = Guid;
+ SurfaceEntity.Name = Name;
+ SurfaceEntity.IsActive = IsActive;
+ }
+
+ internal void Destroy()
+ {
+ SurfaceEntity = null;
+
+ foreach (var deviceConfiguration in DeviceConfigurations)
+ deviceConfiguration.Destroy();
+ DeviceConfigurations.Clear();
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/SurfaceDeviceConfiguration.cs b/src/Artemis.Core/Models/Surface/SurfaceDeviceConfiguration.cs
index 3e7a895ac..0f35084c3 100644
--- a/src/Artemis.Core/Models/Surface/SurfaceDeviceConfiguration.cs
+++ b/src/Artemis.Core/Models/Surface/SurfaceDeviceConfiguration.cs
@@ -1,49 +1,102 @@
-using Artemis.Storage.Entities;
+using System.Linq;
+using Artemis.Storage.Entities;
using RGB.NET.Core;
namespace Artemis.Core.Models.Surface
{
public class SurfaceDeviceConfiguration
{
- internal SurfaceDeviceConfiguration(int deviceId, IRGBDeviceInfo deviceInfo, SurfaceConfiguration surface)
+ internal SurfaceDeviceConfiguration(IRGBDevice device, int deviceId, SurfaceConfiguration surface)
{
+ PositionEntity = new SurfacePositionEntity();
+ Guid = System.Guid.NewGuid().ToString();
+
+ Device = device;
DeviceId = deviceId;
- DeviceName = deviceInfo.DeviceName;
- DeviceModel = deviceInfo.Model;
- DeviceManufacturer = deviceInfo.Manufacturer;
+ DeviceName = device.DeviceInfo.DeviceName;
+ DeviceModel = device.DeviceInfo.Model;
+ DeviceManufacturer = device.DeviceInfo.Manufacturer;
+
+ X = device.Location.X;
+ Y = device.Location.Y;
+ Rotation = 0;
+ ZIndex = 1;
Surface = surface;
+
+ ApplyToEntity();
}
- internal SurfaceDeviceConfiguration(SurfacePositionEntity position, SurfaceConfiguration surfaceConfiguration)
+ internal SurfaceDeviceConfiguration(SurfacePositionEntity positionEntity, SurfaceConfiguration surfaceConfiguration)
{
- Guid = position.Guid;
+ PositionEntity = positionEntity;
+ Guid = positionEntity.Guid;
- DeviceId = position.DeviceId;
- DeviceName = position.DeviceName;
- DeviceModel = position.DeviceModel;
- DeviceManufacturer = position.DeviceManufacturer;
+ DeviceId = positionEntity.DeviceId;
+ DeviceName = positionEntity.DeviceName;
+ DeviceModel = positionEntity.DeviceModel;
+ DeviceManufacturer = positionEntity.DeviceManufacturer;
- X = position.X;
- Y = position.Y;
- Rotation = position.Rotation;
- ZIndex = position.ZIndex;
+ X = positionEntity.X;
+ Y = positionEntity.Y;
+ Rotation = positionEntity.Rotation;
+ ZIndex = positionEntity.ZIndex;
Surface = surfaceConfiguration;
}
-
- internal string Guid { get; set; }
- public int DeviceId { get; set; }
- public string DeviceName { get; set; }
- public string DeviceModel { get; set; }
- public string DeviceManufacturer { get; set; }
+ internal SurfacePositionEntity PositionEntity { get; set; }
+ internal string Guid { get; }
+
+ public IRGBDevice Device { get; private set; }
+ public int DeviceId { get; }
+ public string DeviceName { get; }
+ public string DeviceModel { get; }
+ public string DeviceManufacturer { get; }
public double X { get; set; }
public double Y { get; set; }
public double Rotation { get; set; }
public int ZIndex { get; set; }
- public SurfaceConfiguration Surface { get; internal set; }
+ public SurfaceConfiguration Surface { get; private set; }
+
+ ///
+ /// Applies the configuration to the device
+ ///
+ public void ApplyToDevice()
+ {
+ Device.Location = new Point(X, Y);
+ }
+
+ ///
+ /// Must be called when saving to the database
+ ///
+ internal void ApplyToEntity()
+ {
+ PositionEntity.Guid = Guid;
+ PositionEntity.SurfaceId = Surface.Guid;
+
+ PositionEntity.DeviceId = DeviceId;
+ PositionEntity.DeviceName = DeviceName;
+ PositionEntity.DeviceModel = DeviceModel;
+ PositionEntity.DeviceManufacturer = DeviceManufacturer;
+
+ PositionEntity.X = X;
+ PositionEntity.Y = Y;
+ PositionEntity.Rotation = Rotation;
+ PositionEntity.ZIndex = ZIndex;
+
+ // Ensure the position entity is in the surface entity's' collection of positions
+ if (Surface.SurfaceEntity.SurfacePositions.All(p => p.Guid != Guid))
+ Surface.SurfaceEntity.SurfacePositions.Add(PositionEntity);
+ }
+
+ public void Destroy()
+ {
+ PositionEntity = null;
+ Device = null;
+ Surface = null;
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/Storage/ISurfaceService.cs b/src/Artemis.Core/Services/Storage/ISurfaceService.cs
new file mode 100644
index 000000000..f7e965d3f
--- /dev/null
+++ b/src/Artemis.Core/Services/Storage/ISurfaceService.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Artemis.Core.Events;
+using Artemis.Core.Models.Surface;
+using Artemis.Core.Services.Interfaces;
+
+namespace Artemis.Core.Services.Storage
+{
+ public interface ISurfaceService : IArtemisService
+ {
+ ///
+ /// Gets or sets the currently active surface configuration
+ ///
+ SurfaceConfiguration ActiveSurfaceConfiguration { get; set; }
+
+ ///
+ /// Gets a read-only list of all surface configurations
+ ///
+ ReadOnlyCollection SurfaceConfigurations { get; }
+
+ ///
+ /// Creates a new surface configuration with the supplied name
+ ///
+ /// The name for the new surface configuration
+ ///
+ SurfaceConfiguration CreateSurfaceConfiguration(string name);
+
+ ///
+ /// Deletes the supplied surface configuration, surface configuration may not be the active surface configuration
+ ///
+ /// The surface configuration to delete, may not be the active surface configuration
+ void DeleteSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration);
+
+ ///
+ /// Saves the provided surface configurations to permanent storage
+ ///
+ /// The configurations to save
+ /// Whether to also save devices
+ void SaveToRepository(List surfaceConfigurations, bool includeDevices);
+
+ ///
+ /// Saves the provided surface configuration to permanent storage
+ ///
+ /// The configuration to save
+ /// Whether to also save devices
+ void SaveToRepository(SurfaceConfiguration surfaceConfiguration, bool includeDevices);
+
+ ///
+ /// Occurs when the active device configuration has been changed
+ ///
+ event EventHandler ActiveSurfaceConfigurationChanged;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/Storage/SurfaceService.cs b/src/Artemis.Core/Services/Storage/SurfaceService.cs
index d02c54c93..ea3d28453 100644
--- a/src/Artemis.Core/Services/Storage/SurfaceService.cs
+++ b/src/Artemis.Core/Services/Storage/SurfaceService.cs
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
-using System.Threading.Tasks;
using Artemis.Core.Events;
+using Artemis.Core.Exceptions;
using Artemis.Core.Models.Surface;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Repositories.Interfaces;
@@ -14,100 +15,155 @@ namespace Artemis.Core.Services.Storage
public class SurfaceService : ISurfaceService
{
private readonly ILogger _logger;
- private readonly ISurfaceRepository _surfaceRepository;
private readonly IRgbService _rgbService;
+ private readonly List _surfaceConfigurations;
+ private readonly ISurfaceRepository _surfaceRepository;
+ private SurfaceConfiguration _activeSurfaceConfiguration;
public SurfaceService(ILogger logger, ISurfaceRepository surfaceRepository, IRgbService rgbService)
{
_logger = logger;
_surfaceRepository = surfaceRepository;
_rgbService = rgbService;
+ _surfaceConfigurations = new List();
+
+ LoadFromRepository();
_rgbService.DeviceLoaded += RgbServiceOnDeviceLoaded;
}
- public async Task> GetSurfaceConfigurationsAsync()
+ public SurfaceConfiguration ActiveSurfaceConfiguration
{
- var surfaceEntities = await _surfaceRepository.GetAllAsync();
- var configs = new List();
- foreach (var surfaceEntity in surfaceEntities)
- configs.Add(new SurfaceConfiguration(surfaceEntity));
+ get => _activeSurfaceConfiguration;
+ set
+ {
+ if (_activeSurfaceConfiguration == value)
+ return;
- return configs;
+ _activeSurfaceConfiguration = value;
+
+ // Mark only the new value as active
+ foreach (var surfaceConfiguration in _surfaceConfigurations)
+ surfaceConfiguration.IsActive = false;
+ _activeSurfaceConfiguration.IsActive = true;
+
+ SaveToRepository(_surfaceConfigurations, true);
+ OnActiveSurfaceConfigurationChanged(new SurfaceConfigurationEventArgs(_activeSurfaceConfiguration));
+ }
}
- public async Task GetActiveSurfaceConfigurationAsync()
- {
- var entity = (await _surfaceRepository.GetAllAsync()).FirstOrDefault(d => d.IsActive);
- return entity != null ? new SurfaceConfiguration(entity) : null;
- }
-
- public async Task SetActiveSurfaceConfigurationAsync(SurfaceConfiguration surfaceConfiguration)
- {
- var surfaceEntities = await _surfaceRepository.GetAllAsync();
- foreach (var surfaceEntity in surfaceEntities)
- surfaceEntity.IsActive = surfaceEntity.Guid == surfaceConfiguration.Guid;
-
- await _surfaceRepository.SaveAsync();
- }
-
- public List GetSurfaceConfigurations()
- {
- var surfaceEntities = _surfaceRepository.GetAll();
- var configs = new List();
- foreach (var surfaceEntity in surfaceEntities)
- configs.Add(new SurfaceConfiguration(surfaceEntity));
-
- return configs;
- }
-
- public SurfaceConfiguration GetActiveSurfaceConfiguration()
- {
- var entity = _surfaceRepository.GetAll().FirstOrDefault(d => d.IsActive);
- return entity != null ? new SurfaceConfiguration(entity) : null;
- }
-
- public void SetActiveSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration)
- {
- var surfaceEntities = _surfaceRepository.GetAll();
- foreach (var surfaceEntity in surfaceEntities)
- surfaceEntity.IsActive = surfaceEntity.Guid == surfaceConfiguration.Guid;
-
- _surfaceRepository.Save();
- }
+ public ReadOnlyCollection SurfaceConfigurations => _surfaceConfigurations.AsReadOnly();
public SurfaceConfiguration CreateSurfaceConfiguration(string name)
{
// Create a blank config
- var configuration = new SurfaceConfiguration {Name = name, DeviceConfigurations = new List()};
+ var configuration = new SurfaceConfiguration(name);
// Add all current devices
foreach (var rgbDevice in _rgbService.LoadedDevices)
{
var deviceId = GetDeviceId(rgbDevice);
- configuration.DeviceConfigurations.Add(new SurfaceDeviceConfiguration(deviceId, rgbDevice.DeviceInfo, configuration));
+ configuration.DeviceConfigurations.Add(new SurfaceDeviceConfiguration(rgbDevice, deviceId, configuration));
}
+ _surfaceRepository.Add(configuration.SurfaceEntity);
+ SaveToRepository(configuration, true);
return configuration;
}
- private void ApplyDeviceConfiguration(IRGBDevice rgbDevice, SurfaceConfiguration surface)
+ public void DeleteSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration)
+ {
+ if (surfaceConfiguration == ActiveSurfaceConfiguration)
+ throw new ArtemisCoreException($"Cannot delete surface configuration '{surfaceConfiguration.Name}' because it is active.");
+
+ surfaceConfiguration.Destroy();
+ _surfaceConfigurations.Remove(surfaceConfiguration);
+
+ _surfaceRepository.Remove(surfaceConfiguration.SurfaceEntity);
+ _surfaceRepository.Save();
+ }
+
+ #region Event handlers
+
+ private void RgbServiceOnDeviceLoaded(object sender, DeviceEventArgs e)
+ {
+ // Match the newly loaded device with the current config
+ if (ActiveSurfaceConfiguration != null)
+ MatchDeviceConfiguration(e.Device, ActiveSurfaceConfiguration);
+ }
+
+ #endregion
+
+ #region Repository
+
+ private void LoadFromRepository()
+ {
+ 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);
+ // Finally, add the surface config to the collection
+ _surfaceConfigurations.Add(surfaceConfiguration);
+ }
+
+ // When all surface configs are loaded, apply the active surface config
+ var active = SurfaceConfigurations.FirstOrDefault(c => c.IsActive);
+ if (active != null)
+ ActiveSurfaceConfiguration = active;
+ }
+
+ public void SaveToRepository(List surfaceConfigurations, bool includeDevices)
+ {
+ foreach (var surfaceConfiguration in surfaceConfigurations)
+ {
+ surfaceConfiguration.ApplyToEntity();
+ if (!includeDevices)
+ {
+ foreach (var deviceConfiguration in surfaceConfiguration.DeviceConfigurations)
+ deviceConfiguration.ApplyToEntity();
+ }
+ }
+
+ _surfaceRepository.Save();
+ }
+
+ public void SaveToRepository(SurfaceConfiguration surfaceConfiguration, bool includeDevices)
+ {
+ surfaceConfiguration.ApplyToEntity();
+ if (includeDevices)
+ {
+ foreach (var deviceConfiguration in surfaceConfiguration.DeviceConfigurations)
+ deviceConfiguration.ApplyToEntity();
+ }
+
+ _surfaceRepository.Save();
+ }
+
+ #endregion
+
+ #region Utilities
+
+ private void MatchDeviceConfiguration(IRGBDevice rgbDevice, SurfaceConfiguration surfaceConfiguration)
{
var deviceId = GetDeviceId(rgbDevice);
- var deviceConfig = surface.DeviceConfigurations.FirstOrDefault(d => d.DeviceName == rgbDevice.DeviceInfo.DeviceName &&
- d.DeviceModel == rgbDevice.DeviceInfo.Model &&
- d.DeviceManufacturer == rgbDevice.DeviceInfo.Manufacturer &&
- d.DeviceId == deviceId);
+ var deviceConfig = surfaceConfiguration.DeviceConfigurations.FirstOrDefault(d => d.DeviceName == rgbDevice.DeviceInfo.DeviceName &&
+ d.DeviceModel == rgbDevice.DeviceInfo.Model &&
+ d.DeviceManufacturer == rgbDevice.DeviceInfo.Manufacturer &&
+ d.DeviceId == deviceId);
if (deviceConfig == null)
{
_logger.Information("No active surface config found for {deviceInfo}, device ID: {deviceId}. Adding a new entry.", rgbDevice.DeviceInfo, deviceId);
- deviceConfig = new SurfaceDeviceConfiguration(deviceId, rgbDevice.DeviceInfo, surface);
- surface.DeviceConfigurations.Add(deviceConfig);
+ deviceConfig = new SurfaceDeviceConfiguration(rgbDevice, deviceId, surfaceConfiguration);
+ surfaceConfiguration.DeviceConfigurations.Add(deviceConfig);
}
- rgbDevice.Location = new Point(deviceConfig.X, deviceConfig.Y);
- OnDeviceConfigurationApplied(new SurfaceConfigurationEventArgs(surface, rgbDevice));
+ deviceConfig.ApplyToDevice();
}
private int GetDeviceId(IRGBDevice rgbDevice)
@@ -120,43 +176,17 @@ namespace Artemis.Core.Services.Storage
.IndexOf(rgbDevice) + 1;
}
- private void RgbServiceOnDeviceLoaded(object sender, DeviceEventArgs e)
- {
- var activeConfiguration = GetActiveSurfaceConfiguration();
- if (activeConfiguration == null)
- {
- _logger.Information("No active surface config found, cannot apply settings to {deviceInfo}", e.Device.DeviceInfo);
- return;
- }
-
- ApplyDeviceConfiguration(e.Device, GetActiveSurfaceConfiguration());
- }
+ #endregion
#region Events
- public event EventHandler SurfaceConfigurationApplied;
+ public event EventHandler ActiveSurfaceConfigurationChanged;
- private void OnDeviceConfigurationApplied(SurfaceConfigurationEventArgs e)
+ protected virtual void OnActiveSurfaceConfigurationChanged(SurfaceConfigurationEventArgs e)
{
- SurfaceConfigurationApplied?.Invoke(this, e);
+ ActiveSurfaceConfigurationChanged?.Invoke(this, e);
}
#endregion
}
-
- public interface ISurfaceService : IArtemisService
- {
- Task> GetSurfaceConfigurationsAsync();
- Task GetActiveSurfaceConfigurationAsync();
- Task SetActiveSurfaceConfigurationAsync(SurfaceConfiguration surfaceConfiguration);
- List GetSurfaceConfigurations();
- SurfaceConfiguration GetActiveSurfaceConfiguration();
- SurfaceConfiguration CreateSurfaceConfiguration(string name);
- void SetActiveSurfaceConfiguration(SurfaceConfiguration surfaceConfiguration);
-
- ///
- /// Occurs when a device has a new surface configuration applied to it
- ///
- event EventHandler SurfaceConfigurationApplied;
- }
}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Repositories/Interfaces/ISurfaceRepository.cs b/src/Artemis.Storage/Repositories/Interfaces/ISurfaceRepository.cs
index 47be22342..220fdc2e4 100644
--- a/src/Artemis.Storage/Repositories/Interfaces/ISurfaceRepository.cs
+++ b/src/Artemis.Storage/Repositories/Interfaces/ISurfaceRepository.cs
@@ -7,6 +7,7 @@ namespace Artemis.Storage.Repositories.Interfaces
public interface ISurfaceRepository : IRepository
{
void Add(SurfaceEntity surfaceEntity);
+ void Remove(SurfaceEntity surfaceEntity);
SurfaceEntity Get(string name);
Task GetAsync(string name);
List GetAll();
diff --git a/src/Artemis.Storage/Repositories/SurfaceRepository.cs b/src/Artemis.Storage/Repositories/SurfaceRepository.cs
index 69ef4d6c7..0d29da7c2 100644
--- a/src/Artemis.Storage/Repositories/SurfaceRepository.cs
+++ b/src/Artemis.Storage/Repositories/SurfaceRepository.cs
@@ -22,6 +22,11 @@ namespace Artemis.Storage.Repositories
_dbContext.Surfaces.Add(surfaceEntity);
}
+ public void Remove(SurfaceEntity surfaceEntity)
+ {
+ _dbContext.Surfaces.Remove(surfaceEntity);
+ }
+
public SurfaceEntity Get(string name)
{
return _dbContext.Surfaces.Include(s => s.SurfacePositions).FirstOrDefault(p => p.Name == name);