From 43e396bf6d8055a7204ac9c957c543ad25781baf Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 16 Oct 2023 19:44:13 +0200 Subject: [PATCH] Core - Refactored layout loading, fixing LED images Core - Make a better effort at removing orphaned devices from device providers that failed to load --- .../Models/Surface/ArtemisDevice.cs | 87 ++++++++++--------- src/Artemis.Core/Models/Surface/ArtemisLed.cs | 6 +- .../Models/Surface/Layout/ArtemisLayout.cs | 14 +-- .../Models/Surface/Layout/ArtemisLedLayout.cs | 17 +--- src/Artemis.Core/Services/DeviceService.cs | 38 ++++---- .../Services/Interfaces/IRenderService.cs | 5 -- src/Artemis.Core/Services/RenderService.cs | 3 - .../Controls/DeviceVisualizer.cs | 42 +++++---- .../Device/Tabs/DeviceLayoutTabView.axaml | 2 +- .../Device/Tabs/DeviceLayoutTabViewModel.cs | 21 ++--- 10 files changed, 111 insertions(+), 124 deletions(-) diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs index d5ddd019d..80d417b31 100644 --- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs +++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics; using System.Linq; using Artemis.Core.DeviceProviders; using Artemis.Storage.Entities.Surface; @@ -22,6 +23,7 @@ public class ArtemisDevice : CorePropertyChanged internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider) { + Debug.WriteLine("Creating Artemis device for " + rgbDevice.DeviceInfo.DeviceName); rgbDevice.EnsureValidDimensions(); _originalLeds = new List(rgbDevice.Select(l => new OriginalLed(l))); _originalSize = rgbDevice.Size; @@ -44,24 +46,25 @@ public class ArtemisDevice : CorePropertyChanged InputIdentifiers = new List(); InputMappings = new Dictionary(); Categories = new HashSet(); - + RgbDevice.ColorCorrections.Clear(); RgbDevice.ColorCorrections.Add(new ScaleColorCorrection(this)); - - UpdateLeds(false); + + CreateArtemisLeds(false); ApplyKeyboardLayout(); CalculateRenderProperties(); Save(); - + RgbDevice.PropertyChanged += RgbDeviceOnPropertyChanged; } internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, DeviceEntity deviceEntity) { + Debug.WriteLine("Creating Artemis device for " + rgbDevice.DeviceInfo.DeviceName); rgbDevice.EnsureValidDimensions(); _originalLeds = new List(rgbDevice.Select(l => new OriginalLed(l))); _originalSize = rgbDevice.Size; - + RgbDevice = rgbDevice; Identifier = rgbDevice.GetDeviceIdentifier(); DeviceEntity = deviceEntity; @@ -75,18 +78,18 @@ public class ArtemisDevice : CorePropertyChanged foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers) InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier)); - + RgbDevice.ColorCorrections.Clear(); RgbDevice.ColorCorrections.Add(new ScaleColorCorrection(this)); - - UpdateLeds(false); + + CreateArtemisLeds(false); Load(); ApplyKeyboardLayout(); CalculateRenderProperties(); - + RgbDevice.PropertyChanged += RgbDeviceOnPropertyChanged; } - + /// /// Gets the (hopefully unique and persistent) ID of this device /// @@ -475,30 +478,28 @@ public class ArtemisDevice : CorePropertyChanged /// internal void ApplyLayout(ArtemisLayout? layout, bool createMissingLeds, bool removeExcessiveLeds) { - if (layout == null) + if (layout != null && layout.IsValid && createMissingLeds && !DeviceProvider.CreateMissingLedsSupported) + throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} set to true because the device provider does not support it"); + if (layout != null && layout.IsValid && removeExcessiveLeds && !DeviceProvider.RemoveExcessiveLedsSupported) + throw new ArtemisCoreException($"Cannot apply layout with {nameof(removeExcessiveLeds)} set to true because the device provider does not support it"); + + // Always clear the current layout + ClearLayout(); + + // If a valid layout was supplied, apply the layout to the device + if (layout != null && layout.IsValid) { - ClearLayout(); - UpdateLeds(true); - - CalculateRenderProperties(); - return; + layout.ApplyToDevice(RgbDevice, createMissingLeds, removeExcessiveLeds); + Layout = layout; + } + else + { + Layout = null; } - if (createMissingLeds && !DeviceProvider.CreateMissingLedsSupported) - throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} " + - "set to true because the device provider does not support it"); - if (removeExcessiveLeds && !DeviceProvider.RemoveExcessiveLedsSupported) - throw new ArtemisCoreException($"Cannot apply layout with {nameof(removeExcessiveLeds)} " + - "set to true because the device provider does not support it"); - - ClearLayout(); - if (layout.IsValid) - layout.ApplyTo(RgbDevice, createMissingLeds, removeExcessiveLeds); - UpdateLeds(true); - - Layout = layout; - Layout.ApplyDevice(this); - + // Recreate Artemis LEDs + CreateArtemisLeds(true); + // Calculate render properties with the new layout CalculateRenderProperties(); } @@ -506,7 +507,7 @@ public class ArtemisDevice : CorePropertyChanged { if (Layout == null) return; - + RgbDevice.DeviceInfo.LayoutMetadata = null; RgbDevice.Size = _originalSize; Layout = null; @@ -530,7 +531,7 @@ public class ArtemisDevice : CorePropertyChanged DeviceEntity.InputMappings.Clear(); foreach ((ArtemisLed? original, ArtemisLed? mapped) in InputMappings) DeviceEntity.InputMappings.Add(new InputMappingEntity {OriginalLedId = (int) original.RgbLed.Id, MappedLedId = (int) mapped.RgbLed.Id}); - + DeviceEntity.Categories.Clear(); foreach (DeviceCategory deviceCategory in Categories) DeviceEntity.Categories.Add((int) deviceCategory); @@ -547,7 +548,7 @@ public class ArtemisDevice : CorePropertyChanged Categories.Add((DeviceCategory) deviceEntityCategory); if (!Categories.Any()) ApplyDefaultCategories(); - + LoadInputMappings(); } @@ -565,19 +566,25 @@ public class ArtemisDevice : CorePropertyChanged path.AddRect(artemisLed.AbsoluteRectangle); Path = path; - + OnDeviceUpdated(); } - private void UpdateLeds(bool loadInputMappings) + private void CreateArtemisLeds(bool loadInputMappings) { Leds = RgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); LedIds = new ReadOnlyDictionary(Leds.ToDictionary(l => l.RgbLed.Id, l => l)); - + if (loadInputMappings) LoadInputMappings(); } + private void UpdateArtemisLeds() + { + foreach (ArtemisLed artemisLed in Leds) + artemisLed.CalculateRectangles(); + } + private void LoadInputMappings() { InputMappings.Clear(); @@ -606,12 +613,12 @@ public class ArtemisDevice : CorePropertyChanged else LogicalLayout = DeviceEntity.LogicalLayout; } - + private void RgbDeviceOnPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName != nameof(IRGBDevice.Surface) || RgbDevice.Surface == null) return; - + RgbDevice.Rotation = DeviceEntity.Rotation; RgbDevice.Scale = DeviceEntity.Scale; ApplyLocation(DeviceEntity.X, DeviceEntity.Y); @@ -624,7 +631,7 @@ public class ArtemisDevice : CorePropertyChanged RgbDevice.Location = new Point(1, 1); RgbDevice.Location = new Point(x, y); - UpdateLeds(false); + UpdateArtemisLeds(); CalculateRenderProperties(); } } diff --git a/src/Artemis.Core/Models/Surface/ArtemisLed.cs b/src/Artemis.Core/Models/Surface/ArtemisLed.cs index f3d0500e1..8d9cae40e 100644 --- a/src/Artemis.Core/Models/Surface/ArtemisLed.cs +++ b/src/Artemis.Core/Models/Surface/ArtemisLed.cs @@ -1,4 +1,5 @@ -using RGB.NET.Core; +using System.Linq; +using RGB.NET.Core; using SkiaSharp; namespace Artemis.Core; @@ -15,6 +16,9 @@ public class ArtemisLed : CorePropertyChanged { RgbLed = led; Device = device; + Layout = device.Layout?.Leds.FirstOrDefault(l => l.RgbLayout.Id == led.Id.ToString()); + Layout?.ApplyCustomLedData(Device); + CalculateRectangles(); } diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs index a931dcb0a..f44e3185d 100644 --- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs +++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLayout.cs @@ -36,11 +36,6 @@ public class ArtemisLayout /// public LayoutSource Source { get; } - /// - /// Gets the device this layout is applied to - /// - public ArtemisDevice? Device { get; private set; } - /// /// Gets a boolean indicating whether a valid layout was loaded /// @@ -69,7 +64,7 @@ public class ArtemisLayout /// /// Applies the layout to the provided device /// - public void ApplyTo(IRGBDevice device, bool createMissingLeds = false, bool removeExcessiveLeds = false) + public void ApplyToDevice(IRGBDevice device, bool createMissingLeds = false, bool removeExcessiveLeds = false) { device.Size = new Size(MathF.Round(RgbLayout.Width), MathF.Round(RgbLayout.Height)); device.DeviceInfo.LayoutMetadata = RgbLayout.CustomData; @@ -124,13 +119,6 @@ public class ArtemisLayout } } - internal void ApplyDevice(ArtemisDevice artemisDevice) - { - Device = artemisDevice; - foreach (ArtemisLedLayout artemisLedLayout in Leds) - artemisLedLayout.ApplyDevice(Device); - } - internal static ArtemisLayout? GetDefaultLayout(ArtemisDevice device) { string layoutFolder = Path.Combine(Constants.ApplicationFolder, "DefaultLayouts", "Artemis"); diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs index 025732df5..29872c4b6 100644 --- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs +++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs @@ -27,11 +27,6 @@ public class ArtemisLedLayout /// public ILedLayout RgbLayout { get; } - /// - /// Gets the LED this layout is applied to - /// - public ArtemisLed? Led { get; protected set; } - /// /// Gets the name of the logical layout this LED belongs to /// @@ -46,17 +41,9 @@ public class ArtemisLedLayout /// Gets the custom layout data embedded in the RGB.NET layout /// public LayoutCustomLedData LayoutCustomLedData { get; } + - internal void ApplyDevice(ArtemisDevice device) - { - Led = device.Leds.FirstOrDefault(d => d.RgbLed.Id.ToString() == RgbLayout.Id); - if (Led != null) - Led.Layout = this; - - ApplyCustomLedData(device); - } - - private void ApplyCustomLedData(ArtemisDevice artemisDevice) + internal void ApplyCustomLedData(ArtemisDevice artemisDevice) { if (LayoutCustomLedData.LogicalLayouts == null || !LayoutCustomLedData.LogicalLayouts.Any()) return; diff --git a/src/Artemis.Core/Services/DeviceService.cs b/src/Artemis.Core/Services/DeviceService.cs index 14e330977..ae43ab5ef 100644 --- a/src/Artemis.Core/Services/DeviceService.cs +++ b/src/Artemis.Core/Services/DeviceService.cs @@ -30,13 +30,13 @@ internal class DeviceService : IDeviceService EnabledDevices = new ReadOnlyCollection(_enabledDevices); Devices = new ReadOnlyCollection(_devices); - + RenderScale.RenderScaleMultiplierChanged += RenderScaleOnRenderScaleMultiplierChanged; } public IReadOnlyCollection EnabledDevices { get; } public IReadOnlyCollection Devices { get; } - + /// public void IdentifyDevice(ArtemisDevice device) { @@ -52,11 +52,12 @@ internal class DeviceService : IDeviceService try { // Can't see why this would happen, RgbService used to do this though - List toRemove = _devices.Where(a => rgbDeviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList(); + List toRemove = _devices.Where(a => a.DeviceProvider.Id == deviceProvider.Id).ToList(); _logger.Verbose("[AddDeviceProvider] Removing {Count} old device(s)", toRemove.Count); foreach (ArtemisDevice device in toRemove) { _devices.Remove(device); + _enabledDevices.Remove(device); OnDeviceRemoved(new DeviceEventArgs(device)); } @@ -94,7 +95,7 @@ internal class DeviceService : IDeviceService _devices.Add(artemisDevice); if (artemisDevice.IsEnabled) _enabledDevices.Add(artemisDevice); - + _logger.Debug("Device provider {deviceProvider} added {deviceName}", deviceProvider.GetType().Name, rgbDevice.DeviceInfo.DeviceName); } @@ -104,7 +105,7 @@ internal class DeviceService : IDeviceService OnDeviceProviderAdded(new DeviceProviderEventArgs(deviceProvider, addedDevices)); foreach (ArtemisDevice artemisDevice in addedDevices) OnDeviceAdded(new DeviceEventArgs(artemisDevice)); - + UpdateLeds(); } catch (Exception e) @@ -118,9 +119,8 @@ internal class DeviceService : IDeviceService public void RemoveDeviceProvider(DeviceProvider deviceProvider) { _logger.Verbose("[RemoveDeviceProvider] Pausing rendering to remove {DeviceProvider}", deviceProvider.GetType().Name); - IRGBDeviceProvider rgbDeviceProvider = deviceProvider.RgbDeviceProvider; - List toRemove = _devices.Where(a => rgbDeviceProvider.Devices.Any(d => a.RgbDevice == d)).ToList(); - + List toRemove = _devices.Where(a => a.DeviceProvider.Id == deviceProvider.Id).ToList(); + try { _logger.Verbose("[RemoveDeviceProvider] Removing {Count} old device(s)", toRemove.Count); @@ -131,11 +131,11 @@ internal class DeviceService : IDeviceService } _devices.Sort((a, b) => a.ZIndex - b.ZIndex); - + OnDeviceProviderRemoved(new DeviceProviderEventArgs(deviceProvider, toRemove)); foreach (ArtemisDevice artemisDevice in toRemove) OnDeviceRemoved(new DeviceEventArgs(artemisDevice)); - + UpdateLeds(); } catch (Exception e) @@ -155,7 +155,7 @@ internal class DeviceService : IDeviceService SaveDevices(); } - + /// public void ApplyDeviceLayout(ArtemisDevice device, ArtemisLayout? layout) { @@ -163,10 +163,10 @@ internal class DeviceService : IDeviceService device.ApplyLayout(layout, false, false); else device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported); - + UpdateLeds(); } - + /// public void EnableDevice(ArtemisDevice device) { @@ -213,7 +213,7 @@ internal class DeviceService : IDeviceService _deviceRepository.Save(_devices.Select(d => d.DeviceEntity)); UpdateLeds(); } - + private ArtemisDevice GetArtemisDevice(IRGBDevice rgbDevice) { string deviceIdentifier = rgbDevice.GetDeviceIdentifier(); @@ -233,7 +233,7 @@ internal class DeviceService : IDeviceService ApplyDeviceLayout(device, device.GetBestDeviceLayout()); return device; } - + private void BlinkDevice(ArtemisDevice device, int blinkCount) { RGBSurface surface = _renderService.Value.Surface; @@ -259,7 +259,7 @@ internal class DeviceService : IDeviceService } }); } - + private void CalculateRenderProperties() { foreach (ArtemisDevice artemisDevice in Devices) @@ -271,7 +271,7 @@ internal class DeviceService : IDeviceService { OnLedsChanged(); } - + private void RenderScaleOnRenderScaleMultiplierChanged(object? sender, EventArgs e) { CalculateRenderProperties(); @@ -296,7 +296,7 @@ internal class DeviceService : IDeviceService /// public event EventHandler? DeviceProviderRemoved; - + /// public event EventHandler? LedsChanged; @@ -329,7 +329,7 @@ internal class DeviceService : IDeviceService { DeviceProviderRemoved?.Invoke(this, e); } - + protected virtual void OnLedsChanged() { LedsChanged?.Invoke(this, EventArgs.Empty); diff --git a/src/Artemis.Core/Services/Interfaces/IRenderService.cs b/src/Artemis.Core/Services/Interfaces/IRenderService.cs index 153f4c827..e33d4fea0 100644 --- a/src/Artemis.Core/Services/Interfaces/IRenderService.cs +++ b/src/Artemis.Core/Services/Interfaces/IRenderService.cs @@ -21,11 +21,6 @@ public interface IRenderService : IArtemisService /// RGBSurface Surface { get; } - /// - /// Gets a list of registered renderers. - /// - List Renderers { get; } - /// /// Gets or sets a boolean indicating whether rendering is paused. /// diff --git a/src/Artemis.Core/Services/RenderService.cs b/src/Artemis.Core/Services/RenderService.cs index f9bf44013..7dc8dd64d 100644 --- a/src/Artemis.Core/Services/RenderService.cs +++ b/src/Artemis.Core/Services/RenderService.cs @@ -56,9 +56,6 @@ internal class RenderService : IRenderService, IRenderer, IDisposable /// public RGBSurface Surface => _surfaceManager.Surface; - /// - public List Renderers { get; } = new(); - /// public bool IsPaused { diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs index dab8dacc4..46db27445 100644 --- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs +++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs @@ -123,27 +123,35 @@ public class DeviceVisualizer : Control if (Device == null) return false; - bool difference = false; - - int newLedCount = Device.RgbDevice.Count(); - if (_previousState.Length != newLedCount) + // Device might be modified mid-check, in that case just pretend it was not dirty + try { - _previousState = new Color[newLedCount]; - difference = true; - } + bool difference = false; - // Check all LEDs for differences and copy the colors to a new state - int index = 0; - foreach (Led led in Device.RgbDevice) - { - if (_previousState[index] != led.Color) + int newLedCount = Device.RgbDevice.Count(); + if (_previousState.Length != newLedCount) + { + _previousState = new Color[newLedCount]; difference = true; + } - _previousState[index] = led.Color; - index++; + // Check all LEDs for differences and copy the colors to a new state + int index = 0; + foreach (Led led in Device.RgbDevice) + { + if (_previousState[index] != led.Color) + difference = true; + + _previousState[index] = led.Color; + index++; + } + + return difference; + } + catch (Exception) + { + return false; } - - return difference; } private void Update() @@ -335,7 +343,7 @@ public class DeviceVisualizer : Control deviceVisualizerLed.DrawBitmap(context, 2 * device.Scale); } - BitmapCache[path] = renderTargetBitmap; + // BitmapCache[path] = renderTargetBitmap; return renderTargetBitmap; } diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml b/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml index d253e231f..5075c6e5d 100644 --- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml +++ b/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabView.axaml @@ -52,7 +52,7 @@ - + diff --git a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabViewModel.cs index 419243e9a..c6e5dd6ba 100644 --- a/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabViewModel.cs +++ b/src/Artemis.UI/Screens/Device/Tabs/DeviceLayoutTabViewModel.cs @@ -33,7 +33,7 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase Device = device; DisplayName = "Layout"; DefaultLayoutPath = Device.DeviceProvider.LoadLayout(Device).FilePath; - + this.WhenActivated(d => { Device.PropertyChanged += DeviceOnPropertyChanged; @@ -42,23 +42,21 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase } public ArtemisDevice Device { get; } - + public string DefaultLayoutPath { get; } public string? ImagePath => Device.Layout?.Image?.LocalPath; - + public string? CustomLayoutPath => Device.CustomLayoutPath; - + public bool HasCustomLayout => Device.CustomLayoutPath != null; - + public void ClearCustomLayout() { Device.CustomLayoutPath = null; _notificationService.CreateNotification() .WithMessage("Cleared imported layout.") .WithSeverity(NotificationSeverity.Informational); - - _deviceService.SaveDevice(Device); } public async Task BrowseCustomLayout() @@ -75,8 +73,6 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase .WithTitle("Imported layout") .WithMessage($"File loaded from {files[0]}") .WithSeverity(NotificationSeverity.Informational); - - _deviceService.SaveDevice(Device); } } @@ -154,7 +150,12 @@ public class DeviceLayoutTabViewModel : ActivatableViewModelBase { if (e.PropertyName is nameof(Device.CustomLayoutPath) or nameof(Device.DisableDefaultLayout)) { - Task.Run(() => _deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout())); + Task.Run(() => + { + _deviceService.ApplyDeviceLayout(Device, Device.GetBestDeviceLayout()); + _deviceService.SaveDevice(Device); + }); + this.RaisePropertyChanged(nameof(CustomLayoutPath)); this.RaisePropertyChanged(nameof(HasCustomLayout)); }