mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 11:26:27 +00:00
Nodes - Rewrote storage
This commit is contained in:
1 parent
3dfc25b092
commit
5b183d3010
23 files changed
+270
-134
No files matched your search
@@ -13,7 +13,7 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
{
|
||||
private readonly string _displayName;
|
||||
private readonly EventConditionEntity _entity;
|
||||
private readonly EventDefaultNode _eventNode;
|
||||
private EventDefaultNode _eventNode;
|
||||
private DataModelPath? _eventPath;
|
||||
private DateTime _lastProcessedTrigger;
|
||||
private EventOverlapMode _overlapMode;
|
||||
@@ -199,6 +199,7 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
Script = _entity.Script != null
|
||||
? new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", _entity.Script, ProfileElement.Profile)
|
||||
: new NodeScript<bool>($"Activate {_displayName}", $"Whether or not the event should activate the {_displayName}", ProfileElement.Profile);
|
||||
UpdateEventNode();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -219,8 +220,15 @@ public class EventCondition : CorePropertyChanged, INodeScriptCondition
|
||||
public void LoadNodeScript()
|
||||
{
|
||||
Script.Load();
|
||||
|
||||
// The load action may have created an event node, use that one over the one we have here
|
||||
INode? existingEventNode = Script.Nodes.FirstOrDefault(n => n.Id == EventDefaultNode.NodeId);
|
||||
if (existingEventNode != null)
|
||||
_eventNode = (EventDefaultNode) existingEventNode;
|
||||
|
||||
UpdateEventNode();
|
||||
Script.LoadConnections();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -253,13 +261,13 @@ public enum EventOverlapMode
|
||||
/// </summary>
|
||||
Restart,
|
||||
|
||||
/// <summary>
|
||||
/// Ignore subsequent event fires until the timeline finishes
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
/// <summary>
|
||||
/// Play another copy of the timeline on top of the current run
|
||||
/// </summary>
|
||||
Copy
|
||||
Copy,
|
||||
|
||||
/// <summary>
|
||||
/// Ignore subsequent event fires until the timeline finishes
|
||||
/// </summary>
|
||||
Ignore
|
||||
}
|
||||
@@ -72,6 +72,30 @@ namespace Artemis.Core
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="Layer" /> class by copying the provided <paramref name="source"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The layer to copy</param>
|
||||
/// <param name="parent">The parent of the layer</param>
|
||||
public Layer(Layer source, ProfileElement parent) : base(parent, parent.Profile)
|
||||
{
|
||||
LayerEntity = CoreJson.DeserializeObject<LayerEntity>(CoreJson.SerializeObject(source.LayerEntity, true), true) ?? new LayerEntity();
|
||||
LayerEntity.Id = Guid.NewGuid();
|
||||
|
||||
Profile = source.Profile;
|
||||
Parent = parent;
|
||||
|
||||
_general = new LayerGeneralProperties();
|
||||
_transform = new LayerTransformProperties();
|
||||
|
||||
_leds = new List<ArtemisLed>();
|
||||
Leds = new ReadOnlyCollection<ArtemisLed>(_leds);
|
||||
|
||||
Adapter = new LayerAdapter(this);
|
||||
Load();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A collection of all the LEDs this layer is assigned to.
|
||||
/// </summary>
|
||||
@@ -349,7 +373,7 @@ namespace Artemis.Core
|
||||
|
||||
if (ShouldBeEnabled)
|
||||
Enable();
|
||||
else if (Timeline.IsFinished)
|
||||
else if (Timeline.IsFinished && !Children.Any())
|
||||
Disable();
|
||||
|
||||
if (Timeline.Delta == TimeSpan.Zero)
|
||||
@@ -365,15 +389,16 @@ namespace Artemis.Core
|
||||
// Remove children that finished their timeline and update the rest
|
||||
for (int index = 0; index < Children.Count; index++)
|
||||
{
|
||||
ProfileElement profileElement = Children[index];
|
||||
if (((Layer) profileElement).Timeline.IsFinished)
|
||||
Layer child = (Layer) Children[index];
|
||||
if (!child.Timeline.IsFinished)
|
||||
{
|
||||
RemoveChild(profileElement);
|
||||
profileElement.Dispose();
|
||||
index--;
|
||||
child.Update(deltaTime);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
profileElement.Update(deltaTime);
|
||||
|
||||
RemoveChild(child);
|
||||
child.Dispose();
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,6 +408,12 @@ namespace Artemis.Core
|
||||
if (Disposed)
|
||||
throw new ObjectDisposedException("Layer");
|
||||
|
||||
RenderSelf(canvas, basePosition);
|
||||
RenderChildren(canvas, basePosition);
|
||||
}
|
||||
|
||||
private void RenderSelf(SKCanvas canvas, SKPointI basePosition)
|
||||
{
|
||||
// Ensure the layer is ready
|
||||
if (!Enabled || Path == null || LayerShape?.Path == null || !General.PropertiesInitialized || !Transform.PropertiesInitialized || !Leds.Any())
|
||||
return;
|
||||
@@ -454,6 +485,13 @@ namespace Artemis.Core
|
||||
Timeline.ClearDelta();
|
||||
}
|
||||
|
||||
private void RenderChildren(SKCanvas canvas, SKPointI basePosition)
|
||||
{
|
||||
// Render children first so they go below
|
||||
for (int i = Children.Count - 1; i >= 0; i--)
|
||||
Children[i].Render(canvas, basePosition);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Enable()
|
||||
{
|
||||
@@ -523,11 +561,11 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public void CreateCopyAsChild()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
// Create a copy of the layer and it's properties
|
||||
|
||||
// Add to children
|
||||
Layer copy = new(this, this);
|
||||
copy.AddLeds(Leds);
|
||||
copy.Enable();
|
||||
copy.Timeline.JumpToStart();
|
||||
AddChild(copy);
|
||||
}
|
||||
|
||||
internal void CalculateRenderProperties()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
@@ -51,11 +52,11 @@ namespace Artemis.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static NodeTypeRegistration? Get(Guid pluginGuid, string type)
|
||||
public static NodeTypeRegistration? Get(NodeEntity entity)
|
||||
{
|
||||
lock (Registrations)
|
||||
{
|
||||
return Registrations.FirstOrDefault(r => r.Plugin.Guid == pluginGuid && r.NodeData.Type.Name == type);
|
||||
return Registrations.FirstOrDefault(r => r.MatchesEntity(entity));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
@@ -37,6 +38,16 @@ namespace Artemis.Core
|
||||
if (IsInStore)
|
||||
NodeTypeStore.Remove(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided entity matches this node type registration.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to check.</param>
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,6 +9,11 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public interface INode : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the node.
|
||||
/// </summary>
|
||||
Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the node
|
||||
/// </summary>
|
||||
|
||||
@@ -59,6 +59,9 @@ namespace Artemis.Core
|
||||
|
||||
/// <summary>
|
||||
/// Removes a node from the script
|
||||
/// <para>
|
||||
/// Note: If the node is <see cref="IDisposable"/> you must dispose it yourself, unless you plan to reuse the node.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="node">The node to remove</param>
|
||||
void RemoveNode(INode node);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a kind of node that cannot be deleted inside a <see cref="INode" />.
|
||||
/// </summary>
|
||||
public interface IDefaultNode : INode
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a kind of node that cannot be deleted inside a <see cref="NodeScript" />.
|
||||
/// </summary>
|
||||
public abstract class DefaultNode : Node, IDefaultNode
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsDefaultNode => true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
protected DefaultNode(Guid id, string name, string description = "") : base(name, description)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -7,19 +7,18 @@ using Humanizer;
|
||||
|
||||
namespace Artemis.Core.Internal
|
||||
{
|
||||
internal class EventDefaultNode : Node
|
||||
internal class EventDefaultNode : DefaultNode
|
||||
{
|
||||
internal static readonly Guid NodeId = new("278735FE-69E9-4A73-A6B8-59E83EE19305");
|
||||
private readonly Dictionary<PropertyInfo, OutputPin> _propertyPins;
|
||||
private readonly List<OutputPin> _pinBucket = new();
|
||||
private IDataModelEvent? _dataModelEvent;
|
||||
|
||||
public EventDefaultNode() : base("Event Arguments", "Contains the event arguments that triggered the evaluation")
|
||||
public EventDefaultNode() : base(NodeId, "Event Arguments", "Contains the event arguments that triggered the evaluation")
|
||||
{
|
||||
_propertyPins = new Dictionary<PropertyInfo, OutputPin>();
|
||||
}
|
||||
|
||||
public override bool IsDefaultNode => true;
|
||||
|
||||
public void CreatePins(IDataModelEvent? dataModelEvent)
|
||||
{
|
||||
if (_dataModelEvent == dataModelEvent)
|
||||
@@ -33,9 +32,12 @@ namespace Artemis.Core.Internal
|
||||
if (dataModelEvent == null)
|
||||
return;
|
||||
|
||||
foreach (PropertyInfo propertyInfo in dataModelEvent.ArgumentsType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
foreach (PropertyInfo propertyInfo in dataModelEvent.ArgumentsType
|
||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof(DataModelIgnoreAttribute))))
|
||||
{
|
||||
_propertyPins.Add(propertyInfo, CreateOrAddOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Evaluate()
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
namespace Artemis.Core.Internal
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core.Internal
|
||||
{
|
||||
internal interface IExitNode : INode
|
||||
{ }
|
||||
{
|
||||
protected static readonly Guid NodeId = new("410C824D-C5E3-4E3A-8080-D50F6C8B83B8");
|
||||
}
|
||||
|
||||
internal class ExitNode<T> : Node, IExitNode
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
|
||||
public InputPin<T> Input { get; }
|
||||
|
||||
public T? Value { get; private set; }
|
||||
@@ -19,6 +23,7 @@
|
||||
|
||||
public ExitNode(string name, string description = "")
|
||||
{
|
||||
Id = IExitNode.NodeId;
|
||||
Name = name;
|
||||
Description = description;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Core.Properties;
|
||||
using Ninject;
|
||||
using Ninject.Parameters;
|
||||
|
||||
@@ -16,6 +15,15 @@ public abstract class Node : CorePropertyChanged, INode
|
||||
public event EventHandler? Resetting;
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
private Guid _id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id
|
||||
{
|
||||
get => _id;
|
||||
set => SetAndNotify(ref _id , value);
|
||||
}
|
||||
|
||||
private string _name;
|
||||
|
||||
@@ -65,7 +73,7 @@ public abstract class Node : CorePropertyChanged, INode
|
||||
public IReadOnlyCollection<IPin> Pins => new ReadOnlyCollection<IPin>(_pins);
|
||||
|
||||
private readonly List<IPinCollection> _pinCollections = new();
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyCollection<IPinCollection> PinCollections => new ReadOnlyCollection<IPinCollection>(_pinCollections);
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ namespace Artemis.Core
|
||||
{
|
||||
private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e)
|
||||
{
|
||||
Load();
|
||||
// Only respond to node changes applicable to the current script
|
||||
if (Entity.Nodes.Any(n => e.TypeRegistration.MatchesEntity(n)))
|
||||
Load();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -153,41 +155,38 @@ namespace Artemis.Core
|
||||
{
|
||||
lock (_nodes)
|
||||
{
|
||||
List<INode> removeNodes = _nodes.Where(n => !n.IsExitNode).ToList();
|
||||
// Remove nodes no longer on the entity
|
||||
List<INode> removeNodes = _nodes.Where(n => Entity.Nodes.All(e => e.Id != n.Id)).ToList();
|
||||
foreach (INode removeNode in removeNodes)
|
||||
{
|
||||
RemoveNode(removeNode);
|
||||
if (removeNode is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Create nodes
|
||||
foreach (NodeEntity entityNode in Entity.Nodes)
|
||||
// Create missing nodes nodes
|
||||
foreach (NodeEntity nodeEntity in Entity.Nodes)
|
||||
{
|
||||
INode? node = LoadNode(entityNode, entityNode.IsExitNode ? ExitNode : null);
|
||||
if (node == null)
|
||||
continue;
|
||||
|
||||
if (!entityNode.IsExitNode)
|
||||
AddNode(node);
|
||||
INode? node = Nodes.FirstOrDefault(n => n.Id == nodeEntity.Id);
|
||||
// If the node already exists, apply the entity to it
|
||||
if (node != null)
|
||||
LoadExistingNode(node, nodeEntity);
|
||||
else
|
||||
{
|
||||
INode? loaded = LoadNode(nodeEntity);
|
||||
if (loaded != null)
|
||||
AddNode(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
LoadConnections();
|
||||
}
|
||||
|
||||
private INode? LoadNode(NodeEntity nodeEntity, INode? node)
|
||||
private void LoadExistingNode(INode node, NodeEntity nodeEntity)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
NodeTypeRegistration? nodeTypeRegistration = NodeTypeStore.Get(nodeEntity.PluginId, nodeEntity.Type);
|
||||
if (nodeTypeRegistration == null)
|
||||
return null;
|
||||
|
||||
// Create the node
|
||||
node = nodeTypeRegistration.NodeData.CreateNode(this, nodeEntity);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.X = nodeEntity.X;
|
||||
node.Y = nodeEntity.Y;
|
||||
}
|
||||
node.X = nodeEntity.X;
|
||||
node.Y = nodeEntity.Y;
|
||||
|
||||
// Restore pin collections
|
||||
foreach (NodePinCollectionEntity entityNodePinCollection in nodeEntity.PinCollections)
|
||||
@@ -201,7 +200,17 @@ namespace Artemis.Core
|
||||
while (collection.Count() < entityNodePinCollection.Amount)
|
||||
collection.Add(collection.CreatePin());
|
||||
}
|
||||
}
|
||||
|
||||
private INode? LoadNode(NodeEntity nodeEntity)
|
||||
{
|
||||
NodeTypeRegistration? nodeTypeRegistration = NodeTypeStore.Get(nodeEntity);
|
||||
if (nodeTypeRegistration == null)
|
||||
return null;
|
||||
|
||||
// Create the node
|
||||
INode node = nodeTypeRegistration.NodeData.CreateNode(this, nodeEntity);
|
||||
LoadExistingNode(node, nodeEntity);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -213,10 +222,10 @@ namespace Artemis.Core
|
||||
List<INode> nodes = Nodes.ToList();
|
||||
foreach (NodeConnectionEntity nodeConnectionEntity in Entity.Connections.OrderBy(p => p.SourcePinCollectionId))
|
||||
{
|
||||
INode? source = nodes.ElementAtOrDefault(nodeConnectionEntity.SourceNode);
|
||||
INode? source = nodes.FirstOrDefault(n => n.Id == nodeConnectionEntity.SourceNode);
|
||||
if (source == null)
|
||||
continue;
|
||||
INode? target = nodes.ElementAtOrDefault(nodeConnectionEntity.TargetNode);
|
||||
INode? target = nodes.FirstOrDefault(n => n.Id == nodeConnectionEntity.TargetNode);
|
||||
if (target == null)
|
||||
continue;
|
||||
|
||||
@@ -263,12 +272,11 @@ namespace Artemis.Core
|
||||
if (Nodes.Count() == 1)
|
||||
return;
|
||||
|
||||
int id = 0;
|
||||
foreach (INode node in Nodes)
|
||||
{
|
||||
NodeEntity nodeEntity = new()
|
||||
{
|
||||
Id = id,
|
||||
Id = node.Id,
|
||||
PluginId = NodeTypeStore.GetPlugin(node)?.Guid ?? Constants.CorePlugin.Guid,
|
||||
Type = node.GetType().Name,
|
||||
X = node.X,
|
||||
@@ -294,7 +302,6 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
Entity.Nodes.Add(nodeEntity);
|
||||
id++;
|
||||
}
|
||||
|
||||
// Store connections
|
||||
@@ -315,7 +322,6 @@ namespace Artemis.Core
|
||||
private void SavePins(INode node, int collectionId, IEnumerable<IPin> pins)
|
||||
{
|
||||
int sourcePinId = 0;
|
||||
List<INode> nodes = Nodes.ToList();
|
||||
foreach (IPin sourcePin in pins.Where(p => p.Direction == PinDirection.Input))
|
||||
{
|
||||
foreach (IPin targetPin in sourcePin.ConnectedTo)
|
||||
@@ -337,11 +343,11 @@ namespace Artemis.Core
|
||||
Entity.Connections.Add(new NodeConnectionEntity
|
||||
{
|
||||
SourceType = sourcePin.Type.Name,
|
||||
SourceNode = nodes.IndexOf(node),
|
||||
SourceNode = node.Id,
|
||||
SourcePinCollectionId = collectionId,
|
||||
SourcePinId = sourcePinId,
|
||||
TargetType = targetPin.Type.Name,
|
||||
TargetNode = nodes.IndexOf(targetPin.Node),
|
||||
TargetNode = targetPin.Node.Id,
|
||||
TargetPinCollectionId = targetPinCollectionId,
|
||||
TargetPinId = targetPinId
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user