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.Collections.Generic;
using System.IO;
using Artemis.Storage.Entities.Plugins;
namespace Artemis.Core
{
@ -34,13 +35,13 @@ namespace Artemis.Core
/// </summary>
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>
/// The plugin used by core components of Artemis
/// </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 EffectPlaceholderPlugin EffectPlaceholderPlugin = new EffectPlaceholderPlugin {Plugin = CorePlugin};

View File

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

View File

@ -20,7 +20,7 @@ namespace Artemis.Core
/// </summary>
/// <param name="parent">The parent 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();
EntityId = Guid.NewGuid();
@ -30,13 +30,10 @@ namespace Artemis.Core
Name = name;
Enabled = true;
LayerEffectsList = new List<BaseLayerEffect>();
ExpandedPropertyGroups = new List<string>();
Parent.AddChild(this);
}
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity)
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) : base(parent.Profile)
{
FolderEntity = folderEntity;
EntityId = folderEntity.Id;
@ -47,9 +44,6 @@ namespace Artemis.Core
Enabled = folderEntity.Enabled;
Order = folderEntity.Order;
LayerEffectsList = new List<BaseLayerEffect>();
ExpandedPropertyGroups = new List<string>();
Load();
}

View File

@ -28,7 +28,7 @@ namespace Artemis.Core
/// </summary>
/// <param name="parent">The parent 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();
EntityId = Guid.NewGuid();
@ -40,15 +40,13 @@ namespace Artemis.Core
_general = new LayerGeneralProperties();
_transform = new LayerTransformProperties();
LayerEffectsList = new List<BaseLayerEffect>();
_leds = new List<ArtemisLed>();
ExpandedPropertyGroups = new List<string>();
Initialize();
Parent.AddChild(this);
}
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity)
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity) : base(parent.Profile)
{
LayerEntity = layerEntity;
EntityId = layerEntity.Id;
@ -58,9 +56,7 @@ namespace Artemis.Core
_general = new LayerGeneralProperties();
_transform = new LayerTransformProperties();
LayerEffectsList = new List<BaseLayerEffect>();
_leds = new List<ArtemisLed>();
ExpandedPropertyGroups = new List<string>();
Load();
Initialize();
@ -263,17 +259,12 @@ namespace Artemis.Core
private void ApplyShapeType()
{
switch (General.ShapeType.CurrentValue)
LayerShape = General.ShapeType.CurrentValue switch
{
case LayerShapeType.Ellipse:
LayerShape = new EllipseShape(this);
break;
case LayerShapeType.Rectangle:
LayerShape = new RectangleShape(this);
break;
default:
throw new ArgumentOutOfRangeException();
}
LayerShapeType.Ellipse => new EllipseShape(this),
LayerShapeType.Rectangle => new RectangleShape(this),
_ => throw new ArgumentOutOfRangeException()
};
}
#endregion

View File

@ -25,7 +25,13 @@ namespace Artemis.Core
/// </summary>
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!;
DefaultValue = default!;

View File

@ -5,7 +5,6 @@ using System.Linq;
using System.Reflection;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Core.Properties;
using Artemis.Storage.Entities.Profile;
using Humanizer;
@ -30,6 +29,12 @@ namespace Artemis.Core
/// </summary>
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>();
_layerPropertyGroups = new List<LayerPropertyGroup>();
}
@ -52,7 +57,7 @@ namespace Artemis.Core
/// <summary>
/// The parent group of this group
/// </summary>
public LayerPropertyGroup Parent { get; internal set; }
public LayerPropertyGroup? Parent { get; internal set; }
/// <summary>
/// The path of this property group
@ -67,12 +72,12 @@ namespace Artemis.Core
/// <summary>
/// The layer brush this property group belongs to
/// </summary>
public BaseLayerBrush LayerBrush { get; internal set; }
public BaseLayerBrush? LayerBrush { get; internal set; }
/// <summary>
/// The layer effect this property group belongs to
/// </summary>
public BaseLayerEffect LayerEffect { get; internal set; }
public BaseLayerEffect? LayerEffect { get; internal set; }
/// <summary>
/// Gets or sets whether the property is hidden in the UI
@ -139,19 +144,16 @@ namespace Artemis.Core
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)
throw new ArgumentNullException(nameof(path));
if (feature == null)
throw new ArgumentNullException(nameof(feature));
if (path == null) throw new ArgumentNullException(nameof(path));
// Doubt this will happen but let's make sure
if (PropertiesInitialized)
throw new ArtemisCoreException("Layer property group already initialized, wut");
Feature = feature;
ProfileElement = profileElement;
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
Path = path.TrimEnd('.');
// Get all properties with a PropertyDescriptionAttribute
@ -209,8 +211,7 @@ namespace Artemis.Core
if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path}");
ILayerProperty instance = (ILayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true);
if (instance == null)
if (!(Activator.CreateInstance(propertyInfo.PropertyType, true) is ILayerProperty instance))
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
@ -230,8 +231,7 @@ namespace Artemis.Core
if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup");
LayerPropertyGroup instance = (LayerPropertyGroup) Activator.CreateInstance(propertyInfo.PropertyType);
if (instance == null)
if (!(Activator.CreateInstance(propertyInfo.PropertyType) is LayerPropertyGroup instance))
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
@ -250,7 +250,7 @@ namespace Artemis.Core
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;
if (entity == null)
{
@ -298,18 +298,18 @@ namespace Artemis.Core
/// <summary>
/// Occurs when the property group has initialized all its children
/// </summary>
public event EventHandler PropertyGroupInitialized;
public event EventHandler? PropertyGroupInitialized;
/// <summary>
/// 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>
/// </summary>
public event EventHandler<LayerPropertyEventArgs> LayerPropertyOnCurrentValueSet;
public event EventHandler<LayerPropertyEventArgs>? LayerPropertyOnCurrentValueSet;
/// <summary>
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
/// </summary>
public event EventHandler VisibilityChanged;
public event EventHandler? VisibilityChanged;
internal virtual void OnVisibilityChanged()
{

View File

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

View File

@ -15,7 +15,7 @@ namespace Artemis.Core
private bool _isActivated;
private readonly object _lock = new object();
internal Profile(ProfileModule module, string name)
internal Profile(ProfileModule module, string name) : base(null!)
{
ProfileEntity = new ProfileEntity();
EntityId = Guid.NewGuid();
@ -30,7 +30,7 @@ namespace Artemis.Core
Save();
}
internal Profile(ProfileModule module, ProfileEntity profileEntity)
internal Profile(ProfileModule module, ProfileEntity profileEntity) : base(null!)
{
Profile = this;
ProfileEntity = profileEntity;
@ -149,7 +149,7 @@ namespace Artemis.Core
ChildrenList.Clear();
// 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)
{
Folder _ = new Folder(this, "Root folder");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,32 +11,32 @@ namespace Artemis.Core.DataModelExpansions
/// <summary>
/// Gets or sets the user-friendly name for this property, shown in the UI.
/// </summary>
public string Name { get; set; }
public string? Name { get; set; }
/// <summary>
/// Gets or sets the user-friendly description for this property, shown in the UI.
/// </summary>
public string Description { get; set; }
public string? Description { get; set; }
/// <summary>
/// Gets or sets the an optional prefix to show before displaying elements in the UI.
/// </summary>
public string Prefix { get; set; }
public string? Prefix { get; set; }
/// <summary>
/// Gets or sets an optional affix to show behind displaying elements in the UI.
/// </summary>
public string Affix { get; set; }
public string? Affix { get; set; }
/// <summary>
/// Gets or sets an optional maximum value, this value is not enforced but used for percentage calculations.
/// </summary>
public object MaxValue { get; set; }
public object? MaxValue { get; set; }
/// <summary>
/// Gets or sets an optional minimum value, this value is not enforced but used for percentage calculations.
/// </summary>
public object MinValue { get; set; }
public object? MinValue { get; set; }
/// <summary>
/// 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>();
/// <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>
/// Gets the plugin feature this data model belongs to
/// </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="name">An optional name, if not provided the key will be used in a humanized form</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)
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>
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;
}

View File

@ -16,7 +16,7 @@ namespace Artemis.Core.DataModelExpansions
/// </summary>
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;
}
@ -49,11 +49,5 @@ namespace Artemis.Core.DataModelExpansions
DataModel.DataModelDescription = GetDataModelDescription();
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
/// </summary>
[Inject]
public ILogger Logger { get; set; }
public ILogger? Logger { get; set; }
/// <inheritdoc />
public override void Disable()

View File

@ -10,10 +10,20 @@ namespace Artemis.Core.LayerBrushes
{
private LayerBrushType _brushType;
private ILayerBrushConfigurationDialog? _configurationDialog;
private LayerBrushDescriptor? _descriptor;
private LayerBrushDescriptor _descriptor;
private Layer _layer;
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>
/// Gets the layer this brush is applied to
/// </summary>
@ -26,7 +36,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary>
/// Gets the descriptor of this brush
/// </summary>
public LayerBrushDescriptor? Descriptor
public LayerBrushDescriptor Descriptor
{
get => _descriptor;
internal set => SetAndNotify(ref _descriptor, value);

View File

@ -7,7 +7,7 @@ namespace Artemis.Core.LayerBrushes
/// </summary>
public abstract class PropertiesLayerBrush<T> : BaseLayerBrush where T : LayerPropertyGroup
{
private T _properties;
private T _properties = null!;
/// <summary>
/// Gets whether all properties on this brush are initialized
@ -35,7 +35,7 @@ namespace Artemis.Core.LayerBrushes
internal void InitializeProperties()
{
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.Initialize(Layer, "LayerBrush.", Descriptor.Provider);
PropertiesInitialized = true;

View File

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

View File

@ -20,6 +20,7 @@ namespace Artemis.Core.LayerBrushes
/// </summary>
protected RgbNetLayerBrush()
{
LedGroup = new ListLedGroup();
BrushType = LayerBrushType.RgbNet;
SupportsTransformation = false;
}
@ -75,7 +76,7 @@ namespace Artemis.Core.LayerBrushes
// Not used in this effect type
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)

View File

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

View File

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

View File

@ -11,7 +11,7 @@ namespace Artemis.Core.LayerEffects
/// </summary>
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;
Description = description;
@ -39,12 +39,12 @@ namespace Artemis.Core.LayerEffects
/// <summary>
/// The type of the layer effect
/// </summary>
public Type LayerEffectType { get; }
public Type? LayerEffectType { get; }
/// <summary>
/// The plugin that provided this <see cref="LayerEffectDescriptor" />
/// </summary>
public LayerEffectProvider? Provider { get; }
public LayerEffectProvider Provider { get; }
/// <summary>
/// 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;
}
if (Provider == 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");
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 LayerEffectType");
BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType);
effect.ProfileElement = renderElement;
@ -85,7 +85,13 @@ namespace Artemis.Core.LayerEffects
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();
renderElement.ActivateLayerEffect(effect);
}

View File

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

View File

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

View File

@ -22,7 +22,7 @@ namespace Artemis.Core.Modules
/// </summary>
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;
}
@ -83,7 +83,6 @@ namespace Artemis.Core.Modules
{
Deactivate(true);
base.InternalDisable();
DataModel = null;
}
}
@ -182,7 +181,7 @@ namespace Artemis.Core.Modules
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)
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);
}
internal void ChangeActiveProfile(Profile profile, ArtemisSurface surface)
internal void ChangeActiveProfile(Profile? profile, ArtemisSurface? surface)
{
if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
if (!IsActivated)
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)
{
@ -219,7 +220,7 @@ namespace Artemis.Core.Modules
ActiveProfile?.Dispose();
ActiveProfile = profile;
ActiveProfile?.Activate(surface);
ActiveProfile?.Activate(surface!);
}
OnActiveProfileChanged();
@ -229,7 +230,7 @@ namespace Artemis.Core.Modules
{
base.Deactivate(isOverride);
Profile profile = ActiveProfile;
Profile? profile = ActiveProfile;
ActiveProfile = null;
profile?.Dispose();
}
@ -249,7 +250,7 @@ namespace Artemis.Core.Modules
/// <summary>
/// Occurs when the <see cref="ActiveProfile" /> has changed
/// </summary>
public event EventHandler ActiveProfileChanged;
public event EventHandler? ActiveProfileChanged;
/// <summary>
/// Invokes the <see cref="ActiveProfileChanged" /> event

View File

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

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
/// <summary>
/// Gets the plugin that provides this feature
/// </summary>
public Plugin Plugin { get; internal set; }
public Plugin Plugin { get; internal set; } = null!; // Will be set right after construction
/// <summary>
/// Gets whether the plugin is enabled
@ -41,7 +41,7 @@ namespace Artemis.Core
/// </summary>
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>
/// Called when the feature is activated

View File

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

View File

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

View File

@ -11,7 +11,7 @@ namespace Artemis.Core
public class TimedUpdateRegistration : IDisposable
{
private DateTime _lastEvent;
private Timer _timer;
private Timer? _timer;
private bool _disposed;
private readonly object _lock = new object();
@ -53,12 +53,12 @@ namespace Artemis.Core
/// <summary>
/// Gets the action that gets called each time the update event fires
/// </summary>
public Action<double> Action { get; }
public Action<double>? Action { get; }
/// <summary>
/// Gets the task that gets called each time the update event fires
/// </summary>
public Func<double, Task> AsyncAction { get; }
public Func<double, Task>? AsyncAction { get; }
/// <summary>
/// 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;
// ReSharper disable InheritdocConsiderUsage
#pragma warning disable 1591
#pragma warning disable 8618
// ReSharper disable InheritdocConsiderUsage
// ReSharper disable UnusedMember.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global

View File

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

View File

@ -23,7 +23,7 @@ namespace Artemis.Core.Services
/// </summary>
internal class CoreService : ICoreService
{
internal static IKernel Kernel;
internal static IKernel Kernel = null!;
private readonly Stopwatch _frameStopWatch;
private readonly ILogger _logger;
@ -32,10 +32,10 @@ namespace Artemis.Core.Services
private readonly IProfileService _profileService;
private readonly IRgbService _rgbService;
private readonly ISurfaceService _surfaceService;
private List<BaseDataModelExpansion> _dataModelExpansions;
private List<Module> _modules;
private List<BaseDataModelExpansion> _dataModelExpansions = new List<BaseDataModelExpansion>();
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,
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService)
{
@ -60,10 +60,11 @@ namespace Artemis.Core.Services
_pluginManagementService.PluginEnabled += (sender, args) => UpdatePluginCache();
_pluginManagementService.PluginDisabled += (sender, args) => UpdatePluginCache();
}
// ReSharper restore UnusedParameter.Local
public TimeSpan FrameTime { get; private set; }
public bool ModuleRenderingDisabled { get; set; }
public List<string> StartupArguments { get; set; }
public List<string>? StartupArguments { get; set; }
public void Dispose()
{
@ -86,13 +87,10 @@ namespace Artemis.Core.Services
// Initialize the services
_pluginManagementService.CopyBuiltInPlugins();
_pluginManagementService.LoadPlugins(StartupArguments.Contains("--ignore-plugin-lock"));
_pluginManagementService.LoadPlugins(StartupArguments != null && StartupArguments.Contains("--ignore-plugin-lock"));
ArtemisSurface surfaceConfig = _surfaceService.ActiveSurface;
if (surfaceConfig != null)
_logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId);
else
_logger.Information("Initialized without an active surface entity");
PlayIntroAnimation();
OnInitialized();
@ -121,7 +119,7 @@ namespace Artemis.Core.Services
FrameRendering += DrawOverlay;
// 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 () =>
{
await Task.Delay(introLength.Add(TimeSpan.FromSeconds(1)));
@ -217,14 +215,14 @@ namespace Artemis.Core.Services
if (_rgbService.IsRenderPaused)
return;
OnFrameRendered(new FrameRenderedEventArgs(_rgbService.BitmapBrush, _rgbService.Surface));
OnFrameRendered(new FrameRenderedEventArgs(_rgbService.BitmapBrush!, _rgbService.Surface));
}
#region Events
public event EventHandler Initialized;
public event EventHandler<FrameRenderingEventArgs> FrameRendering;
public event EventHandler<FrameRenderedEventArgs> FrameRendered;
public event EventHandler? Initialized;
public event EventHandler<FrameRenderingEventArgs>? FrameRendering;
public event EventHandler<FrameRenderedEventArgs>? FrameRendered;
private void OnInitialized()
{

View File

@ -26,7 +26,7 @@ namespace Artemis.Core.Services
/// <summary>
/// Gets or sets a list of startup arguments
/// </summary>
List<string> StartupArguments { get; set; }
List<string>? StartupArguments { get; set; }
/// <summary>
/// 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
/// <see cref="ActiveModuleOverride" /> is active.
/// </summary>
Module ActiveModuleOverride { get; }
Module? ActiveModuleOverride { get; }
/// <summary>
/// Changes the current <see cref="ActiveModuleOverride" /> and deactivates all other modules
/// </summary>
/// <param name="overrideModule"></param>
Task SetActiveModuleOverride(Module overrideModule);
Task SetActiveModuleOverride(Module? overrideModule);
/// <summary>
/// Evaluates every enabled module's activation requirements and activates/deactivates modules accordingly

View File

@ -17,7 +17,7 @@ namespace Artemis.Core.Services
/// <summary>
/// Gets the bitmap brush used to convert the rendered frame to LED-colors
/// </summary>
BitmapBrush BitmapBrush { get; }
BitmapBrush? BitmapBrush { get; }
/// <summary>
/// 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);
}
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
{

View File

@ -242,11 +242,9 @@ namespace Artemis.Core.Services
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
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
string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main);

View File

@ -34,7 +34,7 @@ namespace Artemis.Core.Services
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;
}

View File

@ -34,7 +34,7 @@ namespace Artemis.Core.Services
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;
}

View File

@ -11,7 +11,7 @@ namespace Artemis.Core.Services
public DataModelService(IPluginManagementService pluginManagementService)
{
// 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);
foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetFeaturesOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled))
AddDataModelExpansionDataModel(dataModelExpansion);
@ -40,9 +40,9 @@ namespace Artemis.Core.Services
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)
@ -61,8 +61,7 @@ namespace Artemis.Core.Services
private void AddModuleDataModel(Module module)
{
if (module.InternalDataModel == null)
return;
throw new ArtemisCoreException("Cannot add module data model that is not enabled");
if (module.InternalDataModel.DataModelDescription == null)
throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null");
@ -72,6 +71,8 @@ namespace Artemis.Core.Services
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)
throw new ArtemisPluginFeatureException(dataModelExpansion, "Data model expansion overrides GetDataModelDescription but returned null");

View File

@ -32,6 +32,6 @@ namespace Artemis.Core.Services
/// </summary>
/// <param name="operatorPluginGuid">The operator's plugin GUID</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="modifierType">The type name of the modifier type</param>
/// <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" />
/// </summary>
/// <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>
/// 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();
}
public LayerBrushDescriptor GetDefaultLayerBrush()
public LayerBrushDescriptor? GetDefaultLayerBrush()
{
PluginSetting<LayerBrushReference> defaultReference = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
{
@ -42,6 +42,8 @@ namespace Artemis.Core.Services
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;
}
}

View File

@ -17,7 +17,7 @@ namespace Artemis.Core.Services
private readonly PluginSetting<double> _renderScaleSetting;
private readonly PluginSetting<int> _sampleSizeSetting;
private readonly PluginSetting<int> _targetFrameRateSetting;
private ListLedGroup _surfaceLedGroup;
private ListLedGroup? _surfaceLedGroup;
public RgbService(ILogger logger, ISettingsService settingsService)
{
@ -41,7 +41,7 @@ namespace Artemis.Core.Services
public RGBSurface Surface { get; set; }
public TimerUpdateTrigger UpdateTrigger { get; }
public BitmapBrush BitmapBrush { get; private set; }
public BitmapBrush? BitmapBrush { get; private set; }
public IReadOnlyCollection<IRGBDevice> LoadedDevices => _loadedDevices.AsReadOnly();
public double RenderScale => _renderScaleSetting.Value;
public bool IsRenderPaused { get; set; }
@ -109,7 +109,7 @@ namespace Artemis.Core.Services
public void UpdateSurfaceLedGroup()
{
if (_surfaceLedGroup == null)
if (_surfaceLedGroup == null || BitmapBrush == null)
{
// Apply the application wide brush and decorator
BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value), _sampleSizeSetting);
@ -126,10 +126,6 @@ namespace Artemis.Core.Services
BitmapBrush.Scale = new Scale(_renderScaleSetting.Value);
_surfaceLedGroup = new ListLedGroup(Surface.Leds) {Brush = BitmapBrush};
}
lock (BitmapBrush)
{
}
}
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)
{
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 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)
{
List<ProfileEntity> profileEntities = _profileRepository.GetByModuleId(module.Id);
@ -52,14 +86,14 @@ namespace Artemis.Core.Services
public void ActivateLastProfile(ProfileModule profileModule)
{
ProfileDescriptor activeProfile = GetLastActiveProfile(profileModule);
ProfileDescriptor? activeProfile = GetLastActiveProfile(profileModule);
if (activeProfile != null)
ActivateProfile(activeProfile);
}
public async Task ActivateLastProfileAnimated(ProfileModule profileModule)
{
ProfileDescriptor activeProfile = GetLastActiveProfile(profileModule);
ProfileDescriptor? activeProfile = GetLastActiveProfile(profileModule);
if (activeProfile != null)
await ActivateProfileAnimated(activeProfile);
}
@ -196,7 +230,8 @@ namespace Artemis.Core.Services
string top = profile.UndoStack.Pop();
string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
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();
InstantiateProfile(profile);
@ -220,7 +255,8 @@ namespace Artemis.Core.Services
string top = profile.RedoStack.Pop();
string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings);
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();
InstantiateProfile(profile);
@ -246,7 +282,9 @@ namespace Artemis.Core.Services
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
profileEntity.UpdateGuid(Guid.NewGuid());
@ -256,40 +294,6 @@ namespace Artemis.Core.Services
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
private void OnActiveSurfaceConfigurationSelected(object? sender, SurfaceConfigurationEventArgs e)

View File

@ -28,6 +28,8 @@ namespace Artemis.Core.Services
_surfaceConfigurations = new List<ArtemisSurface>();
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.5);
// LoadFromRepository is guaranteed to set the ActiveSurface
ActiveSurface = null!;
LoadFromRepository();
_rgbService.DeviceLoaded += RgbServiceOnDeviceLoaded;
@ -61,6 +63,7 @@ namespace Artemis.Core.Services
public void SetActiveSurfaceConfiguration(ArtemisSurface surface)
{
if (surface == null) throw new ArgumentNullException(nameof(surface));
if (ActiveSurface == surface)
return;
@ -81,11 +84,8 @@ namespace Artemis.Core.Services
}
// Apply the active surface entity to the devices
if (ActiveSurface != null)
{
foreach (ArtemisDevice device in ActiveSurface.Devices)
device.ApplyToRgbDevice();
}
// Update the RGB service's graphics decorator to work with the new surface entity
_rgbService.UpdateSurfaceLedGroup();
@ -134,7 +134,7 @@ namespace Artemis.Core.Services
ArtemisSurface surfaceConfiguration = new ArtemisSurface(_rgbService.Surface, surfaceEntity, _renderScaleSetting.Value);
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)
{
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(device);
@ -150,7 +150,7 @@ namespace Artemis.Core.Services
}
// 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)
SetActiveSurfaceConfiguration(active);
else
@ -170,13 +170,13 @@ namespace Artemis.Core.Services
private void AddDeviceIfMissing(IRGBDevice rgbDevice, ArtemisSurface surface)
{
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)
return;
// 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)
{
DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
@ -225,8 +225,8 @@ namespace Artemis.Core.Services
#region Events
public event EventHandler<SurfaceConfigurationEventArgs> ActiveSurfaceConfigurationSelected;
public event EventHandler<SurfaceConfigurationEventArgs> SurfaceConfigurationUpdated;
public event EventHandler<SurfaceConfigurationEventArgs>? ActiveSurfaceConfigurationSelected;
public event EventHandler<SurfaceConfigurationEventArgs>? SurfaceConfigurationUpdated;
protected virtual void OnActiveSurfaceConfigurationChanged(SurfaceConfigurationEventArgs e)
{

View File

@ -10,6 +10,9 @@ namespace Artemis.Core
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;
lock (Registrations)
{
@ -74,8 +77,8 @@ namespace Artemis.Core
#region Events
public static event EventHandler<ConditionOperatorStoreEvent> ConditionOperatorAdded;
public static event EventHandler<ConditionOperatorStoreEvent> ConditionOperatorRemoved;
public static event EventHandler<ConditionOperatorStoreEvent>? ConditionOperatorAdded;
public static event EventHandler<ConditionOperatorStoreEvent>? ConditionOperatorRemoved;
private static void OnConditionOperatorAdded(ConditionOperatorStoreEvent e)
{

View File

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

View File

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

View File

@ -51,14 +51,14 @@ namespace Artemis.Core
{
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
public static event EventHandler<LayerEffectStoreEvent> LayerEffectAdded;
public static event EventHandler<LayerEffectStoreEvent> LayerEffectRemoved;
public static event EventHandler<LayerEffectStoreEvent>? LayerEffectAdded;
public static event EventHandler<LayerEffectStoreEvent>? LayerEffectRemoved;
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()}";
// If restart is required, start the executable again after the process was killed
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
{
@ -45,7 +45,7 @@ namespace Artemis.Core
/// </summary>
/// <param name="url">The URL to open</param>
/// <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
{
@ -61,7 +61,7 @@ namespace Artemis.Core
/// <returns></returns>
internal static string GetCurrentLocation()
{
return Process.GetCurrentProcess().MainModule.FileName;
return Process.GetCurrentProcess().MainModule!.FileName!;
}
#region Events

View File

@ -6,7 +6,7 @@ namespace Artemis.Core
{
internal static class DeserializationLogger
{
private static ILogger _logger;
private static ILogger? _logger;
public static void Initialize(IKernel kernel)
{
@ -15,7 +15,7 @@ namespace Artemis.Core
public static void LogPredicateDeserializationFailure(DataModelConditionPredicate dataModelConditionPredicate, JsonException exception)
{
_logger.Warning(
_logger?.Warning(
exception,
"Failed to deserialize display condition predicate {left} {operator} {right}",
dataModelConditionPredicate.Entity.LeftPath?.Path,
@ -26,7 +26,7 @@ namespace Artemis.Core
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
// In the same iteration, create the accessor
Expression condition = null;
Expression? condition = null;
foreach (string memberName in path.Split('.'))
{
BinaryExpression notNull = Expression.NotEqual(source, Expression.Constant(null));

View File

@ -21,21 +21,19 @@ namespace Artemis.Core
_logger = logger;
_profileService = profileService;
_surfaceService = surfaceService;
CreateIntroProfile();
AnimationProfile = CreateIntroProfile();
}
public Profile AnimationProfile { get; set; }
public void Render(double deltaTime, SKCanvas canvas)
{
if (AnimationProfile == null)
return;
AnimationProfile.Update(deltaTime);
AnimationProfile.Render(canvas);
}
private void CreateIntroProfile()
private Profile CreateIntroProfile()
{
try
{
@ -44,24 +42,24 @@ namespace Artemis.Core
ProfileEntity profileEntity = JsonConvert.DeserializeObject<ProfileEntity>(json);
// Inject every LED on the surface into each layer
foreach (LayerEntity profileEntityLayer in profileEntity.Layers)
{
profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity
{
DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(),
LedName = l.RgbLed.Id.ToString()
}));
}
Profile profile = new Profile(new DummyModule(), profileEntity);
profile.Activate(_surfaceService.ActiveSurface);
_profileService.InstantiateProfile(profile);
AnimationProfile = profile;
return profile;
}
catch (Exception e)
{
_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);
MemberExpression member = propertyLambda.Body as MemberExpression;
MemberExpression? member = propertyLambda.Body as MemberExpression;
if (member == null)
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)
throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.", propertyLambda));