mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
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)
128 lines
4.8 KiB
C#
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();
|
|
}
|
|
} |