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

Core - Nullable refactoring

Core - Nullable refactoring


Core - Nullable refactoring (finish)
This commit is contained in:
SpoinkyNL 2020-11-17 22:50:38 +01:00
parent b185b28645
commit fb3466e102
61 changed files with 320 additions and 272 deletions

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Artemis.Storage.Entities.Plugins;
namespace Artemis.Core namespace Artemis.Core
{ {
@ -34,13 +35,13 @@ namespace Artemis.Core
/// </summary> /// </summary>
public static readonly PluginInfo CorePluginInfo = new PluginInfo public static readonly PluginInfo CorePluginInfo = new PluginInfo
{ {
Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core" Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core", Version = new Version(2,0)
}; };
/// <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 Plugin(CorePluginInfo, new DirectoryInfo(ApplicationFolder)); public static readonly Plugin CorePlugin = new Plugin(CorePluginInfo, new DirectoryInfo(ApplicationFolder), null);
internal static readonly CorePluginFeature CorePluginFeature = new CorePluginFeature {Plugin = CorePlugin}; internal static readonly CorePluginFeature CorePluginFeature = new CorePluginFeature {Plugin = CorePlugin};
internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new EffectPlaceholderPlugin {Plugin = CorePlugin}; internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new EffectPlaceholderPlugin {Plugin = CorePlugin};

View File

@ -8,6 +8,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// A static class providing <see cref="Process" /> extensions /// A static class providing <see cref="Process" /> extensions
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1060:Move pinvokes to native methods class", Justification = "I don't care, piss off")]
public static class ProcessExtensions public static class ProcessExtensions
{ {
/// <summary> /// <summary>

View File

@ -20,7 +20,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
/// <param name="parent">The parent of the folder</param> /// <param name="parent">The parent of the folder</param>
/// <param name="name">The name of the folder</param> /// <param name="name">The name of the folder</param>
public Folder(ProfileElement parent, string name) public Folder(ProfileElement parent, string name) : base(parent.Profile)
{ {
FolderEntity = new FolderEntity(); FolderEntity = new FolderEntity();
EntityId = Guid.NewGuid(); EntityId = Guid.NewGuid();
@ -29,14 +29,11 @@ namespace Artemis.Core
Profile = Parent.Profile; Profile = Parent.Profile;
Name = name; Name = name;
Enabled = true; Enabled = true;
LayerEffectsList = new List<BaseLayerEffect>();
ExpandedPropertyGroups = new List<string>();
Parent.AddChild(this); Parent.AddChild(this);
} }
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) : base(parent.Profile)
{ {
FolderEntity = folderEntity; FolderEntity = folderEntity;
EntityId = folderEntity.Id; EntityId = folderEntity.Id;
@ -46,10 +43,7 @@ namespace Artemis.Core
Name = folderEntity.Name; Name = folderEntity.Name;
Enabled = folderEntity.Enabled; Enabled = folderEntity.Enabled;
Order = folderEntity.Order; Order = folderEntity.Order;
LayerEffectsList = new List<BaseLayerEffect>();
ExpandedPropertyGroups = new List<string>();
Load(); Load();
} }

View File

@ -28,7 +28,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
/// <param name="parent">The parent of the layer</param> /// <param name="parent">The parent of the layer</param>
/// <param name="name">The name of the layer</param> /// <param name="name">The name of the layer</param>
public Layer(ProfileElement parent, string name) public Layer(ProfileElement parent, string name) : base(parent.Profile)
{ {
LayerEntity = new LayerEntity(); LayerEntity = new LayerEntity();
EntityId = Guid.NewGuid(); EntityId = Guid.NewGuid();
@ -39,16 +39,14 @@ namespace Artemis.Core
Enabled = true; Enabled = true;
_general = new LayerGeneralProperties(); _general = new LayerGeneralProperties();
_transform = new LayerTransformProperties(); _transform = new LayerTransformProperties();
LayerEffectsList = new List<BaseLayerEffect>();
_leds = new List<ArtemisLed>(); _leds = new List<ArtemisLed>();
ExpandedPropertyGroups = new List<string>();
Initialize(); Initialize();
Parent.AddChild(this); Parent.AddChild(this);
} }
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity) internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity) : base(parent.Profile)
{ {
LayerEntity = layerEntity; LayerEntity = layerEntity;
EntityId = layerEntity.Id; EntityId = layerEntity.Id;
@ -57,10 +55,8 @@ namespace Artemis.Core
Parent = parent; Parent = parent;
_general = new LayerGeneralProperties(); _general = new LayerGeneralProperties();
_transform = new LayerTransformProperties(); _transform = new LayerTransformProperties();
LayerEffectsList = new List<BaseLayerEffect>();
_leds = new List<ArtemisLed>(); _leds = new List<ArtemisLed>();
ExpandedPropertyGroups = new List<string>();
Load(); Load();
Initialize(); Initialize();
@ -263,17 +259,12 @@ namespace Artemis.Core
private void ApplyShapeType() private void ApplyShapeType()
{ {
switch (General.ShapeType.CurrentValue) LayerShape = General.ShapeType.CurrentValue switch
{ {
case LayerShapeType.Ellipse: LayerShapeType.Ellipse => new EllipseShape(this),
LayerShape = new EllipseShape(this); LayerShapeType.Rectangle => new RectangleShape(this),
break; _ => throw new ArgumentOutOfRangeException()
case LayerShapeType.Rectangle: };
LayerShape = new RectangleShape(this);
break;
default:
throw new ArgumentOutOfRangeException();
}
} }
#endregion #endregion

View File

@ -25,7 +25,13 @@ namespace Artemis.Core
/// </summary> /// </summary>
protected LayerProperty() protected LayerProperty()
{ {
// Cant define generic types as nullable ¯\_(ツ)_/¯ // These are set right after construction to keep the constructor (and inherited constructs) clean
ProfileElement = null!;
LayerPropertyGroup = null!;
Path = null!;
Entity = null!;
PropertyDescription = null!;
CurrentValue = default!; CurrentValue = default!;
DefaultValue = default!; DefaultValue = default!;

View File

@ -5,7 +5,6 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using Artemis.Core.LayerBrushes; using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects; using Artemis.Core.LayerEffects;
using Artemis.Core.Properties;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Humanizer; using Humanizer;
@ -30,6 +29,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
protected LayerPropertyGroup() protected LayerPropertyGroup()
{ {
// These are set right after construction to keep the constructor (and inherited constructs) clean
GroupDescription = null!;
Feature = null!;
ProfileElement = null!;
Path = null!;
_layerProperties = new List<ILayerProperty>(); _layerProperties = new List<ILayerProperty>();
_layerPropertyGroups = new List<LayerPropertyGroup>(); _layerPropertyGroups = new List<LayerPropertyGroup>();
} }
@ -52,7 +57,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// The parent group of this group /// The parent group of this group
/// </summary> /// </summary>
public LayerPropertyGroup Parent { get; internal set; } public LayerPropertyGroup? Parent { get; internal set; }
/// <summary> /// <summary>
/// The path of this property group /// The path of this property group
@ -67,12 +72,12 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// The layer brush this property group belongs to /// The layer brush this property group belongs to
/// </summary> /// </summary>
public BaseLayerBrush LayerBrush { get; internal set; } public BaseLayerBrush? LayerBrush { get; internal set; }
/// <summary> /// <summary>
/// The layer effect this property group belongs to /// The layer effect this property group belongs to
/// </summary> /// </summary>
public BaseLayerEffect LayerEffect { get; internal set; } public BaseLayerEffect? LayerEffect { get; internal set; }
/// <summary> /// <summary>
/// Gets or sets whether the property is hidden in the UI /// Gets or sets whether the property is hidden in the UI
@ -139,19 +144,16 @@ namespace Artemis.Core
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty); PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
} }
internal void Initialize(RenderProfileElement profileElement, [NotNull] string path, PluginFeature feature) internal void Initialize(RenderProfileElement profileElement, string path, PluginFeature feature)
{ {
if (path == null) if (path == null) throw new ArgumentNullException(nameof(path));
throw new ArgumentNullException(nameof(path));
if (feature == null)
throw new ArgumentNullException(nameof(feature));
// Doubt this will happen but let's make sure // Doubt this will happen but let's make sure
if (PropertiesInitialized) if (PropertiesInitialized)
throw new ArtemisCoreException("Layer property group already initialized, wut"); throw new ArtemisCoreException("Layer property group already initialized, wut");
Feature = feature; Feature = feature ?? throw new ArgumentNullException(nameof(feature));
ProfileElement = profileElement; ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
Path = path.TrimEnd('.'); Path = path.TrimEnd('.');
// Get all properties with a PropertyDescriptionAttribute // Get all properties with a PropertyDescriptionAttribute
@ -209,8 +211,7 @@ namespace Artemis.Core
if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType)) if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path}"); throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path}");
ILayerProperty instance = (ILayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true); if (!(Activator.CreateInstance(propertyInfo.PropertyType, true) is ILayerProperty instance))
if (instance == null)
throw new ArtemisPluginException($"Failed to create instance of layer property at {path}"); throw new ArtemisPluginException($"Failed to create instance of layer property at {path}");
// Ensure the description has a name, if not this is a good point to set it based on the property info // Ensure the description has a name, if not this is a good point to set it based on the property info
@ -230,8 +231,7 @@ namespace Artemis.Core
if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType)) if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup"); throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup");
LayerPropertyGroup instance = (LayerPropertyGroup) Activator.CreateInstance(propertyInfo.PropertyType); if (!(Activator.CreateInstance(propertyInfo.PropertyType) is LayerPropertyGroup instance))
if (instance == null)
throw new ArtemisPluginException($"Failed to create instance of layer property group at {path + propertyInfo.Name}"); throw new ArtemisPluginException($"Failed to create instance of layer property group at {path + propertyInfo.Name}");
// Ensure the description has a name, if not this is a good point to set it based on the property info // Ensure the description has a name, if not this is a good point to set it based on the property info
@ -250,7 +250,7 @@ namespace Artemis.Core
private PropertyEntity GetPropertyEntity(RenderProfileElement profileElement, string path, out bool fromStorage) private PropertyEntity GetPropertyEntity(RenderProfileElement profileElement, string path, out bool fromStorage)
{ {
PropertyEntity entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.FeatureId == Feature.Id && p.Path == path); PropertyEntity? entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.FeatureId == Feature.Id && p.Path == path);
fromStorage = entity != null; fromStorage = entity != null;
if (entity == null) if (entity == null)
{ {
@ -298,18 +298,18 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Occurs when the property group has initialized all its children /// Occurs when the property group has initialized all its children
/// </summary> /// </summary>
public event EventHandler PropertyGroupInitialized; public event EventHandler? PropertyGroupInitialized;
/// <summary> /// <summary>
/// Occurs when one of the current value of one of the layer properties in this group changes by some form of input /// Occurs when one of the current value of one of the layer properties in this group changes by some form of input
/// <para>Note: Will not trigger on properties in child groups</para> /// <para>Note: Will not trigger on properties in child groups</para>
/// </summary> /// </summary>
public event EventHandler<LayerPropertyEventArgs> LayerPropertyOnCurrentValueSet; public event EventHandler<LayerPropertyEventArgs>? LayerPropertyOnCurrentValueSet;
/// <summary> /// <summary>
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated /// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
/// </summary> /// </summary>
public event EventHandler VisibilityChanged; public event EventHandler? VisibilityChanged;
internal virtual void OnVisibilityChanged() internal virtual void OnVisibilityChanged()
{ {

View File

@ -20,7 +20,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets a the path outlining the shape /// Gets a the path outlining the shape
/// </summary> /// </summary>
public SKPath Path { get; protected set; } public SKPath? Path { get; protected set; }
/// <summary> /// <summary>
/// Calculates the <see cref="Path" /> /// Calculates the <see cref="Path" />

View File

@ -15,7 +15,7 @@ namespace Artemis.Core
private bool _isActivated; private bool _isActivated;
private readonly object _lock = new object(); private readonly object _lock = new object();
internal Profile(ProfileModule module, string name) internal Profile(ProfileModule module, string name) : base(null!)
{ {
ProfileEntity = new ProfileEntity(); ProfileEntity = new ProfileEntity();
EntityId = Guid.NewGuid(); EntityId = Guid.NewGuid();
@ -30,7 +30,7 @@ namespace Artemis.Core
Save(); Save();
} }
internal Profile(ProfileModule module, ProfileEntity profileEntity) internal Profile(ProfileModule module, ProfileEntity profileEntity) : base(null!)
{ {
Profile = this; Profile = this;
ProfileEntity = profileEntity; ProfileEntity = profileEntity;
@ -149,7 +149,7 @@ namespace Artemis.Core
ChildrenList.Clear(); ChildrenList.Clear();
// Populate the profile starting at the root, the rest is populated recursively // Populate the profile starting at the root, the rest is populated recursively
FolderEntity rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId); FolderEntity? rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId);
if (rootFolder == null) if (rootFolder == null)
{ {
Folder _ = new Folder(this, "Root folder"); Folder _ = new Folder(this, "Root folder");

View File

@ -13,7 +13,7 @@ namespace Artemis.Core
{ {
private bool _enabled; private bool _enabled;
private Guid _entityId; private Guid _entityId;
private string _name; private string? _name;
private int _order; private int _order;
private ProfileElement? _parent; private ProfileElement? _parent;
private Profile _profile; private Profile _profile;
@ -21,8 +21,9 @@ namespace Artemis.Core
internal List<ProfileElement> ChildrenList; internal List<ProfileElement> ChildrenList;
internal bool Disposed; internal bool Disposed;
internal ProfileElement() internal ProfileElement(Profile profile)
{ {
_profile = profile;
ChildrenList = new List<ProfileElement>(); ChildrenList = new List<ProfileElement>();
} }
@ -56,7 +57,16 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// The element's children /// The element's children
/// </summary> /// </summary>
public ReadOnlyCollection<ProfileElement> Children => ChildrenList.AsReadOnly(); public ReadOnlyCollection<ProfileElement> Children
{
get
{
lock (ChildrenList)
{
return ChildrenList.AsReadOnly();
}
}
}
/// <summary> /// <summary>
/// The order in which this element appears in the update loop and editor /// The order in which this element appears in the update loop and editor
@ -70,7 +80,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// The name which appears in the editor /// The name which appears in the editor
/// </summary> /// </summary>
public string Name public string? Name
{ {
get => _name; get => _name;
set => SetAndNotify(ref _name, value); set => SetAndNotify(ref _name, value);

View File

@ -16,10 +16,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
public abstract class RenderProfileElement : ProfileElement public abstract class RenderProfileElement : ProfileElement
{ {
internal RenderProfileElement() internal RenderProfileElement(Profile profile) : base(profile)
{ {
Timeline = new Timeline(); Timeline = new Timeline();
Renderer = new Renderer(); Renderer = new Renderer();
ExpandedPropertyGroups = new List<string>();
LayerEffectsList = new List<BaseLayerEffect>();
LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded; LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded;
LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved; LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved;
@ -68,7 +70,7 @@ namespace Artemis.Core
LayerEffectEntity layerEffectEntity = new LayerEffectEntity LayerEffectEntity layerEffectEntity = new LayerEffectEntity
{ {
Id = layerEffect.EntityId, Id = layerEffect.EntityId,
ProviderId = layerEffect.Descriptor.PlaceholderFor ?? layerEffect.ProviderId, ProviderId = layerEffect.Descriptor?.PlaceholderFor ?? layerEffect.ProviderId,
EffectType = layerEffect.GetEffectTypeName(), EffectType = layerEffect.GetEffectTypeName(),
Name = layerEffect.Name, Name = layerEffect.Name,
Enabled = layerEffect.Enabled, Enabled = layerEffect.Enabled,
@ -76,7 +78,7 @@ namespace Artemis.Core
Order = layerEffect.Order Order = layerEffect.Order
}; };
RenderElementEntity.LayerEffects.Add(layerEffectEntity); RenderElementEntity.LayerEffects.Add(layerEffectEntity);
layerEffect.BaseProperties.ApplyToEntity(); layerEffect.BaseProperties?.ApplyToEntity();
} }
// Conditions // Conditions
@ -290,8 +292,7 @@ namespace Artemis.Core
private void LayerEffectStoreOnLayerEffectRemoved(object? sender, LayerEffectStoreEvent e) private void LayerEffectStoreOnLayerEffectRemoved(object? sender, LayerEffectStoreEvent e)
{ {
// If effects provided by the plugin are on the element, replace them with placeholders // If effects provided by the plugin are on the element, replace them with placeholders
List<BaseLayerEffect> pluginEffects = LayerEffectsList.Where(ef => ef.Descriptor.Provider != null && List<BaseLayerEffect> pluginEffects = LayerEffectsList.Where(ef => ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
foreach (BaseLayerEffect pluginEffect in pluginEffects) foreach (BaseLayerEffect pluginEffect in pluginEffects)
{ {
LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.EntityId); LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.EntityId);
@ -323,13 +324,13 @@ namespace Artemis.Core
protected set => SetAndNotify(ref _displayConditionMet, value); protected set => SetAndNotify(ref _displayConditionMet, value);
} }
private DataModelConditionGroup _displayCondition; private DataModelConditionGroup? _displayCondition;
private bool _displayConditionMet; private bool _displayConditionMet;
/// <summary> /// <summary>
/// Gets or sets the root display condition group /// Gets or sets the root display condition group
/// </summary> /// </summary>
public DataModelConditionGroup DisplayCondition public DataModelConditionGroup? DisplayCondition
{ {
get => _displayCondition; get => _displayCondition;
set => SetAndNotify(ref _displayCondition, value); set => SetAndNotify(ref _displayCondition, value);

View File

@ -26,7 +26,7 @@ namespace Artemis.Core
if (IsOpen) if (IsOpen)
throw new ArtemisCoreException("Cannot open render context because it is already open"); throw new ArtemisCoreException("Cannot open render context because it is already open");
if (!_valid) if (!_valid || Canvas == null)
{ {
SKRect pathBounds = path.Bounds; SKRect pathBounds = path.Bounds;
int width = (int) pathBounds.Width; int width = (int) pathBounds.Width;
@ -59,7 +59,7 @@ namespace Artemis.Core
if (_disposed) if (_disposed)
throw new ObjectDisposedException("Renderer"); throw new ObjectDisposedException("Renderer");
Canvas.Restore(); Canvas?.Restore();
Paint?.Dispose(); Paint?.Dispose();
Paint = null; Paint = null;

View File

@ -14,7 +14,7 @@ namespace Artemis.Core
public class ArtemisDevice : CorePropertyChanged public class ArtemisDevice : CorePropertyChanged
{ {
private ReadOnlyCollection<ArtemisLed> _leds; private ReadOnlyCollection<ArtemisLed> _leds;
private SKPath _renderPath; private SKPath? _renderPath;
private SKRect _renderRectangle; private SKRect _renderRectangle;
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface) internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface)
@ -23,7 +23,7 @@ namespace Artemis.Core
DeviceProvider = deviceProvider; DeviceProvider = deviceProvider;
Surface = surface; Surface = surface;
DeviceEntity = new DeviceEntity(); DeviceEntity = new DeviceEntity();
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
Rotation = 0; Rotation = 0;
Scale = 1; Scale = 1;
@ -39,7 +39,7 @@ namespace Artemis.Core
DeviceProvider = deviceProvider; DeviceProvider = deviceProvider;
Surface = surface; Surface = surface;
DeviceEntity = deviceEntity; DeviceEntity = deviceEntity;
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
} }
/// <summary> /// <summary>
@ -54,7 +54,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the path surrounding the device, sized to match the render scale /// Gets the path surrounding the device, sized to match the render scale
/// </summary> /// </summary>
public SKPath RenderPath public SKPath? RenderPath
{ {
get => _renderPath; get => _renderPath;
private set => SetAndNotify(ref _renderPath, value); private set => SetAndNotify(ref _renderPath, value);

View File

@ -22,7 +22,7 @@ namespace Artemis.Core.Ninject
protected override ILogger CreateInstance(IContext context) protected override ILogger CreateInstance(IContext context)
{ {
Type requestingType = context.Request.ParentContext?.Plan?.Type; Type? requestingType = context.Request.ParentContext?.Plan?.Type;
if (requestingType != null) if (requestingType != null)
return Logger.ForContext(requestingType); return Logger.ForContext(requestingType);
return Logger; return Logger;

View File

@ -11,32 +11,32 @@ namespace Artemis.Core.DataModelExpansions
/// <summary> /// <summary>
/// Gets or sets the user-friendly name for this property, shown in the UI. /// Gets or sets the user-friendly name for this property, shown in the UI.
/// </summary> /// </summary>
public string Name { get; set; } public string? Name { get; set; }
/// <summary> /// <summary>
/// Gets or sets the user-friendly description for this property, shown in the UI. /// Gets or sets the user-friendly description for this property, shown in the UI.
/// </summary> /// </summary>
public string Description { get; set; } public string? Description { get; set; }
/// <summary> /// <summary>
/// Gets or sets the an optional prefix to show before displaying elements in the UI. /// Gets or sets the an optional prefix to show before displaying elements in the UI.
/// </summary> /// </summary>
public string Prefix { get; set; } public string? Prefix { get; set; }
/// <summary> /// <summary>
/// Gets or sets an optional affix to show behind displaying elements in the UI. /// Gets or sets an optional affix to show behind displaying elements in the UI.
/// </summary> /// </summary>
public string Affix { get; set; } public string? Affix { get; set; }
/// <summary> /// <summary>
/// Gets or sets an optional maximum value, this value is not enforced but used for percentage calculations. /// Gets or sets an optional maximum value, this value is not enforced but used for percentage calculations.
/// </summary> /// </summary>
public object MaxValue { get; set; } public object? MaxValue { get; set; }
/// <summary> /// <summary>
/// Gets or sets an optional minimum value, this value is not enforced but used for percentage calculations. /// Gets or sets an optional minimum value, this value is not enforced but used for percentage calculations.
/// </summary> /// </summary>
public object MinValue { get; set; } public object? MinValue { get; set; }
/// <summary> /// <summary>
/// Gets or sets whether this property resets the max depth of the data model, defaults to true /// Gets or sets whether this property resets the max depth of the data model, defaults to true

View File

@ -15,6 +15,16 @@ namespace Artemis.Core.DataModelExpansions
{ {
private readonly Dictionary<string, DataModel> _dynamicDataModels = new Dictionary<string, DataModel>(); private readonly Dictionary<string, DataModel> _dynamicDataModels = new Dictionary<string, DataModel>();
/// <summary>
/// Creates a new instance of the <see cref="DataModel" /> class
/// </summary>
protected DataModel()
{
// These are both set right after construction to keep the constructor of inherited classes clean
Feature = null!;
DataModelDescription = null!;
}
/// <summary> /// <summary>
/// Gets the plugin feature this data model belongs to /// Gets the plugin feature this data model belongs to
/// </summary> /// </summary>
@ -60,7 +70,7 @@ namespace Artemis.Core.DataModelExpansions
/// <param name="key">The key of the child, must be unique to this data model</param> /// <param name="key">The key of the child, must be unique to this data model</param>
/// <param name="name">An optional name, if not provided the key will be used in a humanized form</param> /// <param name="name">An optional name, if not provided the key will be used in a humanized form</param>
/// <param name="description">An optional description</param> /// <param name="description">An optional description</param>
public T AddDynamicChild<T>(T dynamicDataModel, string key, string name = null, string description = null) where T : DataModel public T AddDynamicChild<T>(T dynamicDataModel, string key, string? name = null, string? description = null) where T : DataModel
{ {
if (dynamicDataModel == null) if (dynamicDataModel == null)
throw new ArgumentNullException(nameof(dynamicDataModel)); throw new ArgumentNullException(nameof(dynamicDataModel));
@ -140,7 +150,7 @@ namespace Artemis.Core.DataModelExpansions
/// <returns>If found, the dynamic data model otherwise <c>null</c></returns> /// <returns>If found, the dynamic data model otherwise <c>null</c></returns>
public T? DynamicChild<T>(string key) where T : DataModel public T? DynamicChild<T>(string key) where T : DataModel
{ {
_dynamicDataModels.TryGetValue(key, out DataModel value); _dynamicDataModels.TryGetValue(key, out DataModel? value);
return value as T; return value as T;
} }

View File

@ -16,7 +16,7 @@ namespace Artemis.Core.DataModelExpansions
/// </summary> /// </summary>
public T DataModel public T DataModel
{ {
get => (T) InternalDataModel; get => InternalDataModel as T ?? throw new InvalidOperationException("Internal datamodel does not match the type of the data model");
internal set => InternalDataModel = value; internal set => InternalDataModel = value;
} }
@ -49,11 +49,5 @@ namespace Artemis.Core.DataModelExpansions
DataModel.DataModelDescription = GetDataModelDescription(); DataModel.DataModelDescription = GetDataModelDescription();
base.InternalEnable(); base.InternalEnable();
} }
internal override void InternalDisable()
{
DataModel = null;
base.InternalDisable();
}
} }
} }

View File

@ -31,7 +31,7 @@ namespace Artemis.Core.DeviceProviders
/// A logger used by the device provider internally, ignore this /// A logger used by the device provider internally, ignore this
/// </summary> /// </summary>
[Inject] [Inject]
public ILogger Logger { get; set; } public ILogger? Logger { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public override void Disable() public override void Disable()

View File

@ -10,10 +10,20 @@ namespace Artemis.Core.LayerBrushes
{ {
private LayerBrushType _brushType; private LayerBrushType _brushType;
private ILayerBrushConfigurationDialog? _configurationDialog; private ILayerBrushConfigurationDialog? _configurationDialog;
private LayerBrushDescriptor? _descriptor; private LayerBrushDescriptor _descriptor;
private Layer _layer; private Layer _layer;
private bool _supportsTransformation = true; private bool _supportsTransformation = true;
/// <summary>
/// Creates a new instance of the <see cref="BaseLayerBrush" /> class
/// </summary>
protected BaseLayerBrush()
{
// Both are set right after construction to keep the constructor of inherited classes clean
_layer = null!;
_descriptor = null!;
}
/// <summary> /// <summary>
/// Gets the layer this brush is applied to /// Gets the layer this brush is applied to
/// </summary> /// </summary>
@ -26,7 +36,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary> /// <summary>
/// Gets the descriptor of this brush /// Gets the descriptor of this brush
/// </summary> /// </summary>
public LayerBrushDescriptor? Descriptor public LayerBrushDescriptor Descriptor
{ {
get => _descriptor; get => _descriptor;
internal set => SetAndNotify(ref _descriptor, value); internal set => SetAndNotify(ref _descriptor, value);

View File

@ -7,7 +7,7 @@ namespace Artemis.Core.LayerBrushes
/// </summary> /// </summary>
public abstract class PropertiesLayerBrush<T> : BaseLayerBrush where T : LayerPropertyGroup public abstract class PropertiesLayerBrush<T> : BaseLayerBrush where T : LayerPropertyGroup
{ {
private T _properties; private T _properties = null!;
/// <summary> /// <summary>
/// Gets whether all properties on this brush are initialized /// Gets whether all properties on this brush are initialized
@ -35,7 +35,7 @@ namespace Artemis.Core.LayerBrushes
internal void InitializeProperties() internal void InitializeProperties()
{ {
Properties = Activator.CreateInstance<T>(); Properties = Activator.CreateInstance<T>();
Properties.GroupDescription ??= new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description}; Properties.GroupDescription = new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description};
Properties.LayerBrush = this; Properties.LayerBrush = this;
Properties.Initialize(Layer, "LayerBrush.", Descriptor.Provider); Properties.Initialize(Layer, "LayerBrush.", Descriptor.Provider);
PropertiesInitialized = true; PropertiesInitialized = true;

View File

@ -59,6 +59,7 @@ namespace Artemis.Core.LayerBrushes
/// </summary> /// </summary>
internal void CreateInstance(Layer layer) internal void CreateInstance(Layer layer)
{ {
if (layer == null) throw new ArgumentNullException(nameof(layer));
if (layer.LayerBrush != null) if (layer.LayerBrush != null)
throw new ArtemisCoreException("Layer already has an instantiated layer brush"); throw new ArtemisCoreException("Layer already has an instantiated layer brush");

View File

@ -20,6 +20,7 @@ namespace Artemis.Core.LayerBrushes
/// </summary> /// </summary>
protected RgbNetLayerBrush() protected RgbNetLayerBrush()
{ {
LedGroup = new ListLedGroup();
BrushType = LayerBrushType.RgbNet; BrushType = LayerBrushType.RgbNet;
SupportsTransformation = false; SupportsTransformation = false;
} }
@ -75,7 +76,7 @@ namespace Artemis.Core.LayerBrushes
// Not used in this effect type // Not used in this effect type
internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint) internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint)
{ {
throw new NotImplementedException("RGB.NET layer effectes do not implement InternalRender"); throw new NotImplementedException("RGB.NET layer effects do not implement InternalRender");
} }
private void LayerOnRenderPropertiesUpdated(object? sender, EventArgs e) private void LayerOnRenderPropertiesUpdated(object? sender, EventArgs e)

View File

@ -9,7 +9,7 @@ namespace Artemis.Core.LayerEffects
/// </summary> /// </summary>
public abstract class BaseLayerEffect : CorePropertyChanged, IDisposable public abstract class BaseLayerEffect : CorePropertyChanged, IDisposable
{ {
private ILayerEffectConfigurationDialog _configurationDialog; private ILayerEffectConfigurationDialog? _configurationDialog;
private LayerEffectDescriptor _descriptor; private LayerEffectDescriptor _descriptor;
private bool _enabled; private bool _enabled;
private Guid _entityId; private Guid _entityId;
@ -18,6 +18,15 @@ namespace Artemis.Core.LayerEffects
private int _order; private int _order;
private RenderProfileElement _profileElement; private RenderProfileElement _profileElement;
/// <inheritdoc />
protected BaseLayerEffect()
{
// These are set right after construction to keep the constructor of inherited classes clean
_profileElement = null!;
_descriptor = null!;
_name = null!;
}
/// <summary> /// <summary>
/// Gets the unique ID of this effect /// Gets the unique ID of this effect
/// </summary> /// </summary>
@ -76,7 +85,7 @@ namespace Artemis.Core.LayerEffects
/// <summary> /// <summary>
/// Gets the <see cref="LayerEffectDescriptor" /> that registered this effect /// Gets the <see cref="LayerEffectDescriptor" /> that registered this effect
/// </summary> /// </summary>
public LayerEffectDescriptor? Descriptor public LayerEffectDescriptor Descriptor
{ {
get => _descriptor; get => _descriptor;
internal set => SetAndNotify(ref _descriptor, value); internal set => SetAndNotify(ref _descriptor, value);
@ -85,7 +94,7 @@ namespace Artemis.Core.LayerEffects
/// <summary> /// <summary>
/// Gets or sets a configuration dialog complementing the regular properties /// Gets or sets a configuration dialog complementing the regular properties
/// </summary> /// </summary>
public ILayerEffectConfigurationDialog ConfigurationDialog public ILayerEffectConfigurationDialog? ConfigurationDialog
{ {
get => _configurationDialog; get => _configurationDialog;
protected set => SetAndNotify(ref _configurationDialog, value); protected set => SetAndNotify(ref _configurationDialog, value);
@ -94,12 +103,12 @@ namespace Artemis.Core.LayerEffects
/// <summary> /// <summary>
/// Gets the ID of the <see cref="LayerEffectProvider"/> that provided this effect /// Gets the ID of the <see cref="LayerEffectProvider"/> that provided this effect
/// </summary> /// </summary>
public string ProviderId => Descriptor?.Provider?.Id; public string ProviderId => Descriptor.Provider.Id;
/// <summary> /// <summary>
/// Gets a reference to the layer property group without knowing it's type /// Gets a reference to the layer property group without knowing it's type
/// </summary> /// </summary>
public virtual LayerPropertyGroup BaseProperties => null; public virtual LayerPropertyGroup? BaseProperties => null;
internal string PropertyRootPath => $"LayerEffect.{EntityId}.{GetType().Name}."; internal string PropertyRootPath => $"LayerEffect.{EntityId}.{GetType().Name}.";

View File

@ -8,7 +8,7 @@ namespace Artemis.Core.LayerEffects
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public abstract class LayerEffect<T> : BaseLayerEffect where T : LayerPropertyGroup public abstract class LayerEffect<T> : BaseLayerEffect where T : LayerPropertyGroup
{ {
private T _properties; private T _properties = null!;
/// <summary> /// <summary>
/// Gets whether all properties on this effect are initialized /// Gets whether all properties on this effect are initialized

View File

@ -11,7 +11,7 @@ namespace Artemis.Core.LayerEffects
/// </summary> /// </summary>
public class LayerEffectDescriptor public class LayerEffectDescriptor
{ {
internal LayerEffectDescriptor(string displayName, string description, string icon, Type layerEffectType, LayerEffectProvider provider) internal LayerEffectDescriptor(string displayName, string description, string icon, Type? layerEffectType, LayerEffectProvider provider)
{ {
DisplayName = displayName; DisplayName = displayName;
Description = description; Description = description;
@ -39,12 +39,12 @@ namespace Artemis.Core.LayerEffects
/// <summary> /// <summary>
/// The type of the layer effect /// The type of the layer effect
/// </summary> /// </summary>
public Type LayerEffectType { get; } public Type? LayerEffectType { get; }
/// <summary> /// <summary>
/// The plugin that provided this <see cref="LayerEffectDescriptor" /> /// The plugin that provided this <see cref="LayerEffectDescriptor" />
/// </summary> /// </summary>
public LayerEffectProvider? Provider { get; } public LayerEffectProvider Provider { get; }
/// <summary> /// <summary>
/// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder /// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder
@ -66,8 +66,8 @@ namespace Artemis.Core.LayerEffects
return; return;
} }
if (Provider == null) if (LayerEffectType == null)
throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its provider"); throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its LayerEffectType");
BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType); BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType);
effect.ProfileElement = renderElement; effect.ProfileElement = renderElement;
@ -85,7 +85,13 @@ namespace Artemis.Core.LayerEffects
private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity) private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
{ {
PlaceholderLayerEffect effect = new PlaceholderLayerEffect(entity, PlaceholderFor) {ProfileElement = renderElement, Descriptor = this}; if (PlaceholderFor == null)
throw new ArtemisCoreException("Cannot create a placeholder instance using a layer effect descriptor that is not a placeholder for anything");
PlaceholderLayerEffect effect = new PlaceholderLayerEffect(entity, PlaceholderFor)
{
ProfileElement = renderElement,
Descriptor = this
};
effect.Initialize(); effect.Initialize();
renderElement.ActivateLayerEffect(effect); renderElement.ActivateLayerEffect(effect);
} }

View File

@ -16,7 +16,7 @@ namespace Artemis.Core.Modules
/// </summary> /// </summary>
/// <param name="processName">The name of the process that must run</param> /// <param name="processName">The name of the process that must run</param>
/// <param name="location">The location of where the process must be running from (optional)</param> /// <param name="location">The location of where the process must be running from (optional)</param>
public ProcessActivationRequirement(string processName, string location = null) public ProcessActivationRequirement(string? processName, string? location = null)
{ {
ProcessName = processName; ProcessName = processName;
Location = location; Location = location;
@ -25,12 +25,12 @@ namespace Artemis.Core.Modules
/// <summary> /// <summary>
/// The name of the process that must run /// The name of the process that must run
/// </summary> /// </summary>
public string ProcessName { get; set; } public string? ProcessName { get; set; }
/// <summary> /// <summary>
/// The location of where the process must be running from /// The location of where the process must be running from
/// </summary> /// </summary>
public string Location { get; set; } public string? Location { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public bool Evaluate() public bool Evaluate()

View File

@ -18,7 +18,7 @@ namespace Artemis.Core.Modules
/// </summary> /// </summary>
public T DataModel public T DataModel
{ {
get => (T) InternalDataModel; get => InternalDataModel as T ?? throw new InvalidOperationException("Internal datamodel does not match the type of the data model");
internal set => InternalDataModel = value; internal set => InternalDataModel = value;
} }
@ -52,12 +52,6 @@ namespace Artemis.Core.Modules
DataModel.DataModelDescription = GetDataModelDescription(); DataModel.DataModelDescription = GetDataModelDescription();
base.InternalEnable(); base.InternalEnable();
} }
internal override void InternalDisable()
{
DataModel = null;
base.InternalDisable();
}
} }

View File

@ -6,12 +6,11 @@ namespace Artemis.Core.Modules
public class ModuleTab<T> : ModuleTab where T : IModuleViewModel public class ModuleTab<T> : ModuleTab where T : IModuleViewModel
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ModuleTab{T}" /> class /// Creates a new instance of the <see cref="ModuleTab{T}" /> class
/// </summary> /// </summary>
/// <param name="title">The title of the tab</param> /// <param name="title">The title of the tab</param>
public ModuleTab(string title) public ModuleTab(string title) : base(title)
{ {
Title = title;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -24,9 +23,13 @@ namespace Artemis.Core.Modules
public abstract class ModuleTab public abstract class ModuleTab
{ {
/// <summary> /// <summary>
/// The module this tab belongs to /// Creates a new instance of the <see cref="ModuleTab" /> class
/// </summary> /// </summary>
internal Module Module { get; set; } /// <param name="title">The title of the tab</param>
protected ModuleTab(string title)
{
Title = title;
}
/// <summary> /// <summary>
/// The title of the tab /// The title of the tab

View File

@ -22,7 +22,7 @@ namespace Artemis.Core.Modules
/// </summary> /// </summary>
public T DataModel public T DataModel
{ {
get => (T) InternalDataModel; get => InternalDataModel as T ?? throw new InvalidOperationException("Internal datamodel does not match the type of the data model");
internal set => InternalDataModel = value; internal set => InternalDataModel = value;
} }
@ -83,7 +83,6 @@ namespace Artemis.Core.Modules
{ {
Deactivate(true); Deactivate(true);
base.InternalDisable(); base.InternalDisable();
DataModel = null;
} }
} }
@ -182,7 +181,7 @@ namespace Artemis.Core.Modules
ProfileRendered(deltaTime, surface, canvas, canvasInfo); ProfileRendered(deltaTime, surface, canvas, canvasInfo);
} }
internal async Task ChangeActiveProfileAnimated(Profile profile, ArtemisSurface surface) internal async Task ChangeActiveProfileAnimated(Profile? profile, ArtemisSurface? surface)
{ {
if (profile != null && profile.Module != this) if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}."); throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
@ -204,12 +203,14 @@ namespace Artemis.Core.Modules
await Task.Delay(50); await Task.Delay(50);
} }
internal void ChangeActiveProfile(Profile profile, ArtemisSurface surface) internal void ChangeActiveProfile(Profile? profile, ArtemisSurface? surface)
{ {
if (profile != null && profile.Module != this) if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}."); throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
if (!IsActivated) if (!IsActivated)
throw new ArtemisCoreException("Cannot activate a profile on a deactivated module"); throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");
if (profile != null && surface == null)
throw new ArtemisCoreException("If changing the active profile to a non-null profile, a surface is required");
lock (_lock) lock (_lock)
{ {
@ -219,7 +220,7 @@ namespace Artemis.Core.Modules
ActiveProfile?.Dispose(); ActiveProfile?.Dispose();
ActiveProfile = profile; ActiveProfile = profile;
ActiveProfile?.Activate(surface); ActiveProfile?.Activate(surface!);
} }
OnActiveProfileChanged(); OnActiveProfileChanged();
@ -229,7 +230,7 @@ namespace Artemis.Core.Modules
{ {
base.Deactivate(isOverride); base.Deactivate(isOverride);
Profile profile = ActiveProfile; Profile? profile = ActiveProfile;
ActiveProfile = null; ActiveProfile = null;
profile?.Dispose(); profile?.Dispose();
} }
@ -249,7 +250,7 @@ namespace Artemis.Core.Modules
/// <summary> /// <summary>
/// Occurs when the <see cref="ActiveProfile" /> has changed /// Occurs when the <see cref="ActiveProfile" /> has changed
/// </summary> /// </summary>
public event EventHandler ActiveProfileChanged; public event EventHandler? ActiveProfileChanged;
/// <summary> /// <summary>
/// Invokes the <see cref="ActiveProfileChanged" /> event /// Invokes the <see cref="ActiveProfileChanged" /> event

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.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -19,10 +20,11 @@ namespace Artemis.Core
private bool _isEnabled; private bool _isEnabled;
internal Plugin(PluginInfo info, DirectoryInfo directory) internal Plugin(PluginInfo info, DirectoryInfo directory, PluginEntity? pluginEntity)
{ {
Info = info; Info = info;
Directory = directory; Directory = directory;
Entity = pluginEntity ?? new PluginEntity {Id = Guid, IsEnabled = true};
_features = new List<PluginFeature>(); _features = new List<PluginFeature>();
} }
@ -42,6 +44,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public DirectoryInfo Directory { get; } public DirectoryInfo Directory { get; }
/// <summary> /// <summary>
/// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins /// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins
/// </summary> /// </summary>
@ -91,7 +94,8 @@ namespace Artemis.Core
/// </summary> /// </summary>
/// <param name="path">The path to resolve</param> /// <param name="path">The path to resolve</param>
/// <returns>An absolute path pointing to the provided relative path</returns> /// <returns>An absolute path pointing to the provided relative path</returns>
public string? ResolveRelativePath(string path) [return: NotNullIfNotNull("path")]
public string? ResolveRelativePath(string? path)
{ {
return path == null ? null : Path.Combine(Directory.FullName, path); return path == null ? null : Path.Combine(Directory.FullName, path);
} }

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the plugin that provides this feature /// Gets the plugin that provides this feature
/// </summary> /// </summary>
public Plugin Plugin { get; internal set; } public Plugin Plugin { get; internal set; } = null!; // Will be set right after construction
/// <summary> /// <summary>
/// Gets whether the plugin is enabled /// Gets whether the plugin is enabled
@ -41,7 +41,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
public string Id => $"{GetType().FullName}-{Plugin.Guid.ToString().Substring(0, 8)}"; // Not as unique as a GUID but good enough and stays readable public string Id => $"{GetType().FullName}-{Plugin.Guid.ToString().Substring(0, 8)}"; // Not as unique as a GUID but good enough and stays readable
internal PluginFeatureEntity Entity { get; set; } internal PluginFeatureEntity Entity { get; set; } = null!; // Will be set right after construction
/// <summary> /// <summary>
/// Called when the feature is activated /// Called when the feature is activated

View File

@ -9,13 +9,13 @@ namespace Artemis.Core
[JsonObject(MemberSerialization.OptIn)] [JsonObject(MemberSerialization.OptIn)]
public class PluginInfo : CorePropertyChanged public class PluginInfo : CorePropertyChanged
{ {
private string _description; private string? _description;
private Guid _guid; private Guid _guid;
private string _icon; private string? _icon;
private string _main; private string _main = null!;
private string _name; private string _name = null!;
private Plugin _plugin; private Plugin _plugin = null!;
private Version _version; private Version _version = null!;
internal PluginInfo() internal PluginInfo()
{ {
@ -45,7 +45,7 @@ namespace Artemis.Core
/// A short description of the plugin /// A short description of the plugin
/// </summary> /// </summary>
[JsonProperty] [JsonProperty]
public string Description public string? Description
{ {
get => _description; get => _description;
set => SetAndNotify(ref _description, value); set => SetAndNotify(ref _description, value);
@ -57,7 +57,7 @@ namespace Artemis.Core
/// icons /// icons
/// </summary> /// </summary>
[JsonProperty] [JsonProperty]
public string Icon public string? Icon
{ {
get => _icon; get => _icon;
set => SetAndNotify(ref _icon, value); set => SetAndNotify(ref _icon, value);

View File

@ -1,4 +1,6 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using Artemis.Core.Properties;
using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces; using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -27,11 +29,11 @@ namespace Artemis.Core
Name = pluginSettingEntity.Name; Name = pluginSettingEntity.Name;
try try
{ {
Value = JsonConvert.DeserializeObject<T>(pluginSettingEntity.Value); _value = JsonConvert.DeserializeObject<T>(pluginSettingEntity.Value);
} }
catch (JsonReaderException) catch (JsonReaderException)
{ {
Value = default; _value = default!;
} }
} }
@ -43,6 +45,8 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// The value of the setting /// The value of the setting
/// </summary> /// </summary>
[AllowNull]
[CanBeNull]
public T Value public T Value
{ {
get => _value; get => _value;
@ -50,7 +54,7 @@ namespace Artemis.Core
{ {
if (Equals(_value, value)) return; if (Equals(_value, value)) return;
_value = value; _value = value!;
OnSettingChanged(); OnSettingChanged();
OnPropertyChanged(nameof(Value)); OnPropertyChanged(nameof(Value));
@ -94,12 +98,12 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Occurs when the value of the setting has been changed /// Occurs when the value of the setting has been changed
/// </summary> /// </summary>
public event EventHandler SettingChanged; public event EventHandler? SettingChanged;
/// <summary> /// <summary>
/// Occurs when the value of the setting has been saved /// Occurs when the value of the setting has been saved
/// </summary> /// </summary>
public event EventHandler SettingSaved; public event EventHandler? SettingSaved;
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()

View File

@ -11,7 +11,7 @@ namespace Artemis.Core
public class TimedUpdateRegistration : IDisposable public class TimedUpdateRegistration : IDisposable
{ {
private DateTime _lastEvent; private DateTime _lastEvent;
private Timer _timer; private Timer? _timer;
private bool _disposed; private bool _disposed;
private readonly object _lock = new object(); private readonly object _lock = new object();
@ -53,12 +53,12 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the action that gets called each time the update event fires /// Gets the action that gets called each time the update event fires
/// </summary> /// </summary>
public Action<double> Action { get; } public Action<double>? Action { get; }
/// <summary> /// <summary>
/// Gets the task that gets called each time the update event fires /// Gets the task that gets called each time the update event fires
/// </summary> /// </summary>
public Func<double, Task> AsyncAction { get; } public Func<double, Task>? AsyncAction { get; }
/// <summary> /// <summary>
/// Starts calling the <see cref="Action" /> or <see cref="AsyncAction"/> at the configured <see cref="Interval" /> /// Starts calling the <see cref="Action" /> or <see cref="AsyncAction"/> at the configured <see cref="Interval" />

View File

@ -22,9 +22,10 @@ SOFTWARE. */
using System; using System;
// ReSharper disable InheritdocConsiderUsage
#pragma warning disable 1591 #pragma warning disable 1591
#pragma warning disable 8618
// ReSharper disable InheritdocConsiderUsage
// ReSharper disable UnusedMember.Global // ReSharper disable UnusedMember.Global
// ReSharper disable MemberCanBePrivate.Global // ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable UnusedAutoPropertyAccessor.Global

View File

@ -60,7 +60,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the bitmap used to sample the brush /// Gets the bitmap used to sample the brush
/// </summary> /// </summary>
public SKBitmap Bitmap { get; private set; } public SKBitmap? Bitmap { get; private set; }
#endregion #endregion
@ -94,6 +94,9 @@ namespace Artemis.Core
private void TakeCenter(IEnumerable<BrushRenderTarget> renderTargets) private void TakeCenter(IEnumerable<BrushRenderTarget> renderTargets)
{ {
if (Bitmap == null)
return;
foreach (BrushRenderTarget renderTarget in renderTargets) foreach (BrushRenderTarget renderTarget in renderTargets)
{ {
Point scaledLocation = renderTarget.Point * Scale; Point scaledLocation = renderTarget.Point * Scale;
@ -104,6 +107,9 @@ namespace Artemis.Core
private void TakeSamples(IEnumerable<BrushRenderTarget> renderTargets) private void TakeSamples(IEnumerable<BrushRenderTarget> renderTargets)
{ {
if (Bitmap == null)
return;
int sampleSize = _sampleSizeSetting.Value; int sampleSize = _sampleSizeSetting.Value;
int sampleDepth = Math.Sqrt(sampleSize).RoundToInt(); int sampleDepth = Math.Sqrt(sampleSize).RoundToInt();

View File

@ -23,7 +23,7 @@ namespace Artemis.Core.Services
/// </summary> /// </summary>
internal class CoreService : ICoreService internal class CoreService : ICoreService
{ {
internal static IKernel Kernel; internal static IKernel Kernel = null!;
private readonly Stopwatch _frameStopWatch; private readonly Stopwatch _frameStopWatch;
private readonly ILogger _logger; private readonly ILogger _logger;
@ -32,10 +32,10 @@ namespace Artemis.Core.Services
private readonly IProfileService _profileService; private readonly IProfileService _profileService;
private readonly IRgbService _rgbService; private readonly IRgbService _rgbService;
private readonly ISurfaceService _surfaceService; private readonly ISurfaceService _surfaceService;
private List<BaseDataModelExpansion> _dataModelExpansions; private List<BaseDataModelExpansion> _dataModelExpansions = new List<BaseDataModelExpansion>();
private List<Module> _modules; private List<Module> _modules = new List<Module>();
// ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else // ReSharper disable UnusedParameter.Local - Storage migration and module service are injected early to ensure it runs before anything else
public CoreService(IKernel kernel, ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginManagementService pluginManagementService, public CoreService(IKernel kernel, ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginManagementService pluginManagementService,
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService) IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService)
{ {
@ -60,10 +60,11 @@ namespace Artemis.Core.Services
_pluginManagementService.PluginEnabled += (sender, args) => UpdatePluginCache(); _pluginManagementService.PluginEnabled += (sender, args) => UpdatePluginCache();
_pluginManagementService.PluginDisabled += (sender, args) => UpdatePluginCache(); _pluginManagementService.PluginDisabled += (sender, args) => UpdatePluginCache();
} }
// ReSharper restore UnusedParameter.Local
public TimeSpan FrameTime { get; private set; } public TimeSpan FrameTime { get; private set; }
public bool ModuleRenderingDisabled { get; set; } public bool ModuleRenderingDisabled { get; set; }
public List<string> StartupArguments { get; set; } public List<string>? StartupArguments { get; set; }
public void Dispose() public void Dispose()
{ {
@ -86,13 +87,10 @@ namespace Artemis.Core.Services
// Initialize the services // Initialize the services
_pluginManagementService.CopyBuiltInPlugins(); _pluginManagementService.CopyBuiltInPlugins();
_pluginManagementService.LoadPlugins(StartupArguments.Contains("--ignore-plugin-lock")); _pluginManagementService.LoadPlugins(StartupArguments != null && StartupArguments.Contains("--ignore-plugin-lock"));
ArtemisSurface surfaceConfig = _surfaceService.ActiveSurface; ArtemisSurface surfaceConfig = _surfaceService.ActiveSurface;
if (surfaceConfig != null) _logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId);
_logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId);
else
_logger.Information("Initialized without an active surface entity");
PlayIntroAnimation(); PlayIntroAnimation();
OnInitialized(); OnInitialized();
@ -121,7 +119,7 @@ namespace Artemis.Core.Services
FrameRendering += DrawOverlay; FrameRendering += DrawOverlay;
// Stop rendering after the profile finishes (take 1 second extra in case of slow updates) // Stop rendering after the profile finishes (take 1 second extra in case of slow updates)
TimeSpan introLength = intro.AnimationProfile.GetAllLayers().Max(l => l.Timeline.Length); TimeSpan introLength = intro.AnimationProfile.GetAllLayers().Max(l => l.Timeline.Length)!;
Task.Run(async () => Task.Run(async () =>
{ {
await Task.Delay(introLength.Add(TimeSpan.FromSeconds(1))); await Task.Delay(introLength.Add(TimeSpan.FromSeconds(1)));
@ -217,14 +215,14 @@ namespace Artemis.Core.Services
if (_rgbService.IsRenderPaused) if (_rgbService.IsRenderPaused)
return; return;
OnFrameRendered(new FrameRenderedEventArgs(_rgbService.BitmapBrush, _rgbService.Surface)); OnFrameRendered(new FrameRenderedEventArgs(_rgbService.BitmapBrush!, _rgbService.Surface));
} }
#region Events #region Events
public event EventHandler Initialized; public event EventHandler? Initialized;
public event EventHandler<FrameRenderingEventArgs> FrameRendering; public event EventHandler<FrameRenderingEventArgs>? FrameRendering;
public event EventHandler<FrameRenderedEventArgs> FrameRendered; public event EventHandler<FrameRenderedEventArgs>? FrameRendered;
private void OnInitialized() private void OnInitialized()
{ {

View File

@ -26,7 +26,7 @@ namespace Artemis.Core.Services
/// <summary> /// <summary>
/// Gets or sets a list of startup arguments /// Gets or sets a list of startup arguments
/// </summary> /// </summary>
List<string> StartupArguments { get; set; } List<string>? StartupArguments { get; set; }
/// <summary> /// <summary>
/// Initializes the core, only call once /// Initializes the core, only call once

View File

@ -12,13 +12,13 @@ namespace Artemis.Core.Services
/// Gets the current active module override. If set, all other modules are deactivated and only the /// Gets the current active module override. If set, all other modules are deactivated and only the
/// <see cref="ActiveModuleOverride" /> is active. /// <see cref="ActiveModuleOverride" /> is active.
/// </summary> /// </summary>
Module ActiveModuleOverride { get; } Module? ActiveModuleOverride { get; }
/// <summary> /// <summary>
/// Changes the current <see cref="ActiveModuleOverride" /> and deactivates all other modules /// Changes the current <see cref="ActiveModuleOverride" /> and deactivates all other modules
/// </summary> /// </summary>
/// <param name="overrideModule"></param> /// <param name="overrideModule"></param>
Task SetActiveModuleOverride(Module overrideModule); Task SetActiveModuleOverride(Module? overrideModule);
/// <summary> /// <summary>
/// Evaluates every enabled module's activation requirements and activates/deactivates modules accordingly /// Evaluates every enabled module's activation requirements and activates/deactivates modules accordingly

View File

@ -17,7 +17,7 @@ namespace Artemis.Core.Services
/// <summary> /// <summary>
/// Gets the bitmap brush used to convert the rendered frame to LED-colors /// Gets the bitmap brush used to convert the rendered frame to LED-colors
/// </summary> /// </summary>
BitmapBrush BitmapBrush { get; } BitmapBrush? BitmapBrush { get; }
/// <summary> /// <summary>
/// Gets the scale the frames are rendered on, a scale of 1.0 means 1 pixel = 1mm /// Gets the scale the frames are rendered on, a scale of 1.0 means 1 pixel = 1mm

View File

@ -149,9 +149,9 @@ namespace Artemis.Core.Services
UpdateModulePriority(module, category, priority); UpdateModulePriority(module, category, priority);
} }
public Module ActiveModuleOverride { get; private set; } public Module? ActiveModuleOverride { get; private set; }
public async Task SetActiveModuleOverride(Module overrideModule) public async Task SetActiveModuleOverride(Module? overrideModule)
{ {
try try
{ {

View File

@ -242,12 +242,10 @@ namespace Artemis.Core.Services
throw new ArtemisCoreException("Cannot load a plugin that is already loaded"); throw new ArtemisCoreException("Cannot load a plugin that is already loaded");
} }
Plugin plugin = new Plugin(pluginInfo, directory);
OnPluginLoading(new PluginEventArgs(plugin));
// Load the entity and fall back on creating a new one // Load the entity and fall back on creating a new one
plugin.Entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid) ?? new PluginEntity {Id = plugin.Guid, IsEnabled = true}; Plugin plugin = new Plugin(pluginInfo, directory, _pluginRepository.GetPluginByGuid(pluginInfo.Guid));
OnPluginLoading(new PluginEventArgs(plugin));
// Locate the main assembly entry // Locate the main assembly entry
string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main); string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main);
if (!File.Exists(mainFile)) if (!File.Exists(mainFile))

View File

@ -34,7 +34,7 @@ namespace Artemis.Core.Services
return ConditionOperatorStore.GetForType(type, side).Select(r => r.ConditionOperator).ToList(); return ConditionOperatorStore.GetForType(type, side).Select(r => r.ConditionOperator).ToList();
} }
public BaseConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType) public BaseConditionOperator? GetConditionOperator(Guid operatorPluginGuid, string operatorType)
{ {
return ConditionOperatorStore.Get(operatorPluginGuid, operatorType)?.ConditionOperator; return ConditionOperatorStore.Get(operatorPluginGuid, operatorType)?.ConditionOperator;
} }

View File

@ -34,7 +34,7 @@ namespace Artemis.Core.Services
return DataBindingModifierTypeStore.GetForType(type, part).Select(r => r.DataBindingModifierType).ToList(); return DataBindingModifierTypeStore.GetForType(type, part).Select(r => r.DataBindingModifierType).ToList();
} }
public BaseDataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType) public BaseDataBindingModifierType? GetModifierType(Guid modifierTypePluginGuid, string modifierType)
{ {
return DataBindingModifierTypeStore.Get(modifierTypePluginGuid, modifierType)?.DataBindingModifierType; return DataBindingModifierTypeStore.Get(modifierTypePluginGuid, modifierType)?.DataBindingModifierType;
} }

View File

@ -11,7 +11,7 @@ namespace Artemis.Core.Services
public DataModelService(IPluginManagementService pluginManagementService) public DataModelService(IPluginManagementService pluginManagementService)
{ {
// Add data models of already loaded plugins // Add data models of already loaded plugins
foreach (Module module in pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled)) foreach (Module module in pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled && p.InternalDataModel != null))
AddModuleDataModel(module); AddModuleDataModel(module);
foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetFeaturesOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled)) foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetFeaturesOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled))
AddDataModelExpansionDataModel(dataModelExpansion); AddDataModelExpansionDataModel(dataModelExpansion);
@ -40,9 +40,9 @@ namespace Artemis.Core.Services
return DataModelStore.GetAll().Select(d => d.DataModel).ToList(); return DataModelStore.GetAll().Select(d => d.DataModel).ToList();
} }
public T GetDataModel<T>() where T : DataModel public T? GetDataModel<T>() where T : DataModel
{ {
return (T) DataModelStore.GetAll().FirstOrDefault(d => d.DataModel is T)?.DataModel; return (T?) DataModelStore.GetAll().FirstOrDefault(d => d.DataModel is T)?.DataModel;
} }
public DataModel? GetPluginDataModel(PluginFeature pluginFeature) public DataModel? GetPluginDataModel(PluginFeature pluginFeature)
@ -61,8 +61,7 @@ namespace Artemis.Core.Services
private void AddModuleDataModel(Module module) private void AddModuleDataModel(Module module)
{ {
if (module.InternalDataModel == null) if (module.InternalDataModel == null)
return; throw new ArtemisCoreException("Cannot add module data model that is not enabled");
if (module.InternalDataModel.DataModelDescription == null) if (module.InternalDataModel.DataModelDescription == null)
throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null"); throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null");
@ -72,6 +71,8 @@ namespace Artemis.Core.Services
private void AddDataModelExpansionDataModel(BaseDataModelExpansion dataModelExpansion) private void AddDataModelExpansionDataModel(BaseDataModelExpansion dataModelExpansion)
{ {
if (dataModelExpansion.InternalDataModel == null)
throw new ArtemisCoreException("Cannot add data model expansion that is not enabled");
if (dataModelExpansion.InternalDataModel.DataModelDescription == null) if (dataModelExpansion.InternalDataModel.DataModelDescription == null)
throw new ArtemisPluginFeatureException(dataModelExpansion, "Data model expansion overrides GetDataModelDescription but returned null"); throw new ArtemisPluginFeatureException(dataModelExpansion, "Data model expansion overrides GetDataModelDescription but returned null");

View File

@ -32,6 +32,6 @@ namespace Artemis.Core.Services
/// </summary> /// </summary>
/// <param name="operatorPluginGuid">The operator's plugin GUID</param> /// <param name="operatorPluginGuid">The operator's plugin GUID</param>
/// <param name="operatorType">The type name of the operator</param> /// <param name="operatorType">The type name of the operator</param>
BaseConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType); BaseConditionOperator? GetConditionOperator(Guid operatorPluginGuid, string operatorType);
} }
} }

View File

@ -33,6 +33,6 @@ namespace Artemis.Core.Services
/// <param name="modifierTypePluginGuid">The modifier type's plugin GUID</param> /// <param name="modifierTypePluginGuid">The modifier type's plugin GUID</param>
/// <param name="modifierType">The type name of the modifier type</param> /// <param name="modifierType">The type name of the modifier type</param>
/// <returns></returns> /// <returns></returns>
BaseDataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType); BaseDataBindingModifierType? GetModifierType(Guid modifierTypePluginGuid, string modifierType);
} }
} }

View File

@ -29,7 +29,7 @@ namespace Artemis.Core.Services
/// If found, returns the registered data model of type <typeparamref name="T" /> /// If found, returns the registered data model of type <typeparamref name="T" />
/// </summary> /// </summary>
/// <typeparam name="T">The type of the data model to find</typeparam> /// <typeparam name="T">The type of the data model to find</typeparam>
T GetDataModel<T>() where T : DataModel; T? GetDataModel<T>() where T : DataModel;
/// <summary> /// <summary>
/// If found, returns the data model of the provided plugin /// If found, returns the data model of the provided plugin

View File

@ -34,7 +34,7 @@ namespace Artemis.Core.Services
return LayerBrushStore.GetAll().Select(r => r.LayerBrushDescriptor).ToList(); return LayerBrushStore.GetAll().Select(r => r.LayerBrushDescriptor).ToList();
} }
public LayerBrushDescriptor GetDefaultLayerBrush() public LayerBrushDescriptor? GetDefaultLayerBrush()
{ {
PluginSetting<LayerBrushReference> defaultReference = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference PluginSetting<LayerBrushReference> defaultReference = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
{ {
@ -42,6 +42,8 @@ namespace Artemis.Core.Services
BrushType = "ColorBrush" BrushType = "ColorBrush"
}); });
defaultReference.Value.LayerBrushProviderId ??= "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba";
defaultReference.Value.BrushType ??= "ColorBrush";
return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor; return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor;
} }
} }

View File

@ -17,7 +17,7 @@ namespace Artemis.Core.Services
private readonly PluginSetting<double> _renderScaleSetting; private readonly PluginSetting<double> _renderScaleSetting;
private readonly PluginSetting<int> _sampleSizeSetting; private readonly PluginSetting<int> _sampleSizeSetting;
private readonly PluginSetting<int> _targetFrameRateSetting; private readonly PluginSetting<int> _targetFrameRateSetting;
private ListLedGroup _surfaceLedGroup; private ListLedGroup? _surfaceLedGroup;
public RgbService(ILogger logger, ISettingsService settingsService) public RgbService(ILogger logger, ISettingsService settingsService)
{ {
@ -41,7 +41,7 @@ namespace Artemis.Core.Services
public RGBSurface Surface { get; set; } public RGBSurface Surface { get; set; }
public TimerUpdateTrigger UpdateTrigger { get; } public TimerUpdateTrigger UpdateTrigger { get; }
public BitmapBrush BitmapBrush { get; private set; } public BitmapBrush? BitmapBrush { get; private set; }
public IReadOnlyCollection<IRGBDevice> LoadedDevices => _loadedDevices.AsReadOnly(); public IReadOnlyCollection<IRGBDevice> LoadedDevices => _loadedDevices.AsReadOnly();
public double RenderScale => _renderScaleSetting.Value; public double RenderScale => _renderScaleSetting.Value;
public bool IsRenderPaused { get; set; } public bool IsRenderPaused { get; set; }
@ -109,7 +109,7 @@ namespace Artemis.Core.Services
public void UpdateSurfaceLedGroup() public void UpdateSurfaceLedGroup()
{ {
if (_surfaceLedGroup == null) if (_surfaceLedGroup == null || BitmapBrush == null)
{ {
// Apply the application wide brush and decorator // Apply the application wide brush and decorator
BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value), _sampleSizeSetting); BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value), _sampleSizeSetting);
@ -126,10 +126,6 @@ namespace Artemis.Core.Services
BitmapBrush.Scale = new Scale(_renderScaleSetting.Value); BitmapBrush.Scale = new Scale(_renderScaleSetting.Value);
_surfaceLedGroup = new ListLedGroup(Surface.Leds) {Brush = BitmapBrush}; _surfaceLedGroup = new ListLedGroup(Surface.Leds) {Brush = BitmapBrush};
} }
lock (BitmapBrush)
{
}
} }
private void OnDeviceLoaded(DeviceEventArgs e) private void OnDeviceLoaded(DeviceEventArgs e)

View File

@ -14,7 +14,7 @@ namespace Artemis.Core.Services
public PluginSetting<T> GetSetting<T>(string name, T defaultValue = default) public PluginSetting<T> GetSetting<T>(string name, T defaultValue = default)
{ {
return _pluginSettings.GetSetting(name, defaultValue); return _pluginSettings.GetSetting(name, defaultValue!);
} }
} }

View File

@ -36,6 +36,40 @@ namespace Artemis.Core.Services
public JsonSerializerSettings MementoSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All}; public JsonSerializerSettings MementoSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All};
public JsonSerializerSettings ExportSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All, Formatting = Formatting.Indented}; public JsonSerializerSettings ExportSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All, Formatting = Formatting.Indented};
public ProfileDescriptor? GetLastActiveProfile(ProfileModule module)
{
List<ProfileEntity> moduleProfiles = _profileRepository.GetByModuleId(module.Id);
if (!moduleProfiles.Any())
return CreateProfileDescriptor(module, "Default");
ProfileEntity? profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault();
return profileEntity == null ? null : new ProfileDescriptor(module, profileEntity);
}
private void SaveActiveProfile(ProfileModule module)
{
if (module.ActiveProfile == null)
return;
List<ProfileEntity> profileEntities = _profileRepository.GetByModuleId(module.Id);
foreach (ProfileEntity profileEntity in profileEntities)
{
profileEntity.IsActive = module.ActiveProfile.EntityId == profileEntity.Id;
_profileRepository.Save(profileEntity);
}
}
/// <summary>
/// Populates all missing LEDs on all currently active profiles
/// </summary>
/// <param name="surface"></param>
private void ActiveProfilesPopulateLeds(ArtemisSurface surface)
{
List<ProfileModule> profileModules = _pluginManagementService.GetFeaturesOfType<ProfileModule>();
foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList())
profileModule.ActiveProfile?.PopulateLeds(surface); // Avoid race condition
}
public List<ProfileDescriptor> GetProfileDescriptors(ProfileModule module) public List<ProfileDescriptor> GetProfileDescriptors(ProfileModule module)
{ {
List<ProfileEntity> profileEntities = _profileRepository.GetByModuleId(module.Id); List<ProfileEntity> profileEntities = _profileRepository.GetByModuleId(module.Id);
@ -52,14 +86,14 @@ namespace Artemis.Core.Services
public void ActivateLastProfile(ProfileModule profileModule) public void ActivateLastProfile(ProfileModule profileModule)
{ {
ProfileDescriptor activeProfile = GetLastActiveProfile(profileModule); ProfileDescriptor? activeProfile = GetLastActiveProfile(profileModule);
if (activeProfile != null) if (activeProfile != null)
ActivateProfile(activeProfile); ActivateProfile(activeProfile);
} }
public async Task ActivateLastProfileAnimated(ProfileModule profileModule) public async Task ActivateLastProfileAnimated(ProfileModule profileModule)
{ {
ProfileDescriptor activeProfile = GetLastActiveProfile(profileModule); ProfileDescriptor? activeProfile = GetLastActiveProfile(profileModule);
if (activeProfile != null) if (activeProfile != null)
await ActivateProfileAnimated(activeProfile); await ActivateProfileAnimated(activeProfile);
} }
@ -196,7 +230,8 @@ namespace Artemis.Core.Services
string top = profile.UndoStack.Pop(); string top = profile.UndoStack.Pop();
string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings); string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
profile.RedoStack.Push(memento); profile.RedoStack.Push(memento);
profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top, MementoSettings); profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top, MementoSettings)
?? throw new InvalidOperationException("Failed to deserialize memento");
profile.Load(); profile.Load();
InstantiateProfile(profile); InstantiateProfile(profile);
@ -220,7 +255,8 @@ namespace Artemis.Core.Services
string top = profile.RedoStack.Pop(); string top = profile.RedoStack.Pop();
string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings); string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
profile.UndoStack.Push(memento); profile.UndoStack.Push(memento);
profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top, MementoSettings); profile.ProfileEntity = JsonConvert.DeserializeObject<ProfileEntity>(top, MementoSettings)
?? throw new InvalidOperationException("Failed to deserialize memento");
profile.Load(); profile.Load();
InstantiateProfile(profile); InstantiateProfile(profile);
@ -246,7 +282,9 @@ namespace Artemis.Core.Services
public ProfileDescriptor ImportProfile(string json, ProfileModule profileModule) public ProfileDescriptor ImportProfile(string json, ProfileModule profileModule)
{ {
ProfileEntity profileEntity = JsonConvert.DeserializeObject<ProfileEntity>(json, ExportSettings); ProfileEntity? profileEntity = JsonConvert.DeserializeObject<ProfileEntity>(json, ExportSettings);
if (profileEntity == null)
throw new ArtemisCoreException("Failed to import profile but JSON.NET threw no error :(");
// Assign a new GUID to make sure it is unique in case of a previous import of the same content // Assign a new GUID to make sure it is unique in case of a previous import of the same content
profileEntity.UpdateGuid(Guid.NewGuid()); profileEntity.UpdateGuid(Guid.NewGuid());
@ -256,40 +294,6 @@ namespace Artemis.Core.Services
return new ProfileDescriptor(profileModule, profileEntity); return new ProfileDescriptor(profileModule, profileEntity);
} }
public ProfileDescriptor GetLastActiveProfile(ProfileModule module)
{
List<ProfileEntity> moduleProfiles = _profileRepository.GetByModuleId(module.Id);
if (!moduleProfiles.Any())
return CreateProfileDescriptor(module, "Default");
ProfileEntity profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault();
return profileEntity == null ? null : new ProfileDescriptor(module, profileEntity);
}
private void SaveActiveProfile(ProfileModule module)
{
if (module.ActiveProfile == null)
return;
List<ProfileEntity> profileEntities = _profileRepository.GetByModuleId(module.Id);
foreach (ProfileEntity profileEntity in profileEntities)
{
profileEntity.IsActive = module.ActiveProfile.EntityId == profileEntity.Id;
_profileRepository.Save(profileEntity);
}
}
/// <summary>
/// Populates all missing LEDs on all currently active profiles
/// </summary>
/// <param name="surface"></param>
private void ActiveProfilesPopulateLeds(ArtemisSurface surface)
{
List<ProfileModule> profileModules = _pluginManagementService.GetFeaturesOfType<ProfileModule>();
foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList())
profileModule.ActiveProfile.PopulateLeds(surface);
}
#region Event handlers #region Event handlers
private void OnActiveSurfaceConfigurationSelected(object? sender, SurfaceConfigurationEventArgs e) private void OnActiveSurfaceConfigurationSelected(object? sender, SurfaceConfigurationEventArgs e)

View File

@ -28,6 +28,8 @@ namespace Artemis.Core.Services
_surfaceConfigurations = new List<ArtemisSurface>(); _surfaceConfigurations = new List<ArtemisSurface>();
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.5); _renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.5);
// LoadFromRepository is guaranteed to set the ActiveSurface
ActiveSurface = null!;
LoadFromRepository(); LoadFromRepository();
_rgbService.DeviceLoaded += RgbServiceOnDeviceLoaded; _rgbService.DeviceLoaded += RgbServiceOnDeviceLoaded;
@ -61,6 +63,7 @@ namespace Artemis.Core.Services
public void SetActiveSurfaceConfiguration(ArtemisSurface surface) public void SetActiveSurfaceConfiguration(ArtemisSurface surface)
{ {
if (surface == null) throw new ArgumentNullException(nameof(surface));
if (ActiveSurface == surface) if (ActiveSurface == surface)
return; return;
@ -81,11 +84,8 @@ namespace Artemis.Core.Services
} }
// Apply the active surface entity to the devices // Apply the active surface entity to the devices
if (ActiveSurface != null) foreach (ArtemisDevice device in ActiveSurface.Devices)
{ device.ApplyToRgbDevice();
foreach (ArtemisDevice device in ActiveSurface.Devices)
device.ApplyToRgbDevice();
}
// Update the RGB service's graphics decorator to work with the new surface entity // Update the RGB service's graphics decorator to work with the new surface entity
_rgbService.UpdateSurfaceLedGroup(); _rgbService.UpdateSurfaceLedGroup();
@ -134,7 +134,7 @@ namespace Artemis.Core.Services
ArtemisSurface surfaceConfiguration = new ArtemisSurface(_rgbService.Surface, surfaceEntity, _renderScaleSetting.Value); ArtemisSurface surfaceConfiguration = new ArtemisSurface(_rgbService.Surface, surfaceEntity, _renderScaleSetting.Value);
foreach (DeviceEntity position in surfaceEntity.DeviceEntities) foreach (DeviceEntity position in surfaceEntity.DeviceEntities)
{ {
IRGBDevice device = _rgbService.Surface.Devices.FirstOrDefault(d => d.GetDeviceIdentifier() == position.DeviceIdentifier); IRGBDevice? device = _rgbService.Surface.Devices.FirstOrDefault(d => d.GetDeviceIdentifier() == position.DeviceIdentifier);
if (device != null) if (device != null)
{ {
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(device); DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(device);
@ -150,7 +150,7 @@ namespace Artemis.Core.Services
} }
// When all surface configs are loaded, apply the active surface config // When all surface configs are loaded, apply the active surface config
ArtemisSurface active = SurfaceConfigurations.FirstOrDefault(c => c.IsActive); ArtemisSurface? active = SurfaceConfigurations.FirstOrDefault(c => c.IsActive);
if (active != null) if (active != null)
SetActiveSurfaceConfiguration(active); SetActiveSurfaceConfiguration(active);
else else
@ -170,13 +170,13 @@ namespace Artemis.Core.Services
private void AddDeviceIfMissing(IRGBDevice rgbDevice, ArtemisSurface surface) private void AddDeviceIfMissing(IRGBDevice rgbDevice, ArtemisSurface surface)
{ {
string deviceIdentifier = rgbDevice.GetDeviceIdentifier(); string deviceIdentifier = rgbDevice.GetDeviceIdentifier();
ArtemisDevice device = surface.Devices.FirstOrDefault(d => d.DeviceEntity.DeviceIdentifier == deviceIdentifier); ArtemisDevice? device = surface.Devices.FirstOrDefault(d => d.DeviceEntity.DeviceIdentifier == deviceIdentifier);
if (device != null) if (device != null)
return; return;
// Find an existing device config and use that // Find an existing device config and use that
DeviceEntity existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier); DeviceEntity? existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier);
if (existingDeviceConfig != null) if (existingDeviceConfig != null)
{ {
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice); DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
@ -225,8 +225,8 @@ namespace Artemis.Core.Services
#region Events #region Events
public event EventHandler<SurfaceConfigurationEventArgs> ActiveSurfaceConfigurationSelected; public event EventHandler<SurfaceConfigurationEventArgs>? ActiveSurfaceConfigurationSelected;
public event EventHandler<SurfaceConfigurationEventArgs> SurfaceConfigurationUpdated; public event EventHandler<SurfaceConfigurationEventArgs>? SurfaceConfigurationUpdated;
protected virtual void OnActiveSurfaceConfigurationChanged(SurfaceConfigurationEventArgs e) protected virtual void OnActiveSurfaceConfigurationChanged(SurfaceConfigurationEventArgs e)
{ {

View File

@ -10,6 +10,9 @@ namespace Artemis.Core
public static ConditionOperatorRegistration Add(BaseConditionOperator conditionOperator) public static ConditionOperatorRegistration Add(BaseConditionOperator conditionOperator)
{ {
if (conditionOperator.Plugin == null)
throw new ArtemisCoreException("Cannot add a condition operator to the store that is not related to a plugin");
ConditionOperatorRegistration registration; ConditionOperatorRegistration registration;
lock (Registrations) lock (Registrations)
{ {
@ -74,8 +77,8 @@ namespace Artemis.Core
#region Events #region Events
public static event EventHandler<ConditionOperatorStoreEvent> ConditionOperatorAdded; public static event EventHandler<ConditionOperatorStoreEvent>? ConditionOperatorAdded;
public static event EventHandler<ConditionOperatorStoreEvent> ConditionOperatorRemoved; public static event EventHandler<ConditionOperatorStoreEvent>? ConditionOperatorRemoved;
private static void OnConditionOperatorAdded(ConditionOperatorStoreEvent e) private static void OnConditionOperatorAdded(ConditionOperatorStoreEvent e)
{ {

View File

@ -57,8 +57,8 @@ namespace Artemis.Core
#region Events #region Events
public static event EventHandler<DataModelStoreEvent> DataModelAdded; public static event EventHandler<DataModelStoreEvent>? DataModelAdded;
public static event EventHandler<DataModelStoreEvent> DataModelRemoved; public static event EventHandler<DataModelStoreEvent>? DataModelRemoved;
private static void OnDataModelAdded(DataModelStoreEvent e) private static void OnDataModelAdded(DataModelStoreEvent e)
{ {

View File

@ -58,8 +58,8 @@ namespace Artemis.Core
#region Events #region Events
public static event EventHandler<LayerBrushStoreEvent> LayerBrushAdded; public static event EventHandler<LayerBrushStoreEvent>? LayerBrushAdded;
public static event EventHandler<LayerBrushStoreEvent> LayerBrushRemoved; public static event EventHandler<LayerBrushStoreEvent>? LayerBrushRemoved;
private static void OnLayerBrushAdded(LayerBrushStoreEvent e) private static void OnLayerBrushAdded(LayerBrushStoreEvent e)
{ {

View File

@ -51,14 +51,14 @@ namespace Artemis.Core
{ {
lock (Registrations) lock (Registrations)
{ {
return Registrations.FirstOrDefault(d => d.PluginFeature.Id == providerId && d.LayerEffectDescriptor.LayerEffectType.Name == typeName); return Registrations.FirstOrDefault(d => d.PluginFeature.Id == providerId && d.LayerEffectDescriptor.LayerEffectType?.Name == typeName);
} }
} }
#region Events #region Events
public static event EventHandler<LayerEffectStoreEvent> LayerEffectAdded; public static event EventHandler<LayerEffectStoreEvent>? LayerEffectAdded;
public static event EventHandler<LayerEffectStoreEvent> LayerEffectRemoved; public static event EventHandler<LayerEffectStoreEvent>? LayerEffectRemoved;
private static void OnLayerEffectAdded(LayerEffectStoreEvent e) private static void OnLayerEffectAdded(LayerEffectStoreEvent e)
{ {

View File

@ -23,7 +23,7 @@ namespace Artemis.Core
string arguments = "-Command \"& {Start-Sleep -s " + delay + "; (Get-Process 'Artemis.UI').kill()}"; string arguments = "-Command \"& {Start-Sleep -s " + delay + "; (Get-Process 'Artemis.UI').kill()}";
// If restart is required, start the executable again after the process was killed // If restart is required, start the executable again after the process was killed
if (restart) if (restart)
arguments = "-Command \"& {Start-Sleep -s " + delay + "; (Get-Process 'Artemis.UI').kill(); Start-Process -FilePath '" + Process.GetCurrentProcess().MainModule.FileName + "'}\""; arguments = "-Command \"& {Start-Sleep -s " + delay + "; (Get-Process 'Artemis.UI').kill(); Start-Process -FilePath '" + Process.GetCurrentProcess().MainModule!.FileName + "'}\"";
ProcessStartInfo info = new ProcessStartInfo ProcessStartInfo info = new ProcessStartInfo
{ {
@ -45,7 +45,7 @@ namespace Artemis.Core
/// </summary> /// </summary>
/// <param name="url">The URL to open</param> /// <param name="url">The URL to open</param>
/// <returns>The process created to open the URL</returns> /// <returns>The process created to open the URL</returns>
public static Process OpenUrl(string url) public static Process? OpenUrl(string url)
{ {
ProcessStartInfo processInfo = new ProcessStartInfo ProcessStartInfo processInfo = new ProcessStartInfo
{ {
@ -61,7 +61,7 @@ namespace Artemis.Core
/// <returns></returns> /// <returns></returns>
internal static string GetCurrentLocation() internal static string GetCurrentLocation()
{ {
return Process.GetCurrentProcess().MainModule.FileName; return Process.GetCurrentProcess().MainModule!.FileName!;
} }
#region Events #region Events

View File

@ -6,7 +6,7 @@ namespace Artemis.Core
{ {
internal static class DeserializationLogger internal static class DeserializationLogger
{ {
private static ILogger _logger; private static ILogger? _logger;
public static void Initialize(IKernel kernel) public static void Initialize(IKernel kernel)
{ {
@ -15,7 +15,7 @@ namespace Artemis.Core
public static void LogPredicateDeserializationFailure(DataModelConditionPredicate dataModelConditionPredicate, JsonException exception) public static void LogPredicateDeserializationFailure(DataModelConditionPredicate dataModelConditionPredicate, JsonException exception)
{ {
_logger.Warning( _logger?.Warning(
exception, exception,
"Failed to deserialize display condition predicate {left} {operator} {right}", "Failed to deserialize display condition predicate {left} {operator} {right}",
dataModelConditionPredicate.Entity.LeftPath?.Path, dataModelConditionPredicate.Entity.LeftPath?.Path,
@ -26,7 +26,7 @@ namespace Artemis.Core
public static void LogModifierDeserializationFailure(string modifierName, JsonSerializationException exception) public static void LogModifierDeserializationFailure(string modifierName, JsonSerializationException exception)
{ {
_logger.Warning(exception, "Failed to deserialize static parameter for modifier {modifierName}", modifierName); _logger?.Warning(exception, "Failed to deserialize static parameter for modifier {modifierName}", modifierName);
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Artemis.Core
{ {
// Create an expression that checks every part of the path for null // Create an expression that checks every part of the path for null
// In the same iteration, create the accessor // In the same iteration, create the accessor
Expression condition = null; Expression? condition = null;
foreach (string memberName in path.Split('.')) foreach (string memberName in path.Split('.'))
{ {
BinaryExpression notNull = Expression.NotEqual(source, Expression.Constant(null)); BinaryExpression notNull = Expression.NotEqual(source, Expression.Constant(null));

View File

@ -21,21 +21,19 @@ namespace Artemis.Core
_logger = logger; _logger = logger;
_profileService = profileService; _profileService = profileService;
_surfaceService = surfaceService; _surfaceService = surfaceService;
CreateIntroProfile();
AnimationProfile = CreateIntroProfile();
} }
public Profile AnimationProfile { get; set; } public Profile AnimationProfile { get; set; }
public void Render(double deltaTime, SKCanvas canvas) public void Render(double deltaTime, SKCanvas canvas)
{ {
if (AnimationProfile == null)
return;
AnimationProfile.Update(deltaTime); AnimationProfile.Update(deltaTime);
AnimationProfile.Render(canvas); AnimationProfile.Render(canvas);
} }
private void CreateIntroProfile() private Profile CreateIntroProfile()
{ {
try try
{ {
@ -44,24 +42,24 @@ namespace Artemis.Core
ProfileEntity profileEntity = JsonConvert.DeserializeObject<ProfileEntity>(json); ProfileEntity profileEntity = JsonConvert.DeserializeObject<ProfileEntity>(json);
// Inject every LED on the surface into each layer // Inject every LED on the surface into each layer
foreach (LayerEntity profileEntityLayer in profileEntity.Layers) foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
{
profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity
{ {
DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(), DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(),
LedName = l.RgbLed.Id.ToString() LedName = l.RgbLed.Id.ToString()
})); }));
}
Profile profile = new Profile(new DummyModule(), profileEntity); Profile profile = new Profile(new DummyModule(), profileEntity);
profile.Activate(_surfaceService.ActiveSurface); profile.Activate(_surfaceService.ActiveSurface);
_profileService.InstantiateProfile(profile); _profileService.InstantiateProfile(profile);
AnimationProfile = profile; return profile;
} }
catch (Exception e) catch (Exception e)
{ {
_logger.Warning(e, "Failed to load intro profile"); _logger.Warning(e, "Failed to load intro profile");
} }
return new Profile(new DummyModule(), "Intro");
} }
} }

View File

@ -10,11 +10,11 @@ namespace Artemis.Core
{ {
Type type = typeof(TSource); Type type = typeof(TSource);
MemberExpression member = propertyLambda.Body as MemberExpression; MemberExpression? member = propertyLambda.Body as MemberExpression;
if (member == null) if (member == null)
throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.", propertyLambda)); throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.", propertyLambda));
PropertyInfo propInfo = member.Member as PropertyInfo; PropertyInfo? propInfo = member.Member as PropertyInfo;
if (propInfo == null) if (propInfo == null)
throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.", propertyLambda)); throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.", propertyLambda));