1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Storage - Fixed remaining issues so far

This commit is contained in:
RobertBeekman 2024-03-09 10:54:12 +01:00
parent 41a7543778
commit 70994feb32
27 changed files with 169 additions and 193 deletions

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.Json; using System.Text.Json;
using Artemis.Core.JsonConverters; using Artemis.Core.JsonConverters;
using Artemis.Storage.Entities.Plugins;
namespace Artemis.Core; namespace Artemis.Core;
@ -90,7 +91,7 @@ public static class Constants
/// <summary> /// <summary>
/// The plugin used by core components of Artemis /// The plugin used by core components of Artemis
/// </summary> /// </summary>
public static readonly Plugin CorePlugin = new(CorePluginInfo, new DirectoryInfo(ApplicationFolder), null); public static readonly Plugin CorePlugin = new(CorePluginInfo, new DirectoryInfo(ApplicationFolder), new PluginEntity(){Id = CorePluginInfo.Guid}, false);
/// <summary> /// <summary>
/// A read-only collection containing all primitive numeric types /// A read-only collection containing all primitive numeric types

View File

@ -30,7 +30,6 @@ public static class ContainerExtensions
container.RegisterMany(coreAssembly, type => type.IsAssignableTo<IProtectedArtemisService>(), Reuse.Singleton, setup: Setup.With(condition: HasAccessToProtectedService)); container.RegisterMany(coreAssembly, type => type.IsAssignableTo<IProtectedArtemisService>(), Reuse.Singleton, setup: Setup.With(condition: HasAccessToProtectedService));
// Bind storage // Bind storage
container.RegisterDelegate(() => StorageManager.CreateRepository(Constants.DataFolder), Reuse.Singleton);
container.RegisterDelegate(() => StorageManager.CreateDbContext(Constants.DataFolder), Reuse.Transient); container.RegisterDelegate(() => StorageManager.CreateDbContext(Constants.DataFolder), Reuse.Transient);
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IRepository>(), Reuse.Singleton); container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IRepository>(), Reuse.Singleton);

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
namespace Artemis.Core; namespace Artemis.Core;
@ -15,7 +16,6 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
/// </summary> /// </summary>
public static readonly ProfileCategory Empty = new("Empty", -1); public static readonly ProfileCategory Empty = new("Empty", -1);
private readonly List<ProfileConfiguration> _profileConfigurations = new();
private bool _isCollapsed; private bool _isCollapsed;
private bool _isSuspended; private bool _isSuspended;
private string _name; private string _name;
@ -31,14 +31,16 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
_name = name; _name = name;
_order = order; _order = order;
Entity = new ProfileCategoryEntity(); Entity = new ProfileCategoryEntity();
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(_profileConfigurations); ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>([]);
Save();
} }
internal ProfileCategory(ProfileCategoryEntity entity) internal ProfileCategory(ProfileCategoryEntity entity)
{ {
_name = null!; _name = null!;
Entity = entity; Entity = entity;
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(_profileConfigurations); ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>([]);
Load(); Load();
} }
@ -83,7 +85,7 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
/// <summary> /// <summary>
/// Gets a read only collection of the profiles inside this category /// Gets a read only collection of the profiles inside this category
/// </summary> /// </summary>
public ReadOnlyCollection<ProfileConfiguration> ProfileConfigurations { get; } public ReadOnlyCollection<ProfileConfiguration> ProfileConfigurations { get; private set; }
/// <summary> /// <summary>
/// Gets the unique ID of this category /// Gets the unique ID of this category
@ -98,27 +100,30 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
/// </summary> /// </summary>
public void AddProfileConfiguration(ProfileConfiguration configuration, int? targetIndex) public void AddProfileConfiguration(ProfileConfiguration configuration, int? targetIndex)
{ {
List<ProfileConfiguration> targetList = ProfileConfigurations.ToList();
// TODO: Look into this, it doesn't seem to make sense // TODO: Look into this, it doesn't seem to make sense
// Removing the original will shift every item in the list forwards, keep that in mind with the target index // Removing the original will shift every item in the list forwards, keep that in mind with the target index
if (configuration.Category == this && targetIndex != null && targetIndex.Value > _profileConfigurations.IndexOf(configuration)) if (configuration.Category == this && targetIndex != null && targetIndex.Value > targetList.IndexOf(configuration))
targetIndex -= 1; targetIndex -= 1;
configuration.Category.RemoveProfileConfiguration(configuration); configuration.Category.RemoveProfileConfiguration(configuration);
if (targetIndex != null) if (targetIndex != null)
{ {
targetIndex = Math.Clamp(targetIndex.Value, 0, _profileConfigurations.Count); targetIndex = Math.Clamp(targetIndex.Value, 0, targetList.Count);
_profileConfigurations.Insert(targetIndex.Value, configuration); targetList.Insert(targetIndex.Value, configuration);
} }
else else
{ {
_profileConfigurations.Add(configuration); targetList.Add(configuration);
} }
configuration.Category = this; configuration.Category = this;
ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(targetList);
for (int index = 0; index < _profileConfigurations.Count; index++) for (int index = 0; index < ProfileConfigurations.Count; index++)
_profileConfigurations[index].Order = index; ProfileConfigurations[index].Order = index;
OnProfileConfigurationAdded(new ProfileConfigurationEventArgs(configuration)); OnProfileConfigurationAdded(new ProfileConfigurationEventArgs(configuration));
} }
@ -156,11 +161,10 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
internal void RemoveProfileConfiguration(ProfileConfiguration configuration) internal void RemoveProfileConfiguration(ProfileConfiguration configuration)
{ {
if (!_profileConfigurations.Remove(configuration)) ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(ProfileConfigurations.Where(pc => pc != configuration).ToList());
return; for (int index = 0; index < ProfileConfigurations.Count; index++)
ProfileConfigurations[index].Order = index;
for (int index = 0; index < _profileConfigurations.Count; index++)
_profileConfigurations[index].Order = index;
OnProfileConfigurationRemoved(new ProfileConfigurationEventArgs(configuration)); OnProfileConfigurationRemoved(new ProfileConfigurationEventArgs(configuration));
} }
@ -174,9 +178,7 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
IsSuspended = Entity.IsSuspended; IsSuspended = Entity.IsSuspended;
Order = Entity.Order; Order = Entity.Order;
_profileConfigurations.Clear(); ProfileConfigurations = new ReadOnlyCollection<ProfileConfiguration>(Entity.ProfileConfigurations.Select(pc => new ProfileConfiguration(this, pc)).ToList());
foreach (ProfileContainerEntity entityProfileConfiguration in Entity.ProfileConfigurations)
_profileConfigurations.Add(new ProfileConfiguration(this, entityProfileConfiguration));
} }
/// <inheritdoc /> /// <inheritdoc />
@ -188,7 +190,7 @@ public class ProfileCategory : CorePropertyChanged, IStorageModel
Entity.Order = Order; Entity.Order = Order;
Entity.ProfileConfigurations.Clear(); Entity.ProfileConfigurations.Clear();
foreach (ProfileConfiguration profileConfiguration in _profileConfigurations) foreach (ProfileConfiguration profileConfiguration in ProfileConfigurations)
Entity.ProfileConfigurations.Add(profileConfiguration.Entity); Entity.ProfileConfigurations.Add(profileConfiguration.Entity);
} }

View File

@ -115,11 +115,11 @@ public class ProfileConfigurationIcon : CorePropertyChanged, IStorageModel
{ {
IconType = (ProfileConfigurationIconType) _entity.ProfileConfiguration.IconType; IconType = (ProfileConfigurationIconType) _entity.ProfileConfiguration.IconType;
Fill = _entity.ProfileConfiguration.IconFill; Fill = _entity.ProfileConfiguration.IconFill;
if (IconType != ProfileConfigurationIconType.MaterialIcon)
return;
IconName = _entity.ProfileConfiguration.MaterialIcon; if (IconType == ProfileConfigurationIconType.MaterialIcon)
IconBytes = IconType == ProfileConfigurationIconType.BitmapImage ? _entity.Icon : null; IconName = _entity.ProfileConfiguration.MaterialIcon;
else
IconBytes = _entity.Icon;
OnIconUpdated(); OnIconUpdated();
} }

View File

@ -23,14 +23,14 @@ public class Plugin : CorePropertyChanged, IDisposable
private bool _isEnabled; private bool _isEnabled;
internal Plugin(PluginInfo info, DirectoryInfo directory, PluginEntity? pluginEntity) internal Plugin(PluginInfo info, DirectoryInfo directory, PluginEntity pluginEntity, bool loadedFromStorage)
{ {
Info = info; Info = info;
Directory = directory; Directory = directory;
Entity = pluginEntity ?? new PluginEntity {Id = Guid}; Entity = pluginEntity;
Info.Plugin = this; Info.Plugin = this;
_loadedFromStorage = pluginEntity != null; _loadedFromStorage = loadedFromStorage;
_features = new List<PluginFeatureInfo>(); _features = new List<PluginFeatureInfo>();
_profilers = new List<Profiler>(); _profilers = new List<Profiler>();

View File

@ -255,7 +255,6 @@ internal class DeviceService : IDeviceService
_logger.Information("No device config found for {DeviceInfo}, device hash: {DeviceHashCode}. Adding a new entry", rgbDevice.DeviceInfo, deviceIdentifier); _logger.Information("No device config found for {DeviceInfo}, device hash: {DeviceHashCode}. Adding a new entry", rgbDevice.DeviceInfo, deviceIdentifier);
device = new ArtemisDevice(rgbDevice, deviceProvider); device = new ArtemisDevice(rgbDevice, deviceProvider);
_deviceRepository.Add(device.DeviceEntity); _deviceRepository.Add(device.DeviceEntity);
_deviceRepository.SaveChanges();
} }
LoadDeviceLayout(device); LoadDeviceLayout(device);

View File

@ -372,7 +372,15 @@ internal class PluginManagementService : IPluginManagementService
} }
// Load the entity and fall back on creating a new one // Load the entity and fall back on creating a new one
Plugin plugin = new(pluginInfo, directory, _pluginRepository.GetPluginByGuid(pluginInfo.Guid)); PluginEntity? entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid);
bool loadedFromStorage = entity != null;
if (entity == null)
{
entity = new PluginEntity {Id = pluginInfo.Guid};
_pluginRepository.AddPlugin(entity);
}
Plugin plugin = new(pluginInfo, directory, entity, loadedFromStorage);
OnPluginLoading(new PluginEventArgs(plugin)); OnPluginLoading(new PluginEventArgs(plugin));
// Locate the main assembly entry // Locate the main assembly entry

View File

@ -40,7 +40,7 @@ internal class RenderService : IRenderService, IRenderer, IDisposable
_graphicsContextProviders = graphicsContextProviders; _graphicsContextProviders = graphicsContextProviders;
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30); _targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 30);
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.25); _renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.5);
_preferredGraphicsContext = settingsService.GetSetting("Core.PreferredGraphicsContext", "Software"); _preferredGraphicsContext = settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
_targetFrameRateSetting.SettingChanged += OnRenderSettingsChanged; _targetFrameRateSetting.SettingChanged += OnRenderSettingsChanged;
_renderScaleSetting.SettingChanged += RenderScaleSettingOnSettingChanged; _renderScaleSetting.SettingChanged += RenderScaleSettingOnSettingChanged;

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
@ -24,7 +25,6 @@ internal class ProfileService : IProfileService
private readonly IPluginManagementService _pluginManagementService; private readonly IPluginManagementService _pluginManagementService;
private readonly IDeviceService _deviceService; private readonly IDeviceService _deviceService;
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new(); private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
private readonly List<ProfileCategory> _profileCategories;
private readonly List<IProfileMigration> _profileMigrators; private readonly List<IProfileMigration> _profileMigrators;
private readonly List<Exception> _renderExceptions = new(); private readonly List<Exception> _renderExceptions = new();
private readonly List<Exception> _updateExceptions = new(); private readonly List<Exception> _updateExceptions = new();
@ -44,9 +44,8 @@ internal class ProfileService : IProfileService
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
_deviceService = deviceService; _deviceService = deviceService;
_profileMigrators = profileMigrators; _profileMigrators = profileMigrators;
_profileCategories = new List<ProfileCategory>(_profileCategoryRepository.GetAll().Select(c => new ProfileCategory(c)).OrderBy(c => c.Order));
ProfileCategories = new ReadOnlyCollection<ProfileCategory>(_profileCategories); ProfileCategories = new ReadOnlyCollection<ProfileCategory>(_profileCategoryRepository.GetAll().Select(c => new ProfileCategory(c)).OrderBy(c => c.Order).ToList());
_deviceService.LedsChanged += DeviceServiceOnLedsChanged; _deviceService.LedsChanged += DeviceServiceOnLedsChanged;
_pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureToggled; _pluginManagementService.PluginFeatureEnabled += PluginManagementServiceOnPluginFeatureToggled;
@ -54,7 +53,7 @@ internal class ProfileService : IProfileService
inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp; inputService.KeyboardKeyUp += InputServiceOnKeyboardKeyUp;
if (!_profileCategories.Any()) if (!ProfileCategories.Any())
CreateDefaultProfileCategories(); CreateDefaultProfileCategories();
UpdateModules(); UpdateModules();
} }
@ -76,55 +75,52 @@ internal class ProfileService : IProfileService
return; return;
} }
lock (_profileCategories) // Iterate the children in reverse because the first category must be rendered last to end up on top
for (int i = ProfileCategories.Count - 1; i > -1; i--)
{ {
// Iterate the children in reverse because the first category must be rendered last to end up on top ProfileCategory profileCategory = ProfileCategories[i];
for (int i = _profileCategories.Count - 1; i > -1; i--) for (int j = profileCategory.ProfileConfigurations.Count - 1; j > -1; j--)
{ {
ProfileCategory profileCategory = _profileCategories[i]; ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j];
for (int j = profileCategory.ProfileConfigurations.Count - 1; j > -1; j--)
// Process hotkeys that where pressed since this profile last updated
ProcessPendingKeyEvents(profileConfiguration);
bool shouldBeActive = profileConfiguration.ShouldBeActive(false);
if (shouldBeActive)
{ {
ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j]; profileConfiguration.Update();
shouldBeActive = profileConfiguration.ActivationConditionMet;
}
// Process hotkeys that where pressed since this profile last updated try
ProcessPendingKeyEvents(profileConfiguration); {
// Make sure the profile is active or inactive according to the parameters above
bool shouldBeActive = profileConfiguration.ShouldBeActive(false); if (shouldBeActive && profileConfiguration.Profile == null && profileConfiguration.BrokenState != "Failed to activate profile")
if (shouldBeActive) profileConfiguration.TryOrBreak(() => ActivateProfile(profileConfiguration), "Failed to activate profile");
if (shouldBeActive && profileConfiguration.Profile != null && !profileConfiguration.Profile.ShouldDisplay)
profileConfiguration.Profile.ShouldDisplay = true;
else if (!shouldBeActive && profileConfiguration.Profile != null)
{ {
profileConfiguration.Update(); if (!profileConfiguration.FadeInAndOut)
shouldBeActive = profileConfiguration.ActivationConditionMet; DeactivateProfile(profileConfiguration);
else if (!profileConfiguration.Profile.ShouldDisplay && profileConfiguration.Profile.Opacity <= 0)
DeactivateProfile(profileConfiguration);
else if (profileConfiguration.Profile.Opacity > 0)
RequestDeactivation(profileConfiguration);
} }
try profileConfiguration.Profile?.Update(deltaTime);
{ }
// Make sure the profile is active or inactive according to the parameters above catch (Exception e)
if (shouldBeActive && profileConfiguration.Profile == null && profileConfiguration.BrokenState != "Failed to activate profile") {
profileConfiguration.TryOrBreak(() => ActivateProfile(profileConfiguration), "Failed to activate profile"); _updateExceptions.Add(e);
if (shouldBeActive && profileConfiguration.Profile != null && !profileConfiguration.Profile.ShouldDisplay)
profileConfiguration.Profile.ShouldDisplay = true;
else if (!shouldBeActive && profileConfiguration.Profile != null)
{
if (!profileConfiguration.FadeInAndOut)
DeactivateProfile(profileConfiguration);
else if (!profileConfiguration.Profile.ShouldDisplay && profileConfiguration.Profile.Opacity <= 0)
DeactivateProfile(profileConfiguration);
else if (profileConfiguration.Profile.Opacity > 0)
RequestDeactivation(profileConfiguration);
}
profileConfiguration.Profile?.Update(deltaTime);
}
catch (Exception e)
{
_updateExceptions.Add(e);
}
} }
} }
LogProfileUpdateExceptions();
_pendingKeyboardEvents.Clear();
} }
LogProfileUpdateExceptions();
_pendingKeyboardEvents.Clear();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -137,35 +133,32 @@ internal class ProfileService : IProfileService
return; return;
} }
lock (_profileCategories) // Iterate the children in reverse because the first category must be rendered last to end up on top
for (int i = ProfileCategories.Count - 1; i > -1; i--)
{ {
// Iterate the children in reverse because the first category must be rendered last to end up on top ProfileCategory profileCategory = ProfileCategories[i];
for (int i = _profileCategories.Count - 1; i > -1; i--) for (int j = profileCategory.ProfileConfigurations.Count - 1; j > -1; j--)
{ {
ProfileCategory profileCategory = _profileCategories[i]; try
for (int j = profileCategory.ProfileConfigurations.Count - 1; j > -1; j--)
{ {
try ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j];
{ // Ensure all criteria are met before rendering
ProfileConfiguration profileConfiguration = profileCategory.ProfileConfigurations[j]; bool fadingOut = profileConfiguration.Profile?.ShouldDisplay == false && profileConfiguration.Profile?.Opacity > 0;
// Ensure all criteria are met before rendering if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && (profileConfiguration.ActivationConditionMet || fadingOut))
bool fadingOut = profileConfiguration.Profile?.ShouldDisplay == false && profileConfiguration.Profile?.Opacity > 0; profileConfiguration.Profile?.Render(canvas, SKPointI.Empty, null);
if (!profileConfiguration.IsSuspended && !profileConfiguration.IsMissingModule && (profileConfiguration.ActivationConditionMet || fadingOut)) }
profileConfiguration.Profile?.Render(canvas, SKPointI.Empty, null); catch (Exception e)
} {
catch (Exception e) _renderExceptions.Add(e);
{
_renderExceptions.Add(e);
}
} }
} }
LogProfileRenderExceptions();
} }
LogProfileRenderExceptions();
} }
/// <inheritdoc /> /// <inheritdoc />
public ReadOnlyCollection<ProfileCategory> ProfileCategories { get; } public ReadOnlyCollection<ProfileCategory> ProfileCategories { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public ProfileConfiguration CloneProfileConfiguration(ProfileConfiguration profileConfiguration) public ProfileConfiguration CloneProfileConfiguration(ProfileConfiguration profileConfiguration)
@ -242,7 +235,7 @@ internal class ProfileService : IProfileService
if (addToTop) if (addToTop)
{ {
profileCategory = new ProfileCategory(name, 1); profileCategory = new ProfileCategory(name, 1);
foreach (ProfileCategory category in _profileCategories) foreach (ProfileCategory category in ProfileCategories)
{ {
category.Order++; category.Order++;
category.Save(); category.Save();
@ -250,11 +243,11 @@ internal class ProfileService : IProfileService
} }
else else
{ {
profileCategory = new ProfileCategory(name, _profileCategories.Count + 1); profileCategory = new ProfileCategory(name, ProfileCategories.Count + 1);
} }
_profileCategoryRepository.Add(profileCategory.Entity); _profileCategoryRepository.Add(profileCategory.Entity);
_profileCategories.Add(profileCategory); ProfileCategories = new ReadOnlyCollection<ProfileCategory>([..ProfileCategories, profileCategory]);
OnProfileCategoryAdded(new ProfileCategoryEventArgs(profileCategory)); OnProfileCategoryAdded(new ProfileCategoryEventArgs(profileCategory));
return profileCategory; return profileCategory;
@ -266,7 +259,7 @@ internal class ProfileService : IProfileService
foreach (ProfileConfiguration profileConfiguration in profileCategory.ProfileConfigurations.ToList()) foreach (ProfileConfiguration profileConfiguration in profileCategory.ProfileConfigurations.ToList())
RemoveProfileConfiguration(profileConfiguration); RemoveProfileConfiguration(profileConfiguration);
_profileCategories.Remove(profileCategory); ProfileCategories = new ReadOnlyCollection<ProfileCategory>(ProfileCategories.Where(c => c != profileCategory).ToList());
_profileCategoryRepository.Remove(profileCategory.Entity); _profileCategoryRepository.Remove(profileCategory.Entity);
OnProfileCategoryRemoved(new ProfileCategoryEventArgs(profileCategory)); OnProfileCategoryRemoved(new ProfileCategoryEventArgs(profileCategory));
@ -299,16 +292,14 @@ internal class ProfileService : IProfileService
{ {
profileCategory.Save(); profileCategory.Save();
_profileCategoryRepository.SaveChanges(); _profileCategoryRepository.SaveChanges();
ProfileCategories = new ReadOnlyCollection<ProfileCategory>(ProfileCategories.OrderBy(c => c.Order).ToList());
lock (_profileCategories)
{
_profileCategories.Sort((a, b) => a.Order - b.Order);
}
} }
/// <inheritdoc /> /// <inheritdoc />
public void SaveProfile(Profile profile, bool includeChildren) public void SaveProfile(Profile profile, bool includeChildren)
{ {
Stopwatch sw = new();
sw.Start();
_logger.Debug("Updating profile - Saving {Profile}", profile); _logger.Debug("Updating profile - Saving {Profile}", profile);
profile.Save(); profile.Save();
if (includeChildren) if (includeChildren)
@ -329,9 +320,17 @@ internal class ProfileService : IProfileService
.SelectMany(c => c.ProfileConfigurations) .SelectMany(c => c.ProfileConfigurations)
.FirstOrDefault(p => p.Profile != null && p.Profile != profile && p.ProfileId == profile.ProfileEntity.Id); .FirstOrDefault(p => p.Profile != null && p.Profile != profile && p.ProfileId == profile.ProfileEntity.Id);
if (localInstance == null) if (localInstance == null)
{
sw.Stop();
_logger.Debug("Updated profile - Saved {Profile} in {Time}ms", profile, sw.Elapsed.TotalMilliseconds);
return; return;
}
DeactivateProfile(localInstance); DeactivateProfile(localInstance);
ActivateProfile(localInstance); ActivateProfile(localInstance);
sw.Stop();
_logger.Debug("Updated profile - Saved {Profile} in {Time}ms", profile, sw.Elapsed.TotalMilliseconds);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -479,10 +478,7 @@ internal class ProfileService : IProfileService
private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e) private void InputServiceOnKeyboardKeyUp(object? sender, ArtemisKeyboardKeyEventArgs e)
{ {
lock (_profileCategories) _pendingKeyboardEvents.Add(e);
{
_pendingKeyboardEvents.Add(e);
}
} }
private void MigrateProfile(JsonObject? configurationJson, JsonObject? profileJson) private void MigrateProfile(JsonObject? configurationJson, JsonObject? profileJson)

View File

@ -14,8 +14,6 @@ class Program
.WithoutThrowOnRegisteringDisposableTransient()); .WithoutThrowOnRegisteringDisposableTransient());
container.RegisterCore(); container.RegisterCore();
container.Resolve<ArtemisDbContext>().Database.EnsureCreated(); container.Resolve<ArtemisDbContext>().Database.EnsureCreated();
} }
} }

View File

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;

View File

@ -1,6 +1,5 @@
using System; using System;
using Artemis.Storage.Entities.Profile.Abstract; using Artemis.Storage.Entities.Profile.Abstract;
using LiteDB;
namespace Artemis.Storage.Entities.Profile; namespace Artemis.Storage.Entities.Profile;
@ -11,8 +10,5 @@ public class FolderEntity : RenderElementEntity
public bool IsExpanded { get; set; } public bool IsExpanded { get; set; }
public bool Suspended { get; set; } public bool Suspended { get; set; }
[BsonRef("ProfileEntity")]
public ProfileEntity Profile { get; set; } = null!;
public Guid ProfileId { get; set; } public Guid ProfileId { get; set; }
} }

View File

@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Artemis.Storage.Entities.Profile.Abstract; using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.AdaptionHints; using Artemis.Storage.Entities.Profile.AdaptionHints;
using LiteDB;
namespace Artemis.Storage.Entities.Profile; namespace Artemis.Storage.Entities.Profile;
@ -25,8 +24,5 @@ public class LayerEntity : RenderElementEntity
public PropertyGroupEntity? TransformPropertyGroup { get; set; } public PropertyGroupEntity? TransformPropertyGroup { get; set; }
public LayerBrushEntity? LayerBrush { get; set; } public LayerBrushEntity? LayerBrush { get; set; }
[BsonRef("ProfileEntity")]
public ProfileEntity Profile { get; set; } = null!;
public Guid ProfileId { get; set; } public Guid ProfileId { get; set; }
} }

View File

@ -31,9 +31,9 @@ internal class DeviceRepository : IDeviceRepository
return _dbContext.Devices.FirstOrDefault(d => d.Id == id); return _dbContext.Devices.FirstOrDefault(d => d.Id == id);
} }
public List<DeviceEntity> GetAll() public IEnumerable<DeviceEntity> GetAll()
{ {
return _dbContext.Devices.ToList(); return _dbContext.Devices;
} }
public void SaveChanges() public void SaveChanges()

View File

@ -37,9 +37,9 @@ internal class EntryRepository : IEntryRepository
return _dbContext.Entries.FirstOrDefault(s => s.EntryId == entryId); return _dbContext.Entries.FirstOrDefault(s => s.EntryId == entryId);
} }
public List<EntryEntity> GetAll() public IEnumerable<EntryEntity> GetAll()
{ {
return _dbContext.Entries.ToList(); return _dbContext.Entries;
} }
public void SaveChanges() public void SaveChanges()

View File

@ -8,6 +8,6 @@ public interface IDeviceRepository : IRepository
void Add(DeviceEntity deviceEntity); void Add(DeviceEntity deviceEntity);
void Remove(DeviceEntity deviceEntity); void Remove(DeviceEntity deviceEntity);
DeviceEntity? Get(string id); DeviceEntity? Get(string id);
List<DeviceEntity> GetAll(); IEnumerable<DeviceEntity> GetAll();
void SaveChanges(); void SaveChanges();
} }

View File

@ -10,6 +10,6 @@ public interface IEntryRepository : IRepository
void Remove(EntryEntity entryEntity); void Remove(EntryEntity entryEntity);
EntryEntity? Get(Guid id); EntryEntity? Get(Guid id);
EntryEntity? GetByEntryId(long entryId); EntryEntity? GetByEntryId(long entryId);
List<EntryEntity> GetAll(); IEnumerable<EntryEntity> GetAll();
void SaveChanges(); void SaveChanges();
} }

View File

@ -8,7 +8,6 @@ public interface IPluginRepository : IRepository
void AddPlugin(PluginEntity pluginEntity); void AddPlugin(PluginEntity pluginEntity);
PluginEntity? GetPluginByGuid(Guid pluginGuid); PluginEntity? GetPluginByGuid(Guid pluginGuid);
void AddSetting(PluginSettingEntity pluginSettingEntity); void AddSetting(PluginSettingEntity pluginSettingEntity);
PluginSettingEntity? GetSettingByGuid(Guid pluginGuid);
PluginSettingEntity? GetSettingByNameAndGuid(string name, Guid pluginGuid); PluginSettingEntity? GetSettingByNameAndGuid(string name, Guid pluginGuid);
void RemoveSettings(Guid pluginGuid); void RemoveSettings(Guid pluginGuid);
void SaveChanges(); void SaveChanges();

View File

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces; using Artemis.Storage.Repositories.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Repositories; namespace Artemis.Storage.Repositories;
@ -22,7 +23,7 @@ internal class PluginRepository : IPluginRepository
public PluginEntity? GetPluginByGuid(Guid pluginGuid) public PluginEntity? GetPluginByGuid(Guid pluginGuid)
{ {
return _dbContext.Plugins.FirstOrDefault(p => p.Id == pluginGuid); return _dbContext.Plugins.Include(p => p.Features).FirstOrDefault(p => p.Id == pluginGuid);
} }
public void AddSetting(PluginSettingEntity pluginSettingEntity) public void AddSetting(PluginSettingEntity pluginSettingEntity)
@ -31,11 +32,6 @@ internal class PluginRepository : IPluginRepository
SaveChanges(); SaveChanges();
} }
public PluginSettingEntity? GetSettingByGuid(Guid pluginGuid)
{
return _dbContext.PluginSettings.FirstOrDefault(p => p.PluginGuid == pluginGuid);
}
public PluginSettingEntity? GetSettingByNameAndGuid(string name, Guid pluginGuid) public PluginSettingEntity? GetSettingByNameAndGuid(string name, Guid pluginGuid)
{ {
return _dbContext.PluginSettings.FirstOrDefault(p => p.Name == name && p.PluginGuid == pluginGuid); return _dbContext.PluginSettings.FirstOrDefault(p => p.Name == name && p.PluginGuid == pluginGuid);

View File

@ -1,12 +1,13 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using LiteDB; using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage; namespace Artemis.Storage;
public static class StorageManager public static class StorageManager
{ {
private static bool _ranMigrations;
private static bool _inUse; private static bool _inUse;
/// <summary> /// <summary>
@ -19,7 +20,7 @@ public static class StorageManager
if (_inUse) if (_inUse)
throw new Exception("Storage is already in use, can't backup now."); throw new Exception("Storage is already in use, can't backup now.");
string database = Path.Combine(dataFolder, "database.db"); string database = Path.Combine(dataFolder, "artemis.db");
if (!File.Exists(database)) if (!File.Exists(database))
return; return;
@ -36,35 +37,20 @@ public static class StorageManager
oldest.Delete(); oldest.Delete();
} }
File.Copy(database, Path.Combine(backupFolder, $"database-{DateTime.Now:yyyy-dd-M--HH-mm-ss}.db")); File.Copy(database, Path.Combine(backupFolder, $"artemis-{DateTime.Now:yyyy-dd-M--HH-mm-ss}.db"));
}
/// <summary>
/// Creates the LiteRepository that will be managed by dependency injection
/// </summary>
/// <param name="dataFolder">The Artemis data folder</param>
public static LiteRepository CreateRepository(string dataFolder)
{
if (_inUse)
throw new Exception("Storage is already in use, use dependency injection to get the repository.");
try
{
_inUse = true;
return new LiteRepository($"FileName={Path.Combine(dataFolder, "database.db")}");
}
catch (LiteException e)
{
// I don't like this way of error reporting, now I need to use reflection if I want a meaningful error message
throw new Exception($"LiteDB threw error code {e.ErrorCode}. See inner exception for more details", e);
}
} }
public static ArtemisDbContext CreateDbContext(string dataFolder) public static ArtemisDbContext CreateDbContext(string dataFolder)
{ {
return new ArtemisDbContext() _inUse = true;
{
DataFolder = dataFolder ArtemisDbContext dbContext = new() {DataFolder = dataFolder};
}; if (_ranMigrations)
return dbContext;
dbContext.Database.Migrate();
_ranMigrations = true;
return dbContext;
} }
} }

View File

@ -174,16 +174,15 @@
<StackPanel Grid.Row="0" Grid.Column="0"> <StackPanel Grid.Row="0" Grid.Column="0">
<TextBlock Classes="library-name">Avalonia</TextBlock> <TextBlock Classes="library-name">Avalonia</TextBlock>
<TextBlock Classes="library-name">DryIoc</TextBlock> <TextBlock Classes="library-name">DryIoc</TextBlock>
<TextBlock Classes="library-name">Entity Framework Core</TextBlock>
<TextBlock Classes="library-name">FluentAvalonia</TextBlock> <TextBlock Classes="library-name">FluentAvalonia</TextBlock>
<TextBlock Classes="library-name">EmbedIO</TextBlock> <TextBlock Classes="library-name">EmbedIO</TextBlock>
<TextBlock Classes="library-name">Humanizer</TextBlock> <TextBlock Classes="library-name">Humanizer</TextBlock>
<TextBlock Classes="library-name">LiteDB</TextBlock>
<TextBlock Classes="library-name">McMaster.NETCore.Plugins</TextBlock> <TextBlock Classes="library-name">McMaster.NETCore.Plugins</TextBlock>
<TextBlock Classes="library-name">Newtonsoft.Json</TextBlock>
<TextBlock Classes="library-name">RGB.NET</TextBlock> <TextBlock Classes="library-name">RGB.NET</TextBlock>
<TextBlock Classes="library-name">Serilog</TextBlock> <TextBlock Classes="library-name">Serilog</TextBlock>
<TextBlock Classes="library-name">SkiaSharp</TextBlock> <TextBlock Classes="library-name">SkiaSharp</TextBlock>
<TextBlock Classes="library-name">Unclassified.NetRevisionTask</TextBlock> <TextBlock Classes="library-name">SQLite</TextBlock>
</StackPanel> </StackPanel>
<StackPanel Grid.Column="1"> <StackPanel Grid.Column="1">
<controls:HyperlinkButton NavigateUri="https://avaloniaui.net/"> <controls:HyperlinkButton NavigateUri="https://avaloniaui.net/">
@ -192,6 +191,9 @@
<controls:HyperlinkButton NavigateUri="https://github.com/dadhi/DryIoc"> <controls:HyperlinkButton NavigateUri="https://github.com/dadhi/DryIoc">
https://github.com/dadhi/DryIoc https://github.com/dadhi/DryIoc
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://learn.microsoft.com/en-us/ef/core/">
https://learn.microsoft.com/en-us/ef/core/
</controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://github.com/amwx/FluentAvalonia"> <controls:HyperlinkButton NavigateUri="https://github.com/amwx/FluentAvalonia">
https://github.com/amwx/FluentAvalonia https://github.com/amwx/FluentAvalonia
</controls:HyperlinkButton> </controls:HyperlinkButton>
@ -201,15 +203,9 @@
<controls:HyperlinkButton NavigateUri="https://github.com/Humanizr/Humanizer"> <controls:HyperlinkButton NavigateUri="https://github.com/Humanizr/Humanizer">
https://github.com/Humanizr/Humanizer https://github.com/Humanizr/Humanizer
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://www.litedb.org/">
https://www.litedb.org/
</controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://github.com/natemcmaster/DotNetCorePlugins"> <controls:HyperlinkButton NavigateUri="https://github.com/natemcmaster/DotNetCorePlugins">
https://github.com/natemcmaster/DotNetCorePlugins https://github.com/natemcmaster/DotNetCorePlugins
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://www.newtonsoft.com/json">
https://www.newtonsoft.com/json
</controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://github.com/DarthAffe/RGB.NET"> <controls:HyperlinkButton NavigateUri="https://github.com/DarthAffe/RGB.NET">
https://github.com/DarthAffe/RGB.NET https://github.com/DarthAffe/RGB.NET
</controls:HyperlinkButton> </controls:HyperlinkButton>
@ -219,8 +215,8 @@
<controls:HyperlinkButton NavigateUri="https://github.com/mono/SkiaSharp"> <controls:HyperlinkButton NavigateUri="https://github.com/mono/SkiaSharp">
https://github.com/mono/SkiaSharp https://github.com/mono/SkiaSharp
</controls:HyperlinkButton> </controls:HyperlinkButton>
<controls:HyperlinkButton NavigateUri="https://unclassified.software/en/apps/netrevisiontask"> <controls:HyperlinkButton NavigateUri="https://www.sqlite.org/">
https://unclassified.software/en/apps/netrevisiontask https://www.sqlite.org/
</controls:HyperlinkButton> </controls:HyperlinkButton>
</StackPanel> </StackPanel>
</Grid> </Grid>

View File

@ -162,7 +162,7 @@ public class GeneralTabViewModel : RoutableScreen
public PluginSetting<bool> ProfileEditorShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false); public PluginSetting<bool> ProfileEditorShowDataModelValues => _settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
public PluginSetting<LogEventLevel> CoreLoggingLevel => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information); public PluginSetting<LogEventLevel> CoreLoggingLevel => _settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Information);
public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Software"); public PluginSetting<string> CorePreferredGraphicsContext => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Software");
public PluginSetting<double> CoreRenderScale => _settingsService.GetSetting("Core.RenderScale", 0.25); public PluginSetting<double> CoreRenderScale => _settingsService.GetSetting("Core.RenderScale", 0.5);
public PluginSetting<int> CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30); public PluginSetting<int> CoreTargetFrameRate => _settingsService.GetSetting("Core.TargetFrameRate", 30);
public PluginSetting<bool> WebServerEnabled => _settingsService.GetSetting("WebServer.Enabled", true); public PluginSetting<bool> WebServerEnabled => _settingsService.GetSetting("WebServer.Enabled", true);
public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696); public PluginSetting<int> WebServerPort => _settingsService.GetSetting("WebServer.Port", 9696);

View File

@ -122,6 +122,7 @@ public partial class ProfileConfigurationEditViewModel : DialogViewModelBase<Pro
ProfileConfiguration.FadeInAndOut = FadeInAndOut; ProfileConfiguration.FadeInAndOut = FadeInAndOut;
await SaveIcon(); await SaveIcon();
ProfileConfiguration.Save();
_profileService.SaveProfileCategory(_profileCategory); _profileService.SaveProfileCategory(_profileCategory);
Close(ProfileConfiguration); Close(ProfileConfiguration);

View File

@ -97,7 +97,7 @@ public partial class SurfaceDeviceViewModel : ActivatableViewModelBase
if (x < 0 || y < 0) if (x < 0 || y < 0)
return false; return false;
double maxTextureSize = 4096 / _settingsService.GetSetting("Core.RenderScale", 0.25).Value; double maxTextureSize = 4096 / _settingsService.GetSetting("Core.RenderScale", 0.5).Value;
if (x + Device.Rectangle.Width > maxTextureSize || y + Device.Rectangle.Height > maxTextureSize) if (x + Device.Rectangle.Width > maxTextureSize || y + Device.Rectangle.Height > maxTextureSize)
return false; return false;

View File

@ -87,7 +87,7 @@ public partial class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewMod
public ReactiveCommand<ArtemisDevice, Unit> SendToBack { get; } public ReactiveCommand<ArtemisDevice, Unit> SendToBack { get; }
public ReactiveCommand<ArtemisDevice, Unit> SendBackward { get; } public ReactiveCommand<ArtemisDevice, Unit> SendBackward { get; }
public double MaxTextureSize => 4096 / _settingsService.GetSetting("Core.RenderScale", 0.25).Value; public double MaxTextureSize => 4096 / _settingsService.GetSetting("Core.RenderScale", 0.5).Value;
public void UpdateSelection(List<SurfaceDeviceViewModel> devices, bool expand, bool invert) public void UpdateSelection(List<SurfaceDeviceViewModel> devices, bool expand, bool invert)
{ {

View File

@ -1,31 +1,30 @@
using Artemis.WebClient.Workshop.Entities; using Artemis.Core;
using LiteDB; using Artemis.Core.Services;
namespace Artemis.WebClient.Workshop.Repositories; namespace Artemis.WebClient.Workshop.Repositories;
internal class AuthenticationRepository : IAuthenticationRepository internal class AuthenticationRepository : IAuthenticationRepository
{ {
private readonly LiteRepository _repository; private readonly PluginSetting<string> _refreshToken;
public AuthenticationRepository(LiteRepository repository) public AuthenticationRepository(ISettingsService settingsService)
{ {
_repository = repository; // Of course anyone can grab these indirectly, but that goes for whatever we do.
_repository.Database.GetCollection<RefreshTokenEntity>().EnsureIndex(s => s.RefreshToken); // ISettingsService is a protected service so we at least don't make it very straightforward.
_refreshToken = settingsService.GetSetting<string>("Workshop.RefreshToken");
} }
/// <inheritdoc /> /// <inheritdoc />
public void SetRefreshToken(string? refreshToken) public void SetRefreshToken(string? refreshToken)
{ {
_repository.Database.GetCollection<RefreshTokenEntity>().DeleteAll(); _refreshToken.Value = refreshToken;
_refreshToken.Save();
if (refreshToken != null)
_repository.Insert(new RefreshTokenEntity {RefreshToken = refreshToken});
} }
/// <inheritdoc /> /// <inheritdoc />
public string? GetRefreshToken() public string? GetRefreshToken()
{ {
return _repository.Query<RefreshTokenEntity>().FirstOrDefault()?.RefreshToken; return _refreshToken.Value;
} }
} }

View File

@ -171,7 +171,12 @@ public class WorkshopService : IWorkshopService
public void SaveInstalledEntry(InstalledEntry entry) public void SaveInstalledEntry(InstalledEntry entry)
{ {
entry.Save(); entry.Save();
_entryRepository.SaveChanges();
// Upsert for plebs
if (entry.Entity.Id == Guid.Empty)
_entryRepository.Add(entry.Entity);
else
_entryRepository.SaveChanges();
} }
/// <inheritdoc /> /// <inheritdoc />