diff --git a/src/Artemis.Core/Events/DynamicDataModelChildEventArgs.cs b/src/Artemis.Core/Events/DynamicDataModelChildEventArgs.cs
index 61e69c6d1..9248a716a 100644
--- a/src/Artemis.Core/Events/DynamicDataModelChildEventArgs.cs
+++ b/src/Artemis.Core/Events/DynamicDataModelChildEventArgs.cs
@@ -8,7 +8,7 @@ namespace Artemis.Core
///
public class DynamicDataModelChildEventArgs : EventArgs
{
- internal DynamicDataModelChildEventArgs(object? dynamicChild, string key)
+ internal DynamicDataModelChildEventArgs(DynamicChild dynamicChild, string key)
{
DynamicChild = dynamicChild;
Key = key;
@@ -17,7 +17,7 @@ namespace Artemis.Core
///
/// Gets the dynamic data model child
///
- public object? DynamicChild { get; }
+ public DynamicChild DynamicChild { get; }
///
/// Gets the key of the dynamic data model on the parent
diff --git a/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs b/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs
index f78bc7c54..27f4294ed 100644
--- a/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs
+++ b/src/Artemis.Core/Models/Profile/DataModel/DataModelPathSegment.cs
@@ -15,8 +15,8 @@ namespace Artemis.Core
{
private Expression>? _accessorLambda;
private DataModel? _dynamicDataModel;
- private Type _dynamicDataModelType;
- private DataModelPropertyAttribute _dynamicDataModelAttribute;
+ private Type? _dynamicDataModelType;
+ private DataModelPropertyAttribute? _dynamicDataModelAttribute;
internal DataModelPathSegment(DataModelPath dataModelPath, string identifier, string path)
{
@@ -184,8 +184,8 @@ namespace Artemis.Core
// If a dynamic data model is found the use that
bool hasDynamicChild = _dynamicDataModel.DynamicChildren.TryGetValue(Identifier, out DynamicChild? dynamicChild);
- if (hasDynamicChild && dynamicChild?.Value != null)
- DetermineDynamicType(dynamicChild.Value, dynamicChild.Attribute);
+ if (hasDynamicChild && dynamicChild?.BaseValue != null)
+ DetermineDynamicType(dynamicChild.BaseValue, dynamicChild.Attribute);
_dynamicDataModel.DynamicChildAdded += DynamicChildOnDynamicChildAdded;
_dynamicDataModel.DynamicChildRemoved += DynamicChildOnDynamicChildRemoved;
@@ -212,12 +212,14 @@ namespace Artemis.Core
accessorExpression = Expression.PropertyOrField(expression, Identifier);
// A dynamic segment calls the generic method DataModel.DynamicChild and provides the identifier as an argument
else
+ {
accessorExpression = Expression.Call(
expression,
- nameof(DataModel.DynamicChild),
+ nameof(DataModel.GetDynamicChildValue),
_dynamicDataModelType != null ? new[] { _dynamicDataModelType } : null,
Expression.Constant(Identifier)
);
+ }
_accessorLambda = Expression.Lambda>(
// Wrap with a null check
@@ -290,7 +292,7 @@ namespace Artemis.Core
private void DynamicChildOnDynamicChildRemoved(object? sender, DynamicDataModelChildEventArgs e)
{
- if (e.DynamicChild == _dynamicDataModel)
+ if (e.DynamicChild.BaseValue == _dynamicDataModel)
DataModelPath.Initialize();
}
diff --git a/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs b/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs
index 392995a4c..2013a9e25 100644
--- a/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs
+++ b/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Artemis.Core.Modules;
@@ -66,183 +67,6 @@ namespace Artemis.Core.DataModelExpansions
return new List().AsReadOnly();
}
- ///
- /// Adds a dynamic child to this data model
- ///
- /// The dynamic child to add
- /// The key of the child, must be unique to this data model
- /// An optional human readable name, if not provided the key will be used in a humanized form
- public T AddDynamicChild(T dynamicChild, string key, string? name = null)
- {
- if (dynamicChild == null)
- throw new ArgumentNullException(nameof(dynamicChild));
- if (key == null)
- throw new ArgumentNullException(nameof(key));
- if (key.Contains('.'))
- throw new ArtemisCoreException("The provided key contains an illegal character (.)");
- if (_dynamicChildren.ContainsKey(key))
- {
- throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
- "because the key is already in use on by another dynamic property this data model.");
- }
-
- if (GetType().GetProperty(key) != null)
- {
- throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
- "because the key is already in use by a static property on this data model.");
- }
-
- DataModelPropertyAttribute attribute = new()
- {
- Name = string.IsNullOrWhiteSpace(name) ? key.Humanize() : name
- };
- if (dynamicChild is DataModel dynamicDataModel)
- {
- dynamicDataModel.Feature = Feature;
- dynamicDataModel.DataModelDescription = attribute;
- }
-
- _dynamicChildren.Add(key, new DynamicChild(attribute, dynamicChild));
-
- OnDynamicDataModelAdded(new DynamicDataModelChildEventArgs(dynamicChild, key));
- return dynamicChild;
- }
-
- ///
- /// Adds a dynamic child to this data model
- ///
- /// The dynamic child to add
- /// The key of the child, must be unique to this data model
- /// A human readable for your dynamic child, shown in the UI
- /// An optional description, shown in the UI
- public T AddDynamicChild(T dynamicChild, string key, string name, string description)
- {
- if (dynamicChild == null)
- throw new ArgumentNullException(nameof(dynamicChild));
- if (key == null)
- throw new ArgumentNullException(nameof(key));
- if (key.Contains('.'))
- throw new ArtemisCoreException("The provided key contains an illegal character (.)");
- if (_dynamicChildren.ContainsKey(key))
- {
- throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
- "because the key is already in use on by another dynamic property this data model.");
- }
-
- if (GetType().GetProperty(key) != null)
- {
- throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
- "because the key is already in use by a static property on this data model.");
- }
-
- DataModelPropertyAttribute attribute = new()
- {
- Name = string.IsNullOrWhiteSpace(name) ? key.Humanize() : name,
- Description = description
- };
- if (dynamicChild is DataModel dynamicDataModel)
- {
- dynamicDataModel.Feature = Feature;
- dynamicDataModel.DataModelDescription = attribute;
- }
-
- _dynamicChildren.Add(key, new DynamicChild(attribute, dynamicChild));
-
- OnDynamicDataModelAdded(new DynamicDataModelChildEventArgs(dynamicChild, key));
- return dynamicChild;
- }
-
- ///
- /// Adds a dynamic child to this data model
- ///
- /// The dynamic child to add
- /// The key of the child, must be unique to this data model
- /// A data model property attribute describing the dynamic child
- public T AddDynamicChild(T dynamicChild, string key, DataModelPropertyAttribute attribute)
- {
- if (dynamicChild == null) throw new ArgumentNullException(nameof(dynamicChild));
- if (key == null) throw new ArgumentNullException(nameof(key));
- if (attribute == null) throw new ArgumentNullException(nameof(attribute));
- if (key.Contains('.'))
- throw new ArtemisCoreException("The provided key contains an illegal character (.)");
- if (_dynamicChildren.ContainsKey(key))
- {
- throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
- "because the key is already in use on by another dynamic property this data model.");
- }
-
- if (GetType().GetProperty(key) != null)
- {
- throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
- "because the key is already in use by a static property on this data model.");
- }
-
- // Make sure a name is on the attribute or funny things might happen
- attribute.Name ??= key.Humanize();
- if (dynamicChild is DataModel dynamicDataModel)
- {
- dynamicDataModel.Feature = Feature;
- dynamicDataModel.DataModelDescription = attribute;
- }
-
- _dynamicChildren.Add(key, new DynamicChild(attribute, dynamicChild));
-
- OnDynamicDataModelAdded(new DynamicDataModelChildEventArgs(dynamicChild, key));
- return dynamicChild;
- }
-
- ///
- /// Removes a dynamic child from the data model by its key
- ///
- /// The key of the dynamic child to remove
- public void RemoveDynamicChildByKey(string key)
- {
- if (!_dynamicChildren.TryGetValue(key, out DynamicChild? dynamicChild))
- return;
-
- _dynamicChildren.Remove(key);
- OnDynamicDataModelRemoved(new DynamicDataModelChildEventArgs(dynamicChild.Value, key));
- }
-
- ///
- /// Removes a dynamic child from this data model
- ///
- /// The dynamic data child to remove
- public void RemoveDynamicChild(T dynamicChild) where T : class
- {
- List keys = _dynamicChildren.Where(kvp => kvp.Value.Value == dynamicChild).Select(kvp => kvp.Key).ToList();
- foreach (string key in keys)
- {
- _dynamicChildren.Remove(key);
- OnDynamicDataModelRemoved(new DynamicDataModelChildEventArgs(dynamicChild, key));
- }
- }
-
- ///
- /// Removes all dynamic children from this data model
- ///
- public void ClearDynamicChildren()
- {
- while (_dynamicChildren.Any())
- RemoveDynamicChildByKey(_dynamicChildren.First().Key);
- }
-
- ///
- /// Gets a dynamic child of type by its key
- ///
- /// The type of data model you expect
- /// The unique key of the dynamic child
- /// If found, the dynamic child otherwise null
- public T? DynamicChild(string key)
- {
- if (!_dynamicChildren.TryGetValue(key, out DynamicChild? dynamicChild))
- return default;
-
- if (dynamicChild.Value is not T)
- return default;
- return (T?) dynamicChild.Value;
- }
-
///
/// Occurs when a dynamic child has been added to this data model
///
@@ -268,10 +92,184 @@ namespace Artemis.Core.DataModelExpansions
{
DynamicChildRemoved?.Invoke(this, e);
}
- }
- ///
- /// Represents a record of a dynamic child value with its property attribute
- ///
- public record DynamicChild(DataModelPropertyAttribute Attribute, object? Value);
+ #region Dynamic children
+
+ ///
+ /// Adds a dynamic child to this data model
+ ///
+ /// The key of the child, must be unique to this data model
+ /// The initial value of the dynamic child
+ /// The resulting dynamic child which can be used to further update the value
+ public DynamicChild AddDynamicChild(string key, T initialValue)
+ {
+ return AddDynamicChild(key, initialValue, new DataModelPropertyAttribute());
+ }
+
+ ///
+ /// Adds a dynamic child to this data model
+ ///
+ /// The key of the child, must be unique to this data model
+ /// The initial value of the dynamic child
+ /// A human readable name for your dynamic child, shown in the UI
+ /// An optional description, shown in the UI
+ /// The resulting dynamic child which can be used to further update the value
+ public DynamicChild AddDynamicChild(string key, T initialValue, string name, string? description = null)
+ {
+ return AddDynamicChild(key, initialValue, new DataModelPropertyAttribute {Name = name, Description = description});
+ }
+
+ ///
+ /// Adds a dynamic child to this data model
+ ///
+ /// The key of the child, must be unique to this data model
+ /// The initial value of the dynamic child
+ /// A data model property attribute describing the dynamic child
+ /// The resulting dynamic child which can be used to further update the value
+ public DynamicChild AddDynamicChild(string key, T initialValue, DataModelPropertyAttribute attribute)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ if (initialValue == null) throw new ArgumentNullException(nameof(initialValue));
+ if (attribute == null) throw new ArgumentNullException(nameof(attribute));
+ if (key.Contains('.'))
+ throw new ArtemisCoreException("The provided key contains an illegal character (.)");
+ if (_dynamicChildren.ContainsKey(key))
+ throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
+ "because the key is already in use on by another dynamic property this data model.");
+
+ if (GetType().GetProperty(key) != null)
+ throw new ArtemisCoreException($"Cannot add a dynamic child with key '{key}' " +
+ "because the key is already in use by a static property on this data model.");
+
+ // Make sure a name is on the attribute or funny things might happen
+ attribute.Name ??= key.Humanize();
+ if (initialValue is DataModel dynamicDataModel)
+ {
+ dynamicDataModel.Feature = Feature;
+ dynamicDataModel.DataModelDescription = attribute;
+ }
+
+ DynamicChild dynamicChild = new(initialValue, key, attribute);
+ _dynamicChildren.Add(key, dynamicChild);
+
+ OnDynamicDataModelAdded(new DynamicDataModelChildEventArgs(dynamicChild, key));
+ return dynamicChild;
+ }
+
+ ///
+ /// Gets a previously added dynamic child by its key
+ ///
+ /// The key of the dynamic child
+ public DynamicChild GetDynamicChild(string key)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ return DynamicChildren[key];
+ }
+
+ ///
+ /// Gets a previously added dynamic child by its key
+ ///
+ /// The typer of dynamic child you are expecting
+ /// The key of the dynamic child
+ ///
+ public DynamicChild GetDynamicChild(string key)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ return (DynamicChild) DynamicChildren[key];
+ }
+
+ ///
+ /// Gets a previously added dynamic child by its key
+ ///
+ /// The key of the dynamic child
+ ///
+ /// When this method returns, the associated with the specified key,
+ /// if the key is found; otherwise, . This parameter is passed uninitialized.
+ ///
+ ///
+ /// if the data model contains the dynamic child; otherwise
+ ///
+ public bool TryGetDynamicChild(string key, [MaybeNullWhen(false)] out DynamicChild dynamicChild)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+
+ dynamicChild = null;
+ if (!DynamicChildren.TryGetValue(key, out DynamicChild? value))
+ return false;
+
+ dynamicChild = value;
+ return true;
+ }
+
+ ///
+ /// Gets a previously added dynamic child by its key
+ ///
+ /// The typer of dynamic child you are expecting
+ /// The key of the dynamic child
+ ///
+ /// When this method returns, the associated with the specified
+ /// key, if the key is found; otherwise, . This parameter is passed uninitialized.
+ ///
+ ///
+ /// if the data model contains the dynamic child; otherwise
+ ///
+ public bool TryGetDynamicChild(string key, [MaybeNullWhen(false)] out DynamicChild dynamicChild)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+
+ dynamicChild = null;
+ if (!DynamicChildren.TryGetValue(key, out DynamicChild? value))
+ return false;
+ if (value is not DynamicChild typedDynamicChild)
+ return false;
+ dynamicChild = typedDynamicChild;
+ return true;
+ }
+
+ ///
+ /// Removes a dynamic child from the data model by its key
+ ///
+ /// The key of the dynamic child to remove
+ public void RemoveDynamicChildByKey(string key)
+ {
+ if (key == null) throw new ArgumentNullException(nameof(key));
+ if (!_dynamicChildren.TryGetValue(key, out DynamicChild? dynamicChild))
+ return;
+
+ _dynamicChildren.Remove(key);
+ OnDynamicDataModelRemoved(new DynamicDataModelChildEventArgs(dynamicChild, key));
+ }
+
+ ///
+ /// Removes a dynamic child from this data model
+ ///
+ /// The dynamic data child to remove
+ public void RemoveDynamicChild(DynamicChild dynamicChild)
+ {
+ if (dynamicChild == null) throw new ArgumentNullException(nameof(dynamicChild));
+ List keys = _dynamicChildren.Where(kvp => kvp.Value.BaseValue == dynamicChild).Select(kvp => kvp.Key).ToList();
+ foreach (string key in keys)
+ {
+ _dynamicChildren.Remove(key);
+ OnDynamicDataModelRemoved(new DynamicDataModelChildEventArgs(dynamicChild, key));
+ }
+ }
+
+ ///
+ /// Removes all dynamic children from this data model
+ ///
+ public void ClearDynamicChildren()
+ {
+ while (_dynamicChildren.Any())
+ RemoveDynamicChildByKey(_dynamicChildren.First().Key);
+ }
+
+ // Used a runtime by data model paths only
+ internal T? GetDynamicChildValue(string key)
+ {
+ return TryGetDynamicChild(key, out DynamicChild? dynamicChild) ? dynamicChild.Value : default;
+ }
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/DataModelExpansions/DynamicChild.cs b/src/Artemis.Core/Plugins/DataModelExpansions/DynamicChild.cs
new file mode 100644
index 000000000..5e5732b55
--- /dev/null
+++ b/src/Artemis.Core/Plugins/DataModelExpansions/DynamicChild.cs
@@ -0,0 +1,66 @@
+using System;
+
+namespace Artemis.Core.DataModelExpansions
+{
+ ///
+ /// Represents a dynamic child value with its property attribute
+ ///
+ public class DynamicChild : DynamicChild
+ {
+ internal DynamicChild(T value, string key, DataModelPropertyAttribute attribute) : base(key, attribute, typeof(T))
+ {
+ Value = value;
+ }
+
+ ///
+ /// Gets or sets the current value of the dynamic child
+ ///
+ public new T Value { get; set; }
+
+ ///
+ protected override object? GetValue()
+ {
+ return Value;
+ }
+ }
+
+ ///
+ /// Represents a dynamic child value with its property attribute
+ ///
+ public abstract class DynamicChild
+ {
+ internal DynamicChild(string key, DataModelPropertyAttribute attribute, Type type)
+ {
+ if (type == null) throw new ArgumentNullException(nameof(type));
+ Key = key ?? throw new ArgumentNullException(nameof(key));
+ Attribute = attribute ?? throw new ArgumentNullException(nameof(attribute));
+ Type = type;
+ }
+
+ ///
+ /// Gets the key of the dynamic child
+ ///
+ public string Key { get; }
+
+ ///
+ /// Gets the attribute describing the dynamic child
+ ///
+ public DataModelPropertyAttribute Attribute { get; }
+
+ ///
+ /// Gets the type of
+ ///
+ public Type Type { get; }
+
+ ///
+ /// Gets the current value of the dynamic child
+ ///
+ public object? BaseValue => GetValue();
+
+ ///
+ /// Gets the current value of the dynamic child
+ ///
+ /// The current value of the dynamic child
+ protected abstract object? GetValue();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs
index 5e4a5066a..26c66ed54 100644
--- a/src/Artemis.Core/Services/RgbService.cs
+++ b/src/Artemis.Core/Services/RgbService.cs
@@ -113,7 +113,7 @@ namespace Artemis.Core.Services
private void SurfaceOnException(ExceptionEventArgs args)
{
- _logger.Warning("Surface threw e");
+ _logger.Warning(args.Exception, "Surface caught exception");
throw args.Exception;
}
diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
index fb3b77506..e3fd07f40 100644
--- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
+++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs
@@ -314,6 +314,7 @@ namespace Artemis.UI.Shared
ImageBrush bitmapBrush = new(bitmap);
bitmapBrush.Freeze();
_backingStore.OpacityMask = bitmapBrush;
+ _backingStore.Children.Clear();
InvalidateMeasure();
}
@@ -331,14 +332,19 @@ namespace Artemis.UI.Shared
private void Render()
{
DrawingContext drawingContext = _backingStore.Append();
- // DrawingContext drawingContext = _backingStore.Open();
if (HighlightedLeds != null && HighlightedLeds.Any())
+ {
+ // Faster on large devices, maybe a bit slower on smaller ones but that's ok
+ ILookup toHighlight = HighlightedLeds.ToLookup(l => l);
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
- deviceVisualizerLed.RenderColor(_backingStore, drawingContext, !HighlightedLeds.Contains(deviceVisualizerLed.Led));
+ deviceVisualizerLed.RenderColor(_backingStore, drawingContext, !toHighlight.Contains(deviceVisualizerLed.Led));
+ }
else
+ {
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
deviceVisualizerLed.RenderColor(_backingStore, drawingContext, false);
+ }
drawingContext.Close();
}
diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs
index 45dc4ac41..cf0699306 100644
--- a/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs
+++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizerLed.cs
@@ -14,10 +14,10 @@ namespace Artemis.UI.Shared
{
private const byte Dimmed = 100;
private const byte NonDimmed = 255;
- private GeometryDrawing? _geometryDrawing;
private Color _renderColor;
-
+ private GeometryDrawing? _geometryDrawing;
private SolidColorBrush? _renderColorBrush;
+ private DrawingGroup? _lastBackingStore;
public DeviceVisualizerLed(ArtemisLed led)
{
@@ -43,12 +43,12 @@ namespace Artemis.UI.Shared
public void RenderColor(DrawingGroup backingStore, DrawingContext drawingContext, bool isDimmed)
{
- if (DisplayGeometry == null)
+ if (DisplayGeometry == null || backingStore == null)
return;
_renderColorBrush ??= new SolidColorBrush();
_geometryDrawing ??= new GeometryDrawing(_renderColorBrush, null, new RectangleGeometry(LedRect));
-
+
byte r = Led.RgbLed.Color.GetR();
byte g = Led.RgbLed.Color.GetG();
byte b = Led.RgbLed.Color.GetB();
@@ -59,9 +59,12 @@ namespace Artemis.UI.Shared
_renderColor.B = b;
_renderColorBrush.Color = _renderColor;
-
- if (!backingStore.Children.Contains(_geometryDrawing))
+ if (_lastBackingStore != backingStore)
+ {
backingStore.Children.Add(_geometryDrawing);
+ _lastBackingStore = backingStore;
+ }
+
}
public void RenderImage(DrawingContext drawingContext)