1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Merge branch 'development'

This commit is contained in:
Robert 2022-08-30 17:22:42 +02:00
commit 9c72c0550e
12 changed files with 293 additions and 135 deletions

View File

@ -11,17 +11,15 @@ namespace Artemis.Core.Services;
internal class ProcessMonitorService : IProcessMonitorService
{
private readonly ProcessComparer _comparer;
private readonly ILogger _logger;
private Process[] _lastScannedProcesses;
public ProcessMonitorService(ILogger logger)
public ProcessMonitorService()
{
_logger = logger;
_comparer = new ProcessComparer();
_lastScannedProcesses = Process.GetProcesses();
Timer processScanTimer = new(1000);
processScanTimer.Elapsed += OnTimerElapsed;
processScanTimer.Start();
_comparer = new ProcessComparer();
ProcessActivationRequirement.ProcessMonitorService = this;
}
@ -30,16 +28,9 @@ internal class ProcessMonitorService : IProcessMonitorService
{
Process[] newProcesses = Process.GetProcesses();
foreach (Process startedProcess in newProcesses.Except(_lastScannedProcesses, _comparer))
{
ProcessStarted?.Invoke(this, new ProcessEventArgs(startedProcess));
//_logger.Verbose("Started Process: {startedProcess}", startedProcess.ProcessName);
}
foreach (Process stoppedProcess in _lastScannedProcesses.Except(newProcesses, _comparer))
{
ProcessStopped?.Invoke(this, new ProcessEventArgs(stoppedProcess));
//_logger.Verbose("Stopped Process: {stoppedProcess}", stoppedProcess.ProcessName);
}
_lastScannedProcesses = newProcesses;
}

View File

@ -8,7 +8,7 @@ namespace Artemis.Core;
/// <summary>
/// Represents a node script
/// </summary>
public interface INodeScript : INotifyPropertyChanged, IDisposable
public interface INodeScript : INotifyPropertyChanged, IDisposable, IStorageModel
{
/// <summary>
/// Gets the name of the node script.
@ -66,6 +66,11 @@ public interface INodeScript : INotifyPropertyChanged, IDisposable
/// </summary>
/// <param name="node">The node to remove</param>
void RemoveNode(INode node);
/// <summary>
/// Loads missing connections between the nodes of this node script from storage
/// </summary>
void LoadConnections();
}
/// <summary>

View File

@ -12,7 +12,7 @@ namespace Artemis.Core;
/// <summary>
/// Represents a node script
/// </summary>
public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageModel
public abstract class NodeScript : CorePropertyChanged, INodeScript
{
private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e)
{
@ -226,9 +226,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageMod
return node;
}
/// <summary>
/// Loads missing connections between the nodes of this node script from the <see cref="Entity" />
/// </summary>
/// <inheritdoc />
public void LoadConnections()
{
List<INode> nodes = Nodes.ToList();

View File

@ -33,12 +33,14 @@ public class LinuxInputProvider : InputProvider
protected override void Dispose(bool disposing)
{
if (disposing)
{
for (int i = _readers.Count - 1; i >= 0; i--)
{
_readers[i].InputEvent -= OnInputEvent;
_readers[i].Dispose();
_readers.RemoveAt(i);
}
}
base.Dispose(disposing);
}
@ -49,6 +51,7 @@ public class LinuxInputProvider : InputProvider
{
if (sender is not LinuxInputDeviceReader reader)
return;
switch (reader.InputDevice.DeviceType)
{
case LinuxDeviceType.Keyboard:
@ -64,40 +67,34 @@ public class LinuxInputProvider : InputProvider
private void HandleKeyboardData(LinuxInputDevice keyboard, LinuxInputEventArgs args)
{
switch (args.Type)
if (args.Type != LinuxInputEventType.KEY)
return;
KeyboardKey key = InputUtilities.KeyFromKeyCode((LinuxKeyboardKeyCodes) args.Code);
bool isDown = args.Value != 0;
//_logger.Verbose($"Keyboard Key: {(LinuxKeyboardKeyCodes)args.Code} | Down: {isDown}");
LinuxInputDevice.LinuxInputId identifier = keyboard.InputId;
OnIdentifierReceived(identifier, InputDeviceType.Keyboard);
ArtemisDevice? device = null;
try
{
case LinuxInputEventType.KEY:
KeyboardKey key = InputUtilities.KeyFromKeyCode((LinuxKeyboardKeyCodes) args.Code);
bool isDown = args.Value != 0;
//_logger.Verbose($"Keyboard Key: {(LinuxKeyboardKeyCodes)args.Code} | Down: {isDown}");
LinuxInputDevice.LinuxInputId identifier = keyboard.InputId;
OnIdentifierReceived(identifier, InputDeviceType.Keyboard);
ArtemisDevice? device = null;
try
{
device = _inputService.GetDeviceByIdentifier(this, identifier, InputDeviceType.Keyboard);
}
catch (Exception e)
{
_logger.Warning(e, "Failed to retrieve input device by its identifier");
}
OnKeyboardDataReceived(device, key, isDown);
break;
device = _inputService.GetDeviceByIdentifier(this, identifier, InputDeviceType.Keyboard);
}
catch (Exception e)
{
_logger.Warning(e, "Failed to retrieve input device by its identifier");
}
OnKeyboardDataReceived(device, key, isDown);
}
private void HandleMouseData(LinuxInputDevice mouse, LinuxInputEventArgs args)
{
LinuxInputDevice.LinuxInputId identifier = mouse.InputId;
OnIdentifierReceived(identifier, InputDeviceType.Mouse);
ArtemisDevice? device = null;
try
@ -112,13 +109,11 @@ public class LinuxInputProvider : InputProvider
switch (args.Type)
{
case LinuxInputEventType.KEY:
var key = (LinuxKeyboardKeyCodes)args.Code;
if (key == LinuxKeyboardKeyCodes.BTN_TOUCH ||
LinuxKeyboardKeyCodes key = (LinuxKeyboardKeyCodes) args.Code;
if (key == LinuxKeyboardKeyCodes.BTN_TOUCH ||
(key >= LinuxKeyboardKeyCodes.BTN_TOOL_PEN && key <= LinuxKeyboardKeyCodes.BTN_TOOL_QUADTAP))
{
//trackpad input, ignore.
return;
}
//0 - up
//1 - down

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core;
namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
@ -8,9 +9,9 @@ namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
/// </summary>
public class ConnectPins : INodeEditorCommand
{
private readonly List<IPin>? _originalConnections;
private readonly IPin _source;
private readonly IPin _target;
private readonly IPin _output;
private readonly IPin _input;
private readonly IPin? _originalConnection;
/// <summary>
/// Creates a new instance of the <see cref="ConnectPins" /> class.
@ -19,10 +20,18 @@ public class ConnectPins : INodeEditorCommand
/// <param name="target">The target of the connection.</param>
public ConnectPins(IPin source, IPin target)
{
_source = source;
_target = target;
if (source.Direction == PinDirection.Output)
{
_output = source;
_input = target;
}
else
{
_output = target;
_input = source;
}
_originalConnections = _target.Direction == PinDirection.Input ? new List<IPin>(_target.ConnectedTo) : null;
_originalConnection = _input.ConnectedTo.FirstOrDefault();
}
#region Implementation of INodeEditorCommand
@ -33,20 +42,16 @@ public class ConnectPins : INodeEditorCommand
/// <inheritdoc />
public void Execute()
{
if (_target.Direction == PinDirection.Input)
_target.DisconnectAll();
_source.ConnectTo(_target);
_input.DisconnectAll();
_output.ConnectTo(_input);
}
/// <inheritdoc />
public void Undo()
{
_target.DisconnectFrom(_source);
if (_originalConnections == null)
return;
foreach (IPin pin in _originalConnections)
_target.ConnectTo(pin);
_input.DisconnectAll();
if (_originalConnection != null)
_input.ConnectTo(_originalConnection);
}
#endregion

View File

@ -25,6 +25,7 @@ public class DataBindingViewModel : ActivatableViewModelBase
private ObservableAsPropertyHelper<ILayerProperty?>? _layerProperty;
private ObservableAsPropertyHelper<NodeScriptViewModel?>? _nodeScriptViewModel;
private bool _playing;
private bool _editorOpen;
public DataBindingViewModel(IProfileEditorService profileEditorService, INodeVmFactory nodeVmFactory, IWindowService windowService, ISettingsService settingsService)
{
@ -49,11 +50,18 @@ public class DataBindingViewModel : ActivatableViewModelBase
.DisposeWith(d);
_profileEditorService.Playing.CombineLatest(_profileEditorService.SuspendedEditing).Subscribe(tuple => _playing = tuple.First || tuple.Second).DisposeWith(d);
DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(60.0 / 1000), DispatcherPriority.Render, Update);
DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Render, Update);
// TODO: Remove in favor of saving each time a node editor command is executed
DispatcherTimer saveTimer = new(TimeSpan.FromMinutes(2), DispatcherPriority.Normal, Save);
updateTimer.Start();
saveTimer.Start();
Disposable.Create(() =>
{
updateTimer.Stop();
saveTimer.Stop();
_profileEditorService.SaveProfile();
}).DisposeWith(d);
});
@ -74,11 +82,19 @@ public class DataBindingViewModel : ActivatableViewModelBase
public async Task OpenEditor()
{
if (LayerProperty != null && LayerProperty.BaseDataBinding.IsEnabled)
if (LayerProperty == null || !LayerProperty.BaseDataBinding.IsEnabled)
return;
try
{
_editorOpen = true;
await _windowService.ShowDialogAsync<NodeScriptWindowViewModel, bool>(("nodeScript", LayerProperty.BaseDataBinding.Script));
await _profileEditorService.SaveProfileAsync();
}
finally
{
_editorOpen = false;
}
}
private void Update(object? sender, EventArgs e)
@ -89,4 +105,10 @@ public class DataBindingViewModel : ActivatableViewModelBase
LayerProperty?.UpdateDataBinding();
}
private void Save(object? sender, EventArgs e)
{
if (!_editorOpen)
_profileEditorService.SaveProfile();
}
}

View File

@ -12,6 +12,7 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Artemis.UI.Shared.Services.ProfileEditor;
using Avalonia;
using Avalonia.Threading;
using DynamicData;
@ -25,6 +26,7 @@ public class NodeScriptWindowViewModel : DialogViewModelBase<bool>
private readonly INodeEditorService _nodeEditorService;
private readonly INodeService _nodeService;
private readonly ISettingsService _settingsService;
private readonly IProfileService _profileService;
private readonly IWindowService _windowService;
public NodeScriptWindowViewModel(NodeScript nodeScript,
@ -32,6 +34,7 @@ public class NodeScriptWindowViewModel : DialogViewModelBase<bool>
INodeEditorService nodeEditorService,
INodeVmFactory vmFactory,
ISettingsService settingsService,
IProfileService profileService,
IWindowService windowService)
{
NodeScript = nodeScript;
@ -43,6 +46,7 @@ public class NodeScriptWindowViewModel : DialogViewModelBase<bool>
_nodeService = nodeService;
_nodeEditorService = nodeEditorService;
_settingsService = settingsService;
_profileService = profileService;
_windowService = windowService;
SourceList<NodeData> nodeSourceList = new();
@ -60,8 +64,17 @@ public class NodeScriptWindowViewModel : DialogViewModelBase<bool>
this.WhenActivated(d =>
{
DispatcherTimer updateTimer = new(TimeSpan.FromMilliseconds(25.0 / 1000), DispatcherPriority.Normal, Update);
// TODO: Remove in favor of saving each time a node editor command is executed
DispatcherTimer saveTimer = new(TimeSpan.FromMinutes(2), DispatcherPriority.Normal, Save);
updateTimer.Start();
Disposable.Create(() => updateTimer.Stop()).DisposeWith(d);
saveTimer.Start();
Disposable.Create(() =>
{
updateTimer.Stop();
saveTimer.Stop();
}).DisposeWith(d);
});
}
@ -133,4 +146,10 @@ public class NodeScriptWindowViewModel : DialogViewModelBase<bool>
{
NodeScript.Run();
}
private void Save(object? sender, EventArgs e)
{
if (NodeScript.Context is Profile profile)
_profileService.SaveProfile(profile, true);
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using Artemis.Core;
@ -41,44 +42,23 @@ public class NodeViewModel : ActivatableViewModelBase
DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v));
SourceList<IPin> nodePins = new();
SourceList<IPinCollection> nodePinCollections = new();
SourceList<PinViewModel> nodePins = new();
SourceList<PinCollectionViewModel> nodePinCollections = new();
// Create observable collections split up by direction
nodePins.Connect()
.Filter(n => n.Direction == PinDirection.Input)
.Transform(p => (PinViewModel) nodeVmFactory.InputPinViewModel(p, nodeScriptViewModel))
.Bind(out ReadOnlyObservableCollection<PinViewModel> inputPins)
.Subscribe();
nodePins.Connect()
.Filter(n => n.Direction == PinDirection.Output)
.Transform(p => (PinViewModel) nodeVmFactory.OutputPinViewModel(p, nodeScriptViewModel))
.Bind(out ReadOnlyObservableCollection<PinViewModel> outputPins)
.Subscribe();
nodePins.Connect().Filter(n => n.Pin.Direction == PinDirection.Input).Bind(out ReadOnlyObservableCollection<PinViewModel> inputPins).Subscribe();
nodePins.Connect().Filter(n => n.Pin.Direction == PinDirection.Output).Bind(out ReadOnlyObservableCollection<PinViewModel> outputPins).Subscribe();
InputPinViewModels = inputPins;
OutputPinViewModels = outputPins;
// Same again but for pin collections
nodePinCollections.Connect()
.Filter(n => n.Direction == PinDirection.Input)
.Transform(c => (PinCollectionViewModel) nodeVmFactory.InputPinCollectionViewModel(c, nodeScriptViewModel))
.Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> inputPinCollections)
.Subscribe();
nodePinCollections.Connect()
.Filter(n => n.Direction == PinDirection.Output)
.Transform(c => (PinCollectionViewModel) nodeVmFactory.OutputPinCollectionViewModel(c, nodeScriptViewModel))
.Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> outputPinCollections)
.Subscribe();
nodePinCollections.Connect().Filter(n => n.PinCollection.Direction == PinDirection.Input).Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> inputPinCollections).Subscribe();
nodePinCollections.Connect().Filter(n => n.PinCollection.Direction == PinDirection.Output).Bind(out ReadOnlyObservableCollection<PinCollectionViewModel> outputPinCollections).Subscribe();
InputPinCollectionViewModels = inputPinCollections;
OutputPinCollectionViewModels = outputPinCollections;
// Create a single observable collection containing all pin view models
InputPinViewModels.ToObservableChangeSet()
.Merge(OutputPinViewModels.ToObservableChangeSet())
.Merge(InputPinCollectionViewModels.ToObservableChangeSet().TransformMany(c => c.PinViewModels))
.Merge(OutputPinCollectionViewModels.ToObservableChangeSet().TransformMany(c => c.PinViewModels))
.Bind(out ReadOnlyObservableCollection<PinViewModel> pins)
.Subscribe();
nodePins.Connect().Merge(nodePinCollections.Connect().TransformMany(c => c.PinViewModels)).Bind(out ReadOnlyObservableCollection<PinViewModel> pins).Subscribe();
PinViewModels = pins;
@ -101,30 +81,54 @@ public class NodeViewModel : ActivatableViewModelBase
// Subscribe to pin changes
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(x => Node.PinAdded += x, x => Node.PinAdded -= x)
.Subscribe(p => nodePins.Add(p.EventArgs.Value))
.Subscribe(p =>
{
if (p.EventArgs.Value.Direction == PinDirection.Input)
nodePins.Add(nodeVmFactory.InputPinViewModel(p.EventArgs.Value, nodeScriptViewModel));
else
nodePins.Add(nodeVmFactory.OutputPinViewModel(p.EventArgs.Value, nodeScriptViewModel));
})
.DisposeWith(d);
Observable.FromEventPattern<SingleValueEventArgs<IPin>>(x => Node.PinRemoved += x, x => Node.PinRemoved -= x)
.Subscribe(p => nodePins.Remove(p.EventArgs.Value))
.Subscribe(p => nodePins!.Remove(nodePins.Items.FirstOrDefault(vm => vm.Pin == p.EventArgs.Value)))
.DisposeWith(d);
nodePins.Edit(l =>
{
l.Clear();
l.AddRange(Node.Pins);
foreach (IPin nodePin in Node.Pins)
{
if (nodePin.Direction == PinDirection.Input)
l.Add(nodeVmFactory.InputPinViewModel(nodePin, nodeScriptViewModel));
else
l.Add(nodeVmFactory.OutputPinViewModel(nodePin, nodeScriptViewModel));
}
});
// Subscribe to pin collection changes
Observable.FromEventPattern<SingleValueEventArgs<IPinCollection>>(x => Node.PinCollectionAdded += x, x => Node.PinCollectionAdded -= x)
.Subscribe(p => nodePinCollections.Add(p.EventArgs.Value))
.Subscribe(p =>
{
if (p.EventArgs.Value.Direction == PinDirection.Input)
nodeVmFactory.InputPinCollectionViewModel(p.EventArgs.Value, nodeScriptViewModel);
else
nodeVmFactory.OutputPinCollectionViewModel(p.EventArgs.Value, nodeScriptViewModel);
})
.DisposeWith(d);
Observable.FromEventPattern<SingleValueEventArgs<IPinCollection>>(x => Node.PinCollectionRemoved += x, x => Node.PinCollectionRemoved -= x)
.Subscribe(p => nodePinCollections.Remove(p.EventArgs.Value))
.Subscribe(p => nodePinCollections!.Remove(nodePinCollections.Items.FirstOrDefault(vm => vm.PinCollection == p.EventArgs.Value)))
.DisposeWith(d);
nodePinCollections.Edit(l =>
{
l.Clear();
l.AddRange(Node.PinCollections);
foreach (IPinCollection nodePinCollection in Node.PinCollections)
{
if (nodePinCollection.Direction == PinDirection.Input)
l.Add(nodeVmFactory.InputPinCollectionViewModel(nodePinCollection, nodeScriptViewModel));
else
l.Add(nodeVmFactory.OutputPinCollectionViewModel(nodePinCollection, nodeScriptViewModel));
}
});
if (Node is Node coreNode)
CustomNodeViewModel = coreNode.GetCustomViewModel(nodeScriptViewModel.NodeScript);
});

View File

@ -0,0 +1,97 @@
using Artemis.Core;
using Artemis.Core.Events;
using Humanizer;
namespace Artemis.VisualScripting.Nodes.Branching;
[Node("Switch (Enum)", "Outputs the input that corresponds to the switch value", "Operators", InputType = typeof(Enum), OutputType = typeof(object))]
public class EnumSwitchNode : Node
{
private readonly Dictionary<Enum, InputPin> _inputPins;
public EnumSwitchNode() : base("Enum Branch", "desc")
{
_inputPins = new Dictionary<Enum, InputPin>();
Output = CreateOutputPin(typeof(object), "Result");
SwitchValue = CreateInputPin<Enum>("Switch");
SwitchValue.PinConnected += OnSwitchPinConnected;
SwitchValue.PinDisconnected += OnSwitchPinDisconnected;
}
public OutputPin Output { get; }
public InputPin<Enum> SwitchValue { get; }
public override void Evaluate()
{
if (SwitchValue.Value is null)
{
Output.Value = null;
return;
}
if (!_inputPins.TryGetValue(SwitchValue.Value, out InputPin? pin))
{
Output.Value = null;
return;
}
if (pin.ConnectedTo.Count == 0)
{
Output.Value = null;
return;
}
Output.Value = pin.Value;
}
private void OnInputPinDisconnected(object? sender, SingleValueEventArgs<IPin> e)
{
// if this is the last pin to disconnect, reset the type.
if (_inputPins.Values.All(i => i.ConnectedTo.Count == 0))
ChangeType(typeof(object));
}
private void OnInputPinConnected(object? sender, SingleValueEventArgs<IPin> e)
{
// change the type of our inputs and output
// depending on the first node the user connects to
ChangeType(e.Value.Type);
}
private void OnSwitchPinConnected(object? sender, SingleValueEventArgs<IPin> e)
{
if (SwitchValue.ConnectedTo.Count == 0)
return;
Type enumType = SwitchValue.ConnectedTo[0].Type;
foreach (Enum enumValue in Enum.GetValues(enumType).Cast<Enum>())
{
InputPin pin = CreateOrAddInputPin(typeof(object), enumValue.ToString().Humanize(LetterCasing.Sentence));
pin.PinConnected += OnInputPinConnected;
pin.PinDisconnected += OnInputPinDisconnected;
_inputPins[enumValue] = pin;
}
}
private void OnSwitchPinDisconnected(object? sender, SingleValueEventArgs<IPin> e)
{
foreach (InputPin input in _inputPins.Values)
{
input.PinConnected -= OnInputPinConnected;
input.PinDisconnected -= OnInputPinDisconnected;
RemovePin(input);
}
_inputPins.Clear();
ChangeType(typeof(object));
}
private void ChangeType(Type type)
{
foreach (InputPin input in _inputPins.Values)
input.ChangeType(type);
Output.ChangeType(type);
}
}

View File

@ -48,9 +48,7 @@ public class DataModelEventCycleNode : Node<DataModelPathEntity, DataModelEventC
public override void Evaluate()
{
object? pathValue = _dataModelPath?.GetValue();
bool hasTriggered = pathValue is IDataModelEvent dataModelEvent
? EvaluateEvent(dataModelEvent)
: EvaluateValue(pathValue);
bool hasTriggered = pathValue is IDataModelEvent dataModelEvent ? EvaluateEvent(dataModelEvent) : EvaluateValue(pathValue);
if (hasTriggered)
{

View File

@ -1,7 +1,6 @@
using System.Linq.Expressions;
using System.Reflection;
using Artemis.Core;
using Artemis.Core.Events;
using Artemis.Core.Modules;
using Artemis.Storage.Entities.Profile;
using Artemis.VisualScripting.Nodes.DataModel.Screens;
@ -41,6 +40,47 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
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)
@ -72,34 +112,12 @@ public class DataModelEventNode : Node<DataModelPathEntity, DataModelEventNodeCu
_propertyPins.Add(expression, CreateOrAddOutputPin(propertyInfo.PropertyType, propertyInfo.Name.Humanize()));
}
}
public override void Evaluate()
private void DataModelPathOnPathValidated(object? sender, EventArgs e)
{
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;
old?.Dispose();
object? pathValue = _dataModelPath?.GetValue();
if (pathValue is IDataModelEvent dataModelEvent)
CreatePins(dataModelEvent);
// Update the output pin now that the type is known and attempt to restore the connection that was likely missing
UpdateOutputPins();
Script?.LoadConnections();
}
/// <inheritdoc />

View File

@ -1,7 +1,6 @@
using Artemis.Core;
using Artemis.Storage.Entities.Profile;
using Artemis.VisualScripting.Nodes.DataModel.Screens;
using Avalonia.Threading;
namespace Artemis.VisualScripting.Nodes.DataModel;
@ -61,17 +60,24 @@ public class DataModelNode : Node<DataModelPathEntity, DataModelNodeCustomViewMo
private void UpdateDataModelPath()
{
DataModelPath? old = _dataModelPath;
old?.Dispose();
_dataModelPath = Storage != null ? new DataModelPath(Storage) : null;
if (_dataModelPath != null)
_dataModelPath.PathValidated += DataModelPathOnPathValidated;
if (old != null)
{
old.PathValidated -= DataModelPathOnPathValidated;
old.Dispose();
}
UpdateOutputPin();
}
private void DataModelPathOnPathValidated(object? sender, EventArgs e)
{
Dispatcher.UIThread.InvokeAsync(UpdateOutputPin);
// Update the output pin now that the type is known and attempt to restore the connection that was likely missing
UpdateOutputPin();
Script?.LoadConnections();
}
/// <inheritdoc />