1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
Robert 88f01abe0d Data model node - Fixed pins disconnecting after restart
Data model event node - Fixed pins disconnecting after restart
Node editor - Fixed connecting already connected input pins to an output pin causing a crash
Node editor - Added auto-save every 2 minutes while nodes are open (profiles are already auto-saved whenever you make a change, nodes will get the same treatment later)
2022-08-30 17:22:36 +02:00

128 lines
4.8 KiB
C#

using System.Linq.Expressions;
using System.Reflection;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Storage.Entities.Profile;
using Artemis.VisualScripting.Nodes.DataModel.Screens;
using Humanizer;
namespace Artemis.VisualScripting.Nodes.DataModel;
[Node("Data Model-Event", "Outputs the latest values of a data model event.", "Data Model", OutputType = typeof(object))]
public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCustomViewModel>, IDisposable
{
private readonly Dictionary<Func<DataModelEventArgs, object>, OutputPin> _propertyPins;
private DataModelPath? _dataModelPath;
private IDataModelEvent? _dataModelEvent;
public DataModelEventNode() : base("Data Model-Event", "Outputs the latest values of a data model event.")
{
_propertyPins = new Dictionary<Func<DataModelEventArgs, object>, OutputPin>();
TimeSinceLastTrigger = CreateOutputPin<Numeric>("Time since trigger");
TriggerCount = CreateOutputPin<Numeric>("Trigger count");
// Monitor storage for changes
StorageModified += (_, _) => UpdateDataModelPath();
}
public INodeScript? Script { get; set; }
public OutputPin<Numeric> TimeSinceLastTrigger { get; }
public OutputPin<Numeric> TriggerCount { get; }
public override void Initialize(INodeScript script)
{
Script = script;
if (Storage == null)
return;
UpdateDataModelPath();
}
public override void Evaluate()
{
object? pathValue = _dataModelPath?.GetValue();
if (pathValue is not IDataModelEvent dataModelEvent)
return;
TimeSinceLastTrigger.Value = new Numeric(dataModelEvent.TimeSinceLastTrigger.TotalMilliseconds);
TriggerCount.Value = new Numeric(dataModelEvent.TriggerCount);
foreach ((Func<DataModelEventArgs, object> propertyAccessor, OutputPin outputPin) in _propertyPins)
{
if (!outputPin.ConnectedTo.Any())
continue;
object value = dataModelEvent.LastEventArgumentsUntyped != null ? propertyAccessor(dataModelEvent.LastEventArgumentsUntyped) : outputPin.Type.GetDefault()!;
outputPin.Value = outputPin.IsNumeric ? new Numeric(value) : value;
}
}
private void UpdateDataModelPath()
{
DataModelPath? old = _dataModelPath;
_dataModelPath = Storage != null ? new DataModelPath(Storage) : null;
if (_dataModelPath != null)
_dataModelPath.PathValidated += DataModelPathOnPathValidated;
if (old != null)
{
old.PathValidated -= DataModelPathOnPathValidated;
old.Dispose();
}
UpdateOutputPins();
}
private void UpdateOutputPins()
{
object? pathValue = _dataModelPath?.GetValue();
if (pathValue is IDataModelEvent dataModelEvent)
CreatePins(dataModelEvent);
}
private void CreatePins(IDataModelEvent? dataModelEvent)
{
if (_dataModelEvent == dataModelEvent)
return;
List<IPin> pins = Pins.Skip(2).ToList();
while (pins.Any())
RemovePin((Pin) pins.First());
_propertyPins.Clear();
_dataModelEvent = dataModelEvent;
if (dataModelEvent == null)
return;
foreach (PropertyInfo propertyInfo in dataModelEvent.ArgumentsType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof(DataModelIgnoreAttribute))))
{
// Expect an IDataModelEvent
ParameterExpression eventParameter = Expression.Parameter(typeof(DataModelEventArgs), "event");
// Cast it to the actual event type
UnaryExpression eventCast = Expression.Convert(eventParameter, propertyInfo.DeclaringType!);
// Access the property
MemberExpression accessor = Expression.Property(eventCast, propertyInfo);
// Cast the property to an object (sadly boxing)
UnaryExpression objectCast = Expression.Convert(accessor, typeof(object));
// Compile the resulting expression
Func<DataModelEventArgs, object> expression = Expression.Lambda<Func<DataModelEventArgs, object>>(objectCast, eventParameter).Compile();
_propertyPins.Add(expression, CreateOrAddOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
}
}
private void DataModelPathOnPathValidated(object? sender, EventArgs e)
{
// Update the output pin now that the type is known and attempt to restore the connection that was likely missing
UpdateOutputPins();
Script?.LoadConnections();
}
/// <inheritdoc />
public void Dispose()
{
_dataModelPath?.Dispose();
}
}