mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 03:19:56 +00:00
Merge branch 'development' into feature/cpm
This commit is contained in:
commit
d0e354952d
235 files changed
+645
-6611
No files matched your search
@@ -5,7 +5,7 @@ using Artemis.Core.DryIoc.Factories;
|
||||
using Artemis.Core.Providers;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Storage;
|
||||
using Artemis.Storage.Migrations.Interfaces;
|
||||
using Artemis.Storage.Migrations;
|
||||
using Artemis.Storage.Repositories.Interfaces;
|
||||
using DryIoc;
|
||||
|
||||
@@ -36,6 +36,7 @@ public static class ContainerExtensions
|
||||
|
||||
// Bind migrations
|
||||
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IStorageMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
|
||||
container.RegisterMany(storageAssembly, type => type.IsAssignableTo<IProfileMigration>(), Reuse.Singleton, nonPublicServiceTypes: true);
|
||||
|
||||
container.RegisterMany(coreAssembly, type => type.IsAssignableTo<ILayoutProvider>(), Reuse.Singleton);
|
||||
container.Register<IPluginSettingsFactory, PluginSettingsFactory>(Reuse.Singleton);
|
||||
|
||||
@@ -248,8 +248,8 @@ public sealed class Layer : RenderProfileElement
|
||||
typeof(PropertyGroupDescriptionAttribute)
|
||||
)!;
|
||||
|
||||
LayerEntity.GeneralPropertyGroup ??= new PropertyGroupEntity {Identifier = generalAttribute.Identifier};
|
||||
LayerEntity.TransformPropertyGroup ??= new PropertyGroupEntity {Identifier = transformAttribute.Identifier};
|
||||
LayerEntity.GeneralPropertyGroup ??= new PropertyGroupEntity {Identifier = generalAttribute.Identifier!};
|
||||
LayerEntity.TransformPropertyGroup ??= new PropertyGroupEntity {Identifier = transformAttribute.Identifier!};
|
||||
|
||||
General.Initialize(this, null, generalAttribute, LayerEntity.GeneralPropertyGroup);
|
||||
Transform.Initialize(this, null, transformAttribute, LayerEntity.TransformPropertyGroup);
|
||||
|
||||
@@ -240,7 +240,8 @@ public abstract class LayerPropertyGroup : IDisposable
|
||||
foreach (LayerPropertyGroup layerPropertyGroup in LayerPropertyGroups)
|
||||
{
|
||||
layerPropertyGroup.ApplyToEntity();
|
||||
PropertyGroupEntity.PropertyGroups.Add(layerPropertyGroup.PropertyGroupEntity);
|
||||
if (layerPropertyGroup.PropertyGroupEntity != null)
|
||||
PropertyGroupEntity.PropertyGroups.Add(layerPropertyGroup.PropertyGroupEntity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ArtemisDevice : CorePropertyChanged
|
||||
InputIdentifiers = new List<ArtemisDeviceInputIdentifier>();
|
||||
InputMappings = new Dictionary<ArtemisLed, ArtemisLed>();
|
||||
Categories = new HashSet<DeviceCategory>();
|
||||
LayoutSelection = new LayoutSelection {Type = DefaultLayoutProvider.LayoutType};
|
||||
LayoutSelection = new LayoutSelection {Type = DefaultLayoutProvider.LAYOUT_TYPE};
|
||||
|
||||
RgbDevice.ColorCorrections.Clear();
|
||||
RgbDevice.ColorCorrections.Add(new ScaleColorCorrection(this));
|
||||
@@ -75,7 +75,7 @@ public class ArtemisDevice : CorePropertyChanged
|
||||
InputIdentifiers = new List<ArtemisDeviceInputIdentifier>();
|
||||
InputMappings = new Dictionary<ArtemisLed, ArtemisLed>();
|
||||
Categories = new HashSet<DeviceCategory>();
|
||||
LayoutSelection = new LayoutSelection {Type = DefaultLayoutProvider.LayoutType};
|
||||
LayoutSelection = new LayoutSelection {Type = DefaultLayoutProvider.LAYOUT_TYPE};
|
||||
|
||||
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
|
||||
InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier));
|
||||
@@ -155,6 +155,9 @@ public class ArtemisDevice : CorePropertyChanged
|
||||
/// </summary>
|
||||
public HashSet<DeviceCategory> Categories { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the layout selection applied to this device
|
||||
/// </summary>
|
||||
public LayoutSelection LayoutSelection { get; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -57,6 +57,9 @@ public class ArtemisLayout
|
||||
/// </summary>
|
||||
public LayoutCustomDeviceData LayoutCustomDeviceData { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether this layout is a default layout or not
|
||||
/// </summary>
|
||||
public bool IsDefaultLayout { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -65,7 +65,7 @@ public class LayerBrushDescriptor
|
||||
BaseLayerBrush brush = (BaseLayerBrush) Provider.Plugin.Resolve(LayerBrushType);
|
||||
brush.Layer = layer;
|
||||
brush.Descriptor = this;
|
||||
brush.LayerBrushEntity = entity ?? new LayerBrushEntity {ProviderId = Provider.Id, BrushType = LayerBrushType.FullName};
|
||||
brush.LayerBrushEntity = entity ?? new LayerBrushEntity {ProviderId = Provider.Id, BrushType = LayerBrushType.FullName ?? throw new InvalidOperationException()};
|
||||
|
||||
brush.Initialize();
|
||||
return brush;
|
||||
|
||||
@@ -231,7 +231,7 @@ public abstract class BaseLayerEffect : BreakableModel, IDisposable, IStorageMod
|
||||
return;
|
||||
|
||||
LayerEffectEntity.ProviderId = Descriptor.Provider.Id;
|
||||
LayerEffectEntity.EffectType = GetType().FullName;
|
||||
LayerEffectEntity.EffectType = GetType().FullName ?? throw new InvalidOperationException();
|
||||
BaseProperties?.ApplyToEntity();
|
||||
LayerEffectEntity.PropertyGroup = BaseProperties?.PropertyGroupEntity;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to register one or more <see cref="INode" />s usable by node scripts.
|
||||
/// </summary>
|
||||
public abstract class NodeProvider : PluginFeature
|
||||
{
|
||||
private readonly List<NodeData> _nodeDescriptors;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="NodeProvider"/> class.
|
||||
/// </summary>
|
||||
public NodeProvider()
|
||||
{
|
||||
_nodeDescriptors = new List<NodeData>();
|
||||
NodeDescriptors = new ReadOnlyCollection<NodeData>(_nodeDescriptors);
|
||||
Disabled += OnDisabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A read-only collection of all nodes added with <see cref="RegisterNodeType{T}" />
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<NodeData> NodeDescriptors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a node descriptor for a given node, so that it appears in the UI.
|
||||
/// <para>Note: You do not need to manually remove these on disable</para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the node you wish to register</typeparam>
|
||||
protected void RegisterNodeType<T>() where T : INode
|
||||
{
|
||||
RegisterNodeType(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a node descriptor for a given node, so that it appears in the UI.
|
||||
/// <para>Note: You do not need to manually remove these on disable</para>
|
||||
/// </summary>
|
||||
/// <param name="nodeType">The type of the node you wish to register</param>
|
||||
protected void RegisterNodeType(Type nodeType)
|
||||
{
|
||||
if (!IsEnabled)
|
||||
throw new ArtemisPluginFeatureException(this, "Can only add a node descriptor when the plugin is enabled");
|
||||
if (nodeType == null)
|
||||
throw new ArgumentNullException(nameof(nodeType));
|
||||
if (!nodeType.IsAssignableTo(typeof(INode)))
|
||||
throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
|
||||
|
||||
NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute<NodeAttribute>();
|
||||
string name = nodeAttribute?.Name ?? nodeType.Name;
|
||||
string description = nodeAttribute?.Description ?? string.Empty;
|
||||
string category = nodeAttribute?.Category ?? string.Empty;
|
||||
string helpUrl = nodeAttribute?.HelpUrl ?? string.Empty;
|
||||
|
||||
NodeData nodeData = new(this, nodeType, name, description, category, helpUrl, nodeAttribute?.InputType, nodeAttribute?.OutputType);
|
||||
_nodeDescriptors.Add(nodeData);
|
||||
NodeTypeStore.Add(nodeData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a color for lines of the provided type.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to add.</param>
|
||||
/// <typeparam name="T">The type to use the color for.</typeparam>
|
||||
protected TypeColorRegistration RegisterTypeColor<T>(SKColor color)
|
||||
{
|
||||
return NodeTypeStore.AddColor(typeof(T), color, this);
|
||||
}
|
||||
|
||||
private void OnDisabled(object? sender, EventArgs e)
|
||||
{
|
||||
// The store will clean up the registrations by itself, the plugin feature just needs to clear its own list
|
||||
_nodeDescriptors.Clear();
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public class PluginSettings
|
||||
if (_settingEntities.ContainsKey(name))
|
||||
return (PluginSetting<T>) _settingEntities[name];
|
||||
// Try to find in database
|
||||
PluginSettingEntity settingEntity = _pluginRepository.GetSettingByNameAndGuid(name, Plugin.Guid);
|
||||
PluginSettingEntity? settingEntity = _pluginRepository.GetSettingByNameAndGuid(name, Plugin.Guid);
|
||||
// If not found, create a new one
|
||||
if (settingEntity == null)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
namespace Artemis.Core.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a layout provider that loads a layout from a custom path.
|
||||
/// </summary>
|
||||
public class CustomPathLayoutProvider : ILayoutProvider
|
||||
{
|
||||
public static string LayoutType = "CustomPath";
|
||||
/// <summary>
|
||||
/// The layout type of this layout provider.
|
||||
/// </summary>
|
||||
public const string LAYOUT_TYPE = "CustomPath";
|
||||
|
||||
/// <inheritdoc />
|
||||
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
|
||||
@@ -21,7 +27,7 @@ public class CustomPathLayoutProvider : ILayoutProvider
|
||||
/// <inheritdoc />
|
||||
public bool IsMatch(ArtemisDevice device)
|
||||
{
|
||||
return device.LayoutSelection.Type == LayoutType;
|
||||
return device.LayoutSelection.Type == LAYOUT_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,7 +37,7 @@ public class CustomPathLayoutProvider : ILayoutProvider
|
||||
/// <param name="path">The path to the custom layout.</param>
|
||||
public void ConfigureDevice(ArtemisDevice device, string? path)
|
||||
{
|
||||
device.LayoutSelection.Type = LayoutType;
|
||||
device.LayoutSelection.Type = LAYOUT_TYPE;
|
||||
device.LayoutSelection.Parameter = path;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
namespace Artemis.Core.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a layout provider that loads a layout from the plugin and falls back to a default layout.
|
||||
/// </summary>
|
||||
public class DefaultLayoutProvider : ILayoutProvider
|
||||
{
|
||||
public static string LayoutType = "Default";
|
||||
/// <summary>
|
||||
/// The layout type of this layout provider.
|
||||
/// </summary>
|
||||
public const string LAYOUT_TYPE = "Default";
|
||||
|
||||
/// <inheritdoc />
|
||||
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
|
||||
@@ -26,7 +32,7 @@ public class DefaultLayoutProvider : ILayoutProvider
|
||||
/// <inheritdoc />
|
||||
public bool IsMatch(ArtemisDevice device)
|
||||
{
|
||||
return device.LayoutSelection.Type == LayoutType;
|
||||
return device.LayoutSelection.Type == LAYOUT_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,7 +41,7 @@ public class DefaultLayoutProvider : ILayoutProvider
|
||||
/// <param name="device">The device to apply the provider to.</param>
|
||||
public void ConfigureDevice(ArtemisDevice device)
|
||||
{
|
||||
device.LayoutSelection.Type = LayoutType;
|
||||
device.LayoutSelection.Type = LAYOUT_TYPE;
|
||||
device.LayoutSelection.Parameter = null;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,17 @@ public interface ILayoutProvider
|
||||
/// <returns>The resulting layout if one was available; otherwise <see langword="null" />.</returns>
|
||||
ArtemisLayout? GetDeviceLayout(ArtemisDevice device);
|
||||
|
||||
/// <summary>
|
||||
/// Applies the layout to the provided device.
|
||||
/// </summary>
|
||||
/// <param name="device">The device to apply to.</param>
|
||||
/// <param name="layout">The layout to apply.</param>
|
||||
void ApplyLayout(ArtemisDevice device, ArtemisLayout layout);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided device is configured to use this layout provider.
|
||||
/// </summary>
|
||||
/// <param name="device">The device to check.</param>
|
||||
/// <returns>A value indicating whether the provided device is configured to use this layout provider.</returns>
|
||||
bool IsMatch(ArtemisDevice device);
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
namespace Artemis.Core.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a layout provider that does not load a layout.
|
||||
/// </summary>
|
||||
public class NoneLayoutProvider : ILayoutProvider
|
||||
{
|
||||
public static string LayoutType = "None";
|
||||
/// <summary>
|
||||
/// The layout type of this layout provider.
|
||||
/// </summary>
|
||||
public const string LAYOUT_TYPE = "None";
|
||||
|
||||
/// <inheritdoc />
|
||||
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
|
||||
@@ -19,7 +25,7 @@ public class NoneLayoutProvider : ILayoutProvider
|
||||
/// <inheritdoc />
|
||||
public bool IsMatch(ArtemisDevice device)
|
||||
{
|
||||
return device.LayoutSelection.Type == LayoutType;
|
||||
return device.LayoutSelection.Type == LAYOUT_TYPE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +34,7 @@ public class NoneLayoutProvider : ILayoutProvider
|
||||
/// <param name="device">The device to apply the provider to.</param>
|
||||
public void ConfigureDevice(ArtemisDevice device)
|
||||
{
|
||||
device.LayoutSelection.Type = LayoutType;
|
||||
device.LayoutSelection.Type = LAYOUT_TYPE;
|
||||
device.LayoutSelection.Parameter = null;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
using DryIoc;
|
||||
using Newtonsoft.Json;
|
||||
using SkiaSharp;
|
||||
|
||||
@@ -13,31 +11,8 @@ namespace Artemis.Core.Services;
|
||||
|
||||
internal class NodeService : INodeService
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private static readonly Type TypeNode = typeof(INode);
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly IContainer _container;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public NodeService(IContainer container)
|
||||
{
|
||||
_container = container;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
public IEnumerable<NodeData> AvailableNodes => NodeTypeStore.GetAll();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<Type> GetRegisteredTypes()
|
||||
{
|
||||
@@ -53,7 +28,7 @@ internal class NodeService : INodeService
|
||||
|
||||
// Objects represent an input that can take any type, these are hardcoded white
|
||||
if (type == typeof(object))
|
||||
return new TypeColorRegistration(type, new SKColor(255, 255, 255, 255), Constants.CorePlugin);
|
||||
return new TypeColorRegistration(type, new SKColor(255, 255, 255, 255), Constants.CorePluginFeature);
|
||||
|
||||
// Come up with a random color based on the type name that should be the same each time
|
||||
MD5 md5Hasher = MD5.Create();
|
||||
@@ -61,32 +36,7 @@ internal class NodeService : INodeService
|
||||
int hash = BitConverter.ToInt32(hashed, 0);
|
||||
|
||||
SKColor baseColor = SKColor.FromHsl(hash % 255, 50 + hash % 50, 50);
|
||||
return new TypeColorRegistration(type, baseColor, Constants.CorePlugin);
|
||||
}
|
||||
|
||||
public NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType)
|
||||
{
|
||||
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
|
||||
if (nodeType == null) throw new ArgumentNullException(nameof(nodeType));
|
||||
|
||||
if (!TypeNode.IsAssignableFrom(nodeType)) throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
|
||||
|
||||
NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute<NodeAttribute>();
|
||||
string name = nodeAttribute?.Name ?? nodeType.Name;
|
||||
string description = nodeAttribute?.Description ?? string.Empty;
|
||||
string category = nodeAttribute?.Category ?? string.Empty;
|
||||
string helpUrl = nodeAttribute?.HelpUrl ?? string.Empty;
|
||||
|
||||
NodeData nodeData = new(plugin, nodeType, name, description, category, helpUrl, nodeAttribute?.InputType, nodeAttribute?.OutputType, (s, e) => CreateNode(s, e, nodeType));
|
||||
return NodeTypeStore.Add(nodeData);
|
||||
}
|
||||
|
||||
public TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color)
|
||||
{
|
||||
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
|
||||
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||
|
||||
return NodeTypeStore.AddColor(type, color, plugin);
|
||||
return new TypeColorRegistration(type, baseColor, Constants.CorePluginFeature);
|
||||
}
|
||||
|
||||
public string ExportScript(NodeScript nodeScript)
|
||||
@@ -103,33 +53,6 @@ internal class NodeService : INodeService
|
||||
|
||||
target.LoadFromEntity(entity);
|
||||
}
|
||||
|
||||
private INode CreateNode(INodeScript script, NodeEntity? entity, Type nodeType)
|
||||
{
|
||||
INode node = _container.Resolve(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
|
||||
if (node is Node concreteNode)
|
||||
concreteNode.Container = _container;
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
node.X = entity.X;
|
||||
node.Y = entity.Y;
|
||||
try
|
||||
{
|
||||
if (node is Node nodeImplementation)
|
||||
nodeImplementation.DeserializeStorage(entity.Storage);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
node.TryInitialize(script);
|
||||
return node;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -153,21 +76,6 @@ public interface INodeService : IArtemisService
|
||||
/// </summary>
|
||||
TypeColorRegistration GetTypeColorRegistration(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a node of the provided <paramref name="nodeType" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin the node belongs to</param>
|
||||
/// <param name="nodeType">The type of node to initialize</param>
|
||||
NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a type with a provided color for use in the node editor
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin making the registration</param>
|
||||
/// <param name="type">The type to associate the color with</param>
|
||||
/// <param name="color">The color to display</param>
|
||||
TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color);
|
||||
|
||||
/// <summary>
|
||||
/// Exports the provided node script to JSON.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,6 +7,9 @@ using System.Threading;
|
||||
|
||||
namespace Artemis.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a monitor that efficiently keeps track of running processes.
|
||||
/// </summary>
|
||||
public static partial class ProcessMonitor
|
||||
{
|
||||
#region Properties & Fields
|
||||
@@ -15,8 +18,11 @@ public static partial class ProcessMonitor
|
||||
|
||||
private static Timer? _timer;
|
||||
|
||||
private static Dictionary<int, ProcessInfo> _processes = new();
|
||||
private static readonly Dictionary<int, ProcessInfo> _processes = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets an immutable array of the current processes.
|
||||
/// </summary>
|
||||
public static ImmutableArray<ProcessInfo> Processes
|
||||
{
|
||||
get
|
||||
@@ -25,9 +31,17 @@ public static partial class ProcessMonitor
|
||||
return _processes.Values.ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the date time at which the last update took place.
|
||||
/// </summary>
|
||||
public static DateTime LastUpdate { get; private set; }
|
||||
|
||||
private static TimeSpan _updateInterval = TimeSpan.FromSeconds(1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interval at which to update the list of processes.
|
||||
/// </summary>
|
||||
public static TimeSpan UpdateInterval
|
||||
{
|
||||
get => _updateInterval;
|
||||
@@ -40,6 +54,9 @@ public static partial class ProcessMonitor
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the monitoring has started.
|
||||
/// </summary>
|
||||
public static bool IsStarted
|
||||
{
|
||||
get
|
||||
@@ -53,7 +70,14 @@ public static partial class ProcessMonitor
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a new process is started.
|
||||
/// </summary>
|
||||
public static event EventHandler<ProcessEventArgs>? ProcessStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a process is stopped.
|
||||
/// </summary>
|
||||
public static event EventHandler<ProcessEventArgs>? ProcessStopped;
|
||||
|
||||
#endregion
|
||||
@@ -69,6 +93,9 @@ public static partial class ProcessMonitor
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Starts monitoring processes.
|
||||
/// </summary>
|
||||
public static void Start()
|
||||
{
|
||||
lock (LOCK)
|
||||
@@ -87,6 +114,9 @@ public static partial class ProcessMonitor
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops monitoring processes.
|
||||
/// </summary>
|
||||
public static void Stop()
|
||||
{
|
||||
lock (LOCK)
|
||||
@@ -100,7 +130,7 @@ public static partial class ProcessMonitor
|
||||
FreeBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the specified process is running
|
||||
/// </summary>
|
||||
@@ -111,7 +141,7 @@ public static partial class ProcessMonitor
|
||||
{
|
||||
if (!IsStarted || (processName == null && processLocation == null))
|
||||
return false;
|
||||
|
||||
|
||||
lock (LOCK)
|
||||
{
|
||||
return _processes.Values.Any(x => IsProcessRunning(x, processName, processLocation));
|
||||
@@ -130,19 +160,19 @@ public static partial class ProcessMonitor
|
||||
OnProcessStopped(info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool IsProcessRunning(ProcessInfo info, string? processName, string? processLocation)
|
||||
{
|
||||
if (processName != null && processLocation != null)
|
||||
return string.Equals(info.ProcessName, processName, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
string.Equals(Path.GetDirectoryName(info.Executable), processLocation, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
|
||||
if (processName != null)
|
||||
return string.Equals(info.ProcessName, processName, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
|
||||
if (processLocation != null)
|
||||
return string.Equals(Path.GetDirectoryName(info.Executable), processLocation, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -152,7 +182,10 @@ public static partial class ProcessMonitor
|
||||
{
|
||||
ProcessStarted?.Invoke(null, new ProcessEventArgs(processInfo));
|
||||
}
|
||||
catch { /* Subscribers are idiots! */ }
|
||||
catch
|
||||
{
|
||||
/* Subscribers are idiots! */
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnProcessStopped(ProcessInfo processInfo)
|
||||
@@ -161,7 +194,10 @@ public static partial class ProcessMonitor
|
||||
{
|
||||
ProcessStopped?.Invoke(null, new ProcessEventArgs(processInfo));
|
||||
}
|
||||
catch { /* Subscribers are idiots! */ }
|
||||
catch
|
||||
{
|
||||
/* Subscribers are idiots! */
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -42,6 +42,7 @@ internal class LayerBrushService : ILayerBrushService
|
||||
BrushType = "SolidBrush"
|
||||
});
|
||||
|
||||
defaultReference.Value ??= new LayerBrushReference();
|
||||
defaultReference.Value.LayerBrushProviderId ??= "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba";
|
||||
defaultReference.Value.BrushType ??= "SolidBrush";
|
||||
return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor;
|
||||
|
||||
@@ -8,8 +8,10 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Migrations;
|
||||
using Artemis.Storage.Repositories.Interfaces;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serilog;
|
||||
using SkiaSharp;
|
||||
|
||||
@@ -24,9 +26,10 @@ internal class ProfileService : IProfileService
|
||||
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
|
||||
private readonly List<ProfileCategory> _profileCategories;
|
||||
private readonly IProfileRepository _profileRepository;
|
||||
private readonly List<IProfileMigration> _profileMigrators;
|
||||
private readonly List<Exception> _renderExceptions = new();
|
||||
private readonly List<Exception> _updateExceptions = new();
|
||||
|
||||
|
||||
private DateTime _lastRenderExceptionLog;
|
||||
private DateTime _lastUpdateExceptionLog;
|
||||
|
||||
@@ -35,13 +38,15 @@ internal class ProfileService : IProfileService
|
||||
IPluginManagementService pluginManagementService,
|
||||
IInputService inputService,
|
||||
IDeviceService deviceService,
|
||||
IProfileRepository profileRepository)
|
||||
IProfileRepository profileRepository,
|
||||
List<IProfileMigration> profileMigrators)
|
||||
{
|
||||
_logger = logger;
|
||||
_profileCategoryRepository = profileCategoryRepository;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_deviceService = deviceService;
|
||||
_profileRepository = profileRepository;
|
||||
_profileMigrators = profileMigrators;
|
||||
_profileCategories = new List<ProfileCategory>(_profileCategoryRepository.GetAll().Select(c => new ProfileCategory(c)).OrderBy(c => c.Order));
|
||||
|
||||
_deviceService.LedsChanged += DeviceServiceOnLedsChanged;
|
||||
@@ -58,7 +63,7 @@ internal class ProfileService : IProfileService
|
||||
public ProfileConfiguration? FocusProfile { get; set; }
|
||||
public ProfileElement? FocusProfileElement { get; set; }
|
||||
public bool UpdateFocusProfile { get; set; }
|
||||
|
||||
|
||||
public bool ProfileRenderingDisabled { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -221,7 +226,7 @@ internal class ProfileService : IProfileService
|
||||
return profileConfiguration.Profile;
|
||||
}
|
||||
|
||||
ProfileEntity profileEntity;
|
||||
ProfileEntity? profileEntity;
|
||||
try
|
||||
{
|
||||
profileEntity = _profileRepository.Get(profileConfiguration.Entity.ProfileId);
|
||||
@@ -280,7 +285,7 @@ internal class ProfileService : IProfileService
|
||||
{
|
||||
DeactivateProfile(profileConfiguration);
|
||||
|
||||
ProfileEntity profileEntity = _profileRepository.Get(profileConfiguration.Entity.ProfileId);
|
||||
ProfileEntity? profileEntity = _profileRepository.Get(profileConfiguration.Entity.ProfileId);
|
||||
if (profileEntity == null)
|
||||
return;
|
||||
|
||||
@@ -353,7 +358,7 @@ internal class ProfileService : IProfileService
|
||||
|
||||
DeactivateProfile(profileConfiguration);
|
||||
SaveProfileCategory(profileConfiguration.Category);
|
||||
ProfileEntity profileEntity = _profileRepository.Get(profileConfiguration.Entity.ProfileId);
|
||||
ProfileEntity? profileEntity = _profileRepository.Get(profileConfiguration.Entity.ProfileId);
|
||||
if (profileEntity != null)
|
||||
_profileRepository.Remove(profileEntity);
|
||||
|
||||
@@ -461,7 +466,12 @@ internal class ProfileService : IProfileService
|
||||
|
||||
await using Stream profileStream = profileEntry.Open();
|
||||
using StreamReader profileReader = new(profileStream);
|
||||
ProfileEntity? profileEntity = JsonConvert.DeserializeObject<ProfileEntity>(await profileReader.ReadToEndAsync(), IProfileService.ExportSettings);
|
||||
JObject? profileJson = JsonConvert.DeserializeObject<JObject>(await profileReader.ReadToEndAsync(), IProfileService.ExportSettings);
|
||||
|
||||
// Before deserializing, apply any pending migrations
|
||||
MigrateProfile(configurationEntity, profileJson);
|
||||
|
||||
ProfileEntity? profileEntity = profileJson?.ToObject<ProfileEntity>(JsonSerializer.Create(IProfileService.ExportSettings));
|
||||
if (profileEntity == null)
|
||||
throw new ArtemisCoreException("Could not import profile, failed to deserialize profile.json");
|
||||
|
||||
@@ -514,10 +524,10 @@ internal class ProfileService : IProfileService
|
||||
public async Task<ProfileConfiguration> OverwriteProfile(MemoryStream archiveStream, ProfileConfiguration profileConfiguration)
|
||||
{
|
||||
ProfileConfiguration imported = await ImportProfile(archiveStream, profileConfiguration.Category, true, true, null, profileConfiguration.Order + 1);
|
||||
|
||||
|
||||
DeleteProfile(profileConfiguration);
|
||||
SaveProfileCategory(imported.Category);
|
||||
|
||||
|
||||
return imported;
|
||||
}
|
||||
|
||||
@@ -545,6 +555,21 @@ internal class ProfileService : IProfileService
|
||||
}
|
||||
}
|
||||
|
||||
private void MigrateProfile(ProfileConfigurationEntity configurationEntity, JObject? profileJson)
|
||||
{
|
||||
if (profileJson == null)
|
||||
return;
|
||||
|
||||
foreach (IProfileMigration profileMigrator in _profileMigrators.OrderBy(m => m.Version))
|
||||
{
|
||||
if (profileMigrator.Version <= configurationEntity.Version)
|
||||
continue;
|
||||
|
||||
profileMigrator.Migrate(profileJson);
|
||||
configurationEntity.Version = profileMigrator.Version;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates all missing LEDs on all currently active profiles
|
||||
/// </summary>
|
||||
|
||||
@@ -100,7 +100,7 @@ public interface IWebServerService : IArtemisService
|
||||
/// <summary>
|
||||
/// Removes an existing Web API controller and restarts the web server
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
|
||||
/// <param name="registration">The registration of the controller to remove.</param>
|
||||
void RemoveController(WebApiControllerRegistration registration);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,16 +13,13 @@ internal class NodeTypeStore
|
||||
|
||||
public static NodeTypeRegistration Add(NodeData nodeData)
|
||||
{
|
||||
if (nodeData.Plugin == null)
|
||||
throw new ArtemisCoreException("Cannot add a data binding modifier type that is not associated with a plugin");
|
||||
|
||||
NodeTypeRegistration typeRegistration;
|
||||
lock (Registrations)
|
||||
{
|
||||
if (Registrations.Any(r => r.NodeData == nodeData))
|
||||
throw new ArtemisCoreException($"Data binding modifier type store already contains modifier '{nodeData.Name}'");
|
||||
|
||||
typeRegistration = new NodeTypeRegistration(nodeData, nodeData.Plugin) {IsInStore = true};
|
||||
typeRegistration = new NodeTypeRegistration(nodeData, nodeData.Provider) {IsInStore = true};
|
||||
Registrations.Add(typeRegistration);
|
||||
}
|
||||
|
||||
@@ -60,24 +57,12 @@ internal class NodeTypeStore
|
||||
}
|
||||
}
|
||||
|
||||
public static Plugin? GetPlugin(INode node)
|
||||
{
|
||||
Type nodeType = node.GetType();
|
||||
lock (Registrations)
|
||||
{
|
||||
return Registrations.FirstOrDefault(r => r.NodeData.Type == nodeType)?.Plugin;
|
||||
}
|
||||
}
|
||||
|
||||
public static TypeColorRegistration AddColor(Type type, SKColor color, Plugin plugin)
|
||||
public static TypeColorRegistration AddColor(Type type, SKColor color, PluginFeature pluginFeature)
|
||||
{
|
||||
TypeColorRegistration typeColorRegistration;
|
||||
lock (ColorRegistrations)
|
||||
{
|
||||
if (ColorRegistrations.Any(r => r.Type == type))
|
||||
throw new ArtemisCoreException($"Node color store already contains a color for '{type.Name}'");
|
||||
|
||||
typeColorRegistration = new TypeColorRegistration(type, color, plugin) {IsInStore = true};
|
||||
typeColorRegistration = new TypeColorRegistration(type, color, pluginFeature) {IsInStore = true};
|
||||
ColorRegistrations.Add(typeColorRegistration);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ namespace Artemis.Core;
|
||||
/// </summary>
|
||||
public class NodeTypeRegistration
|
||||
{
|
||||
internal NodeTypeRegistration(NodeData nodeData, Plugin plugin)
|
||||
internal NodeTypeRegistration(NodeData nodeData, PluginFeature pluginFeature)
|
||||
{
|
||||
NodeData = nodeData;
|
||||
Plugin = plugin;
|
||||
PluginFeature = pluginFeature;
|
||||
|
||||
Plugin.Disabled += OnDisabled;
|
||||
PluginFeature.Disabled += OnDisabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -23,9 +23,9 @@ public class NodeTypeRegistration
|
||||
public NodeData NodeData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin the node is associated with
|
||||
/// Gets the plugin feature the node is associated with
|
||||
/// </summary>
|
||||
public Plugin Plugin { get; }
|
||||
public PluginFeature PluginFeature { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the registration is in the internal Core store
|
||||
@@ -39,12 +39,12 @@ public class NodeTypeRegistration
|
||||
/// <returns><see langword="true" /> if the entity matches this registration; otherwise <see langword="false" />.</returns>
|
||||
public bool MatchesEntity(NodeEntity entity)
|
||||
{
|
||||
return Plugin.Guid == entity.PluginId && NodeData.Type.Name == entity.Type;
|
||||
return PluginFeature.Id == entity.ProviderId && NodeData.Type.Name == entity.Type;
|
||||
}
|
||||
|
||||
private void OnDisabled(object? sender, EventArgs e)
|
||||
{
|
||||
Plugin.Disabled -= OnDisabled;
|
||||
PluginFeature.Disabled -= OnDisabled;
|
||||
if (IsInStore)
|
||||
NodeTypeStore.Remove(this);
|
||||
}
|
||||
@@ -55,13 +55,13 @@ public class NodeTypeRegistration
|
||||
/// </summary>
|
||||
public class TypeColorRegistration
|
||||
{
|
||||
internal TypeColorRegistration(Type type, SKColor color, Plugin plugin)
|
||||
internal TypeColorRegistration(Type type, SKColor color, PluginFeature pluginFeature)
|
||||
{
|
||||
Type = type;
|
||||
Color = color;
|
||||
Plugin = plugin;
|
||||
PluginFeature = pluginFeature;
|
||||
|
||||
Plugin.Disabled += OnDisabled;
|
||||
PluginFeature.Disabled += OnDisabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,9 +80,9 @@ public class TypeColorRegistration
|
||||
public SKColor DarkenedColor => Color.Darken(0.35f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin type color is associated with
|
||||
/// Gets the plugin feature this type color is associated with
|
||||
/// </summary>
|
||||
public Plugin Plugin { get; }
|
||||
public PluginFeature PluginFeature { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the registration is in the internal Core store
|
||||
@@ -91,7 +91,7 @@ public class TypeColorRegistration
|
||||
|
||||
private void OnDisabled(object? sender, EventArgs e)
|
||||
{
|
||||
Plugin.Disabled -= OnDisabled;
|
||||
PluginFeature.Disabled -= OnDisabled;
|
||||
if (IsInStore)
|
||||
NodeTypeStore.RemoveColor(this);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ public interface INode : INotifyPropertyChanged, IBreakableModel
|
||||
/// Gets or sets the ID of the node.
|
||||
/// </summary>
|
||||
Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the node data with information about this node
|
||||
/// </summary>
|
||||
NodeData? NodeData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the node
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Artemis.Core.Nodes;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
|
||||
namespace Artemis.Core;
|
||||
@@ -10,9 +11,9 @@ public class NodeData
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
internal NodeData(Plugin plugin, Type type, string name, string description, string category, string helpUrl, Type? inputType, Type? outputType, Func<INodeScript, NodeEntity?, INode> create)
|
||||
internal NodeData(NodeProvider provider, Type type, string name, string description, string category, string helpUrl, Type? inputType, Type? outputType)
|
||||
{
|
||||
Plugin = plugin;
|
||||
Provider = provider;
|
||||
Type = type;
|
||||
Name = name;
|
||||
Description = description;
|
||||
@@ -20,7 +21,6 @@ public class NodeData
|
||||
HelpUrl = helpUrl;
|
||||
InputType = inputType;
|
||||
OutputType = outputType;
|
||||
_create = create;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -35,14 +35,31 @@ public class NodeData
|
||||
/// <returns>The returning node of type <see cref="Type" /></returns>
|
||||
public INode CreateNode(INodeScript script, NodeEntity? entity)
|
||||
{
|
||||
INode node = _create(script, entity);
|
||||
INode node = (INode) Provider.Plugin.Resolve(Type);
|
||||
node.NodeData = this;
|
||||
if (string.IsNullOrWhiteSpace(node.Name))
|
||||
node.Name = Name;
|
||||
if (string.IsNullOrWhiteSpace(node.Description))
|
||||
node.Description = Description;
|
||||
if (string.IsNullOrWhiteSpace(node.HelpUrl))
|
||||
node.HelpUrl = HelpUrl;
|
||||
|
||||
if (entity != null)
|
||||
{
|
||||
node.X = entity.X;
|
||||
node.Y = entity.Y;
|
||||
try
|
||||
{
|
||||
if (node is Node nodeImplementation)
|
||||
nodeImplementation.DeserializeStorage(entity.Storage);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
node.TryInitialize(script);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -91,11 +108,11 @@ public class NodeData
|
||||
}
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin that provided this node data
|
||||
/// Gets the node provider that provided this node data
|
||||
/// </summary>
|
||||
public Plugin Plugin { get; }
|
||||
public NodeProvider Provider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of <see cref="INode" /> this data represents
|
||||
@@ -132,7 +149,5 @@ public class NodeData
|
||||
/// </summary>
|
||||
public Type? OutputType { get; }
|
||||
|
||||
private readonly Func<INodeScript, NodeEntity?, INode> _create;
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -161,6 +161,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
{
|
||||
foreach (INode node in _nodes)
|
||||
{
|
||||
// ReSharper disable once SuspiciousTypeConversion.Global - Provided by plugins
|
||||
if (node is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
@@ -181,6 +182,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
foreach (INode removeNode in removeNodes)
|
||||
{
|
||||
RemoveNode(removeNode);
|
||||
// ReSharper disable once SuspiciousTypeConversion.Global - Provided by plugins
|
||||
if (removeNode is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
@@ -312,7 +314,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
|
||||
NodeEntity nodeEntity = new()
|
||||
{
|
||||
Id = node.Id,
|
||||
PluginId = NodeTypeStore.GetPlugin(node)?.Guid ?? Constants.CorePlugin.Guid,
|
||||
ProviderId = node.NodeData?.Provider.Id ?? Constants.CorePluginFeature.Id,
|
||||
Type = node.GetType().Name,
|
||||
X = node.X,
|
||||
Y = node.Y,
|
||||
|
||||
@@ -41,6 +41,9 @@ public abstract class Node : BreakableModel, INode
|
||||
set => SetAndNotify(ref _id, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NodeData? NodeData { get; set; }
|
||||
|
||||
private string _name;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -104,8 +107,6 @@ public abstract class Node : BreakableModel, INode
|
||||
/// <inheritdoc />
|
||||
public override string BrokenDisplayName => Name;
|
||||
|
||||
internal IContainer Container { get; set; } = null!;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construtors
|
||||
|
||||
@@ -25,7 +25,9 @@ public abstract class Node<TStorage, TViewModel> : Node<TStorage>, ICustomViewMo
|
||||
/// <param name="nodeScript"></param>
|
||||
public virtual TViewModel GetViewModel(NodeScript nodeScript)
|
||||
{
|
||||
return Container.Resolve<TViewModel>(args: new object[] {this, nodeScript});
|
||||
if (NodeData == null)
|
||||
throw new ArtemisCoreException("Nodes without node data (default nodes or exit nodes) cannot have custom view models");
|
||||
return NodeData.Provider.Plugin.Container.Resolve<TViewModel>(args: new object[] {this, nodeScript});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in new issue
Block a user