Added function in the panel for user to be added new function
This commit is contained in:
97
Examples/Nodify.Calculator/CreateFunctionDialog.xaml
Normal file
97
Examples/Nodify.Calculator/CreateFunctionDialog.xaml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<Window x:Class="Nodify.Calculator.CreateFunctionDialog"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Create New Function"
|
||||||
|
Width="500"
|
||||||
|
Height="520"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
Background="#2D2D30"
|
||||||
|
Foreground="White">
|
||||||
|
<Grid Margin="15">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Function Name -->
|
||||||
|
<TextBlock Text="Function Name:" Grid.Row="0" Margin="0 0 0 4" />
|
||||||
|
<TextBox x:Name="FunctionNameBox" Grid.Row="1" Margin="0 0 0 10"
|
||||||
|
Background="#3E3E42" Foreground="White" Padding="4" />
|
||||||
|
|
||||||
|
<!-- Input Parameters -->
|
||||||
|
<StackPanel Orientation="Horizontal" Grid.Row="2" Margin="0 0 0 4">
|
||||||
|
<TextBlock Text="Input Parameters:" FontWeight="Bold" />
|
||||||
|
<Button Content="➕ Add" Margin="10 0 0 0" Padding="8 2" Click="AddInput_Click"
|
||||||
|
Background="#3E3E42" Foreground="White" Cursor="Hand" />
|
||||||
|
</StackPanel>
|
||||||
|
<DataGrid x:Name="InputParamsGrid" Grid.Row="3" Margin="0 0 0 10"
|
||||||
|
AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="True"
|
||||||
|
Background="#3E3E42" Foreground="White" RowBackground="#3E3E42"
|
||||||
|
AlternatingRowBackground="#333337" GridLinesVisibility="None"
|
||||||
|
HeadersVisibility="Column" BorderBrush="#555">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*" />
|
||||||
|
<DataGridComboBoxColumn Header="Type" SelectedItemBinding="{Binding Type}" Width="120">
|
||||||
|
<DataGridComboBoxColumn.ItemsSource>
|
||||||
|
<x:Array Type="sys:String" xmlns:sys="clr-namespace:System;assembly=System.Runtime">
|
||||||
|
<sys:String>string</sys:String>
|
||||||
|
<sys:String>int</sys:String>
|
||||||
|
<sys:String>double</sys:String>
|
||||||
|
<sys:String>bool</sys:String>
|
||||||
|
<sys:String>float</sys:String>
|
||||||
|
<sys:String>decimal</sys:String>
|
||||||
|
<sys:String>long</sys:String>
|
||||||
|
<sys:String>DateTime</sys:String>
|
||||||
|
<sys:String>object</sys:String>
|
||||||
|
</x:Array>
|
||||||
|
</DataGridComboBoxColumn.ItemsSource>
|
||||||
|
</DataGridComboBoxColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<!-- Output Parameters -->
|
||||||
|
<StackPanel Orientation="Horizontal" Grid.Row="4" Margin="0 0 0 4">
|
||||||
|
<TextBlock Text="Output Parameters:" FontWeight="Bold" />
|
||||||
|
<Button Content="➕ Add" Margin="10 0 0 0" Padding="8 2" Click="AddOutput_Click"
|
||||||
|
Background="#3E3E42" Foreground="White" Cursor="Hand" />
|
||||||
|
</StackPanel>
|
||||||
|
<DataGrid x:Name="OutputParamsGrid" Grid.Row="5" Margin="0 0 0 10"
|
||||||
|
AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="True"
|
||||||
|
Background="#3E3E42" Foreground="White" RowBackground="#3E3E42"
|
||||||
|
AlternatingRowBackground="#333337" GridLinesVisibility="None"
|
||||||
|
HeadersVisibility="Column" BorderBrush="#555">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*" />
|
||||||
|
<DataGridComboBoxColumn Header="Type" SelectedItemBinding="{Binding Type}" Width="120">
|
||||||
|
<DataGridComboBoxColumn.ItemsSource>
|
||||||
|
<x:Array Type="sys:String" xmlns:sys="clr-namespace:System;assembly=System.Runtime">
|
||||||
|
<sys:String>string</sys:String>
|
||||||
|
<sys:String>int</sys:String>
|
||||||
|
<sys:String>double</sys:String>
|
||||||
|
<sys:String>bool</sys:String>
|
||||||
|
<sys:String>float</sys:String>
|
||||||
|
<sys:String>decimal</sys:String>
|
||||||
|
<sys:String>long</sys:String>
|
||||||
|
<sys:String>DateTime</sys:String>
|
||||||
|
<sys:String>object</sys:String>
|
||||||
|
</x:Array>
|
||||||
|
</DataGridComboBoxColumn.ItemsSource>
|
||||||
|
</DataGridComboBoxColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="6">
|
||||||
|
<Button Content="Create" Width="80" Margin="0 0 10 0" Padding="4"
|
||||||
|
Click="OnCreateClick" IsDefault="True" />
|
||||||
|
<Button Content="Cancel" Width="80" Padding="4"
|
||||||
|
Click="OnCancelClick" IsCancel="True" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
52
Examples/Nodify.Calculator/CreateFunctionDialog.xaml.cs
Normal file
52
Examples/Nodify.Calculator/CreateFunctionDialog.xaml.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Nodify.Calculator
|
||||||
|
{
|
||||||
|
public partial class CreateFunctionDialog : Window
|
||||||
|
{
|
||||||
|
public string FunctionName { get; private set; } = string.Empty;
|
||||||
|
public List<FunctionParameterInfo> InputParameters { get; } = new List<FunctionParameterInfo>();
|
||||||
|
public List<FunctionParameterInfo> OutputParameters { get; } = new List<FunctionParameterInfo>();
|
||||||
|
|
||||||
|
private readonly ObservableCollection<FunctionParameterInfo> _inputs = new ObservableCollection<FunctionParameterInfo>();
|
||||||
|
private readonly ObservableCollection<FunctionParameterInfo> _outputs = new ObservableCollection<FunctionParameterInfo>();
|
||||||
|
|
||||||
|
public CreateFunctionDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
InputParamsGrid.ItemsSource = _inputs;
|
||||||
|
OutputParamsGrid.ItemsSource = _outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddInput_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_inputs.Add(new FunctionParameterInfo { Name = $"param{_inputs.Count + 1}", Type = "string" });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddOutput_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_outputs.Add(new FunctionParameterInfo { Name = $"result{_outputs.Count + 1}", Type = "string" });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCreateClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(FunctionNameBox.Text))
|
||||||
|
{
|
||||||
|
MessageBox.Show("Please enter a function name.", "Validation", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionName = FunctionNameBox.Text.Trim();
|
||||||
|
InputParameters.AddRange(_inputs);
|
||||||
|
OutputParameters.AddRange(_outputs);
|
||||||
|
DialogResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCancelClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -433,6 +433,21 @@
|
|||||||
</nodify:Node>
|
</nodify:Node>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate DataType="{x:Type local:FunctionOperationViewModel}">
|
||||||
|
<nodify:Node Header="{Binding Title}"
|
||||||
|
Input="{Binding Input}"
|
||||||
|
Output="{Binding Output}"
|
||||||
|
ToolTip="Double click to edit function"
|
||||||
|
BorderBrush="#FF9800"
|
||||||
|
BorderThickness="2">
|
||||||
|
<nodify:Node.InputBindings>
|
||||||
|
<MouseBinding Gesture="LeftDoubleClick"
|
||||||
|
Command="{Binding DataContext.OpenCalculatorCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||||
|
CommandParameter="{Binding InnerCalculator}" />
|
||||||
|
</nodify:Node.InputBindings>
|
||||||
|
</nodify:Node>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate DataType="{x:Type local:CalculatorOperationViewModel}">
|
<DataTemplate DataType="{x:Type local:CalculatorOperationViewModel}">
|
||||||
<nodify:Node Header="{Binding Title}"
|
<nodify:Node Header="{Binding Title}"
|
||||||
Input="{Binding Input}"
|
Input="{Binding Input}"
|
||||||
@@ -691,6 +706,38 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
|
<!-- Functions Section -->
|
||||||
|
<TextBlock Text="Functions" FontWeight="Bold" Margin="0 8 0 4" />
|
||||||
|
<Button Content="➕ Create New Function"
|
||||||
|
Command="{Binding Calculator.OperationsMenu.CreateFunctionCommand}"
|
||||||
|
Margin="0 0 0 6"
|
||||||
|
Padding="6 3"
|
||||||
|
Cursor="Hand"
|
||||||
|
Background="#FF9800"
|
||||||
|
Foreground="White" />
|
||||||
|
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.AvailableFunctions}"
|
||||||
|
Focusable="False">
|
||||||
|
<ItemsControl.ItemContainerStyle>
|
||||||
|
<Style>
|
||||||
|
<Setter Property="FrameworkElement.Margin" Value="3" />
|
||||||
|
<Setter Property="FrameworkElement.HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="FrameworkElement.Cursor" Value="Hand" />
|
||||||
|
<Setter Property="FrameworkElement.ToolTip" Value="Drag and drop to add this function to the flow" />
|
||||||
|
</Style>
|
||||||
|
</ItemsControl.ItemContainerStyle>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type local:OperationInfoViewModel}">
|
||||||
|
<Border Background="#3E3E42" CornerRadius="3" Padding="6 4"
|
||||||
|
MouseMove="OnNodeDrag" Cursor="Hand">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="ƒ " Foreground="#FF9800" FontWeight="Bold" />
|
||||||
|
<TextBlock Text="{Binding Title}" Foreground="Orange" FontWeight="SemiBold" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|||||||
@@ -428,6 +428,14 @@ namespace Nodify.Calculator
|
|||||||
OnLogMe?.Invoke($"Auth node resolved. Base URL: {_authBaseUrl}, Auth Type: {_authType}");
|
OnLogMe?.Invoke($"Auth node resolved. Base URL: {_authBaseUrl}, Auth Type: {_authType}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Function nodes — execute the inner flow
|
||||||
|
if (op is FunctionOperationViewModel funcOp)
|
||||||
|
{
|
||||||
|
OnLogMe?.Invoke($"Executing function: {funcOp.FunctionName}");
|
||||||
|
ExecuteFunction(funcOp, connections);
|
||||||
|
return;
|
||||||
|
}
|
||||||
OnLogMe?.Invoke($"Starting Execution : {url}");
|
OnLogMe?.Invoke($"Starting Execution : {url}");
|
||||||
var res = GetResponse(url, "get");
|
var res = GetResponse(url, "get");
|
||||||
if (!string.IsNullOrEmpty(res))
|
if (!string.IsNullOrEmpty(res))
|
||||||
@@ -509,16 +517,83 @@ namespace Nodify.Calculator
|
|||||||
|
|
||||||
private static string GetConnectorTextValue(ConnectorViewModel connector)
|
private static string GetConnectorTextValue(ConnectorViewModel connector)
|
||||||
{
|
{
|
||||||
// When the connector is not connected, the user enters text in the Value text box.
|
|
||||||
// Value is a double, so we try to use it as a string representation.
|
|
||||||
// However, for string inputs (URL, token, etc.) the value won't be meaningful as a double.
|
|
||||||
// The user-typed string is actually stored in the Title field for unconnected inputs in some cases,
|
|
||||||
// but here we rely on the ConnectorViewModel.Value being 0 (default) and the actual string
|
|
||||||
// is not captured as double. So we return empty — the AuthOperationViewModel properties are the
|
|
||||||
// primary source set via the node's text boxes.
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExecuteFunction(FunctionOperationViewModel funcOp, ICollection<ConnectionViewModel> connections)
|
||||||
|
{
|
||||||
|
// Propagate outer input values into the function
|
||||||
|
for (int i = 0; i < funcOp.InputParameters.Count; i++)
|
||||||
|
{
|
||||||
|
var outerInputIndex = i + 1; // skip flow triangle
|
||||||
|
if (outerInputIndex < funcOp.Input.Count)
|
||||||
|
{
|
||||||
|
var outerConnector = funcOp.Input[outerInputIndex];
|
||||||
|
var inputCon = connections.FirstOrDefault(c => c.Input == outerConnector);
|
||||||
|
if (inputCon != null)
|
||||||
|
{
|
||||||
|
var sourceNodeId = inputCon.Output.Operation.NodeId;
|
||||||
|
if (outputs.TryGetValue(sourceNodeId, out var val))
|
||||||
|
{
|
||||||
|
outerConnector.Value = double.TryParse(val, out var d) ? d : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger propagation of input values to inner Begin node
|
||||||
|
funcOp.PropagateInputsToInner();
|
||||||
|
|
||||||
|
// Execute the inner calculator's flow (Begin -> End)
|
||||||
|
var innerOps = funcOp.InnerCalculator.Operations;
|
||||||
|
var innerConns = funcOp.InnerCalculator.Connections;
|
||||||
|
|
||||||
|
OnLogMe?.Invoke($" [Function '{funcOp.FunctionName}'] Resolving inner GET variables...");
|
||||||
|
ResolveGetVariableNodes(innerOps, innerConns);
|
||||||
|
|
||||||
|
OnLogMe?.Invoke($" [Function '{funcOp.FunctionName}'] Executing inner chain...");
|
||||||
|
var innerStart = innerOps.FirstOrDefault(n => n.Title?.Equals("Begin", StringComparison.OrdinalIgnoreCase) == true);
|
||||||
|
if (innerStart == null)
|
||||||
|
{
|
||||||
|
OnLogMe?.Invoke($" [Function '{funcOp.FunctionName}'] No Begin node found inside function!", logType.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var visited = new HashSet<string>();
|
||||||
|
TraverseChain(innerStart, "end", innerConns, visited, true);
|
||||||
|
|
||||||
|
// Collect outputs from the inner End node
|
||||||
|
var innerEnd = innerOps.FirstOrDefault(n => n.Title?.Equals("End", StringComparison.OrdinalIgnoreCase) == true);
|
||||||
|
if (innerEnd != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < funcOp.OutputParameters.Count; i++)
|
||||||
|
{
|
||||||
|
var endInputIndex = i + 1; // skip flow triangle
|
||||||
|
if (endInputIndex < innerEnd.Input.Count)
|
||||||
|
{
|
||||||
|
var endConnector = innerEnd.Input[endInputIndex];
|
||||||
|
var endCon = innerConns.FirstOrDefault(c => c.Input == endConnector);
|
||||||
|
if (endCon != null)
|
||||||
|
{
|
||||||
|
var sourceId = endCon.Output.Operation.NodeId;
|
||||||
|
if (outputs.TryGetValue(sourceId, out var resultVal))
|
||||||
|
{
|
||||||
|
var outerOutputIndex = i + 1; // skip flow triangle
|
||||||
|
if (outerOutputIndex < funcOp.Output.Count)
|
||||||
|
{
|
||||||
|
funcOp.Output[outerOutputIndex].Value = double.TryParse(resultVal, out var dv) ? dv : 0;
|
||||||
|
}
|
||||||
|
outputs[funcOp.NodeId] = resultVal;
|
||||||
|
OnLogMe?.Invoke($" [Function '{funcOp.FunctionName}'] Output '{funcOp.OutputParameters[i].Name}' = {resultVal}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OnLogMe?.Invoke($"Function '{funcOp.FunctionName}' execution completed.");
|
||||||
|
}
|
||||||
|
|
||||||
private string GetResponse(string url, string type)
|
private string GetResponse(string url, string type)
|
||||||
{
|
{
|
||||||
string baseURL = !string.IsNullOrWhiteSpace(_authBaseUrl) ? _authBaseUrl : "https://localhost:7107";
|
string baseURL = !string.IsNullOrWhiteSpace(_authBaseUrl) ? _authBaseUrl : "https://localhost:7107";
|
||||||
|
|||||||
119
Examples/Nodify.Calculator/FunctionOperationViewModel.cs
Normal file
119
Examples/Nodify.Calculator/FunctionOperationViewModel.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Nodify.Calculator
|
||||||
|
{
|
||||||
|
public class FunctionOperationViewModel : CalculatorOperationViewModel
|
||||||
|
{
|
||||||
|
private string _functionName = "Function";
|
||||||
|
public string FunctionName
|
||||||
|
{
|
||||||
|
get => _functionName;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _functionName, value))
|
||||||
|
{
|
||||||
|
Title = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FunctionParameterInfo> InputParameters { get; } = new List<FunctionParameterInfo>();
|
||||||
|
public List<FunctionParameterInfo> OutputParameters { get; } = new List<FunctionParameterInfo>();
|
||||||
|
|
||||||
|
private OperationViewModel InnerBegin { get; }
|
||||||
|
private OperationViewModel InnerEnd { get; }
|
||||||
|
|
||||||
|
public FunctionOperationViewModel()
|
||||||
|
{
|
||||||
|
// Add Begin and End nodes inside the inner calculator for flow-based editing
|
||||||
|
InnerBegin = new SystemOperationViewModel
|
||||||
|
{
|
||||||
|
Title = "Begin",
|
||||||
|
SystemOperationType = SystemOperations.BEGIN,
|
||||||
|
Location = new Point(50, 150)
|
||||||
|
};
|
||||||
|
InnerBegin.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||||
|
|
||||||
|
InnerEnd = new SystemOperationViewModel
|
||||||
|
{
|
||||||
|
Title = "End",
|
||||||
|
SystemOperationType = SystemOperations.END,
|
||||||
|
Location = new Point(600, 150)
|
||||||
|
};
|
||||||
|
InnerEnd.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||||
|
|
||||||
|
InnerCalculator.Operations.Add(InnerBegin);
|
||||||
|
InnerCalculator.Operations.Add(InnerEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigureParameters(List<FunctionParameterInfo> inputs, List<FunctionParameterInfo> outputs)
|
||||||
|
{
|
||||||
|
InputParameters.Clear();
|
||||||
|
InputParameters.AddRange(inputs);
|
||||||
|
OutputParameters.Clear();
|
||||||
|
OutputParameters.AddRange(outputs);
|
||||||
|
|
||||||
|
// Add data output connectors to InnerBegin (these feed the inner flow with input values)
|
||||||
|
foreach (var inp in inputs)
|
||||||
|
{
|
||||||
|
InnerBegin.Output.Add(new ConnectorViewModel
|
||||||
|
{
|
||||||
|
Title = $"{inp.Name} ({inp.Type})",
|
||||||
|
Shape = ConnectorShape.Circle,
|
||||||
|
IsInput = false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also add an outer input connector on the function node
|
||||||
|
Input.Add(new ConnectorViewModel
|
||||||
|
{
|
||||||
|
Title = $"{inp.Name} ({inp.Type})"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add data input connectors to InnerEnd (these collect inner flow results)
|
||||||
|
foreach (var outp in outputs)
|
||||||
|
{
|
||||||
|
InnerEnd.Input.Add(new ConnectorViewModel
|
||||||
|
{
|
||||||
|
Title = $"{outp.Name} ({outp.Type})",
|
||||||
|
Shape = ConnectorShape.Circle
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also add an outer output connector on the function node
|
||||||
|
Output.Add(new ConnectorViewModel
|
||||||
|
{
|
||||||
|
Title = $"{outp.Name} ({outp.Type})",
|
||||||
|
IsInput = false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInputValueChanged()
|
||||||
|
{
|
||||||
|
PropagateInputsToInner();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PropagateInputsToInner()
|
||||||
|
{
|
||||||
|
// Propagate outer input values into the InnerBegin's data output connectors
|
||||||
|
// The first output of InnerBegin is the flow triangle, data starts at index 1
|
||||||
|
for (var i = 0; i < InputParameters.Count; i++)
|
||||||
|
{
|
||||||
|
var outerIndex = i; // outer Input: first is flow triangle (index 0), data starts at 1
|
||||||
|
var innerIndex = i + 1; // InnerBegin Output: first is flow triangle, data starts at 1
|
||||||
|
|
||||||
|
if (outerIndex + 1 < Input.Count && innerIndex < InnerBegin.Output.Count)
|
||||||
|
{
|
||||||
|
InnerBegin.Output[innerIndex].Value = Input[outerIndex + 1].Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FunctionParameterInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string Type { get; set; } = "string";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,5 +32,8 @@ namespace Nodify.Calculator
|
|||||||
public string VariableType { get; set; } = string.Empty;
|
public string VariableType { get; set; } = string.Empty;
|
||||||
public string DefaultValue { get; set; } = string.Empty;
|
public string DefaultValue { get; set; } = string.Empty;
|
||||||
public bool IsSimpleVariable { get; set; }
|
public bool IsSimpleVariable { get; set; }
|
||||||
|
public bool IsFunction { get; set; }
|
||||||
|
public List<FunctionParameterInfo> FunctionInputs { get; set; } = new List<FunctionParameterInfo>();
|
||||||
|
public List<FunctionParameterInfo> FunctionOutputs { get; set; } = new List<FunctionParameterInfo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -275,6 +275,24 @@ namespace Nodify.Calculator
|
|||||||
//_o.Input.AddRange(input);
|
//_o.Input.AddRange(input);
|
||||||
return _o;
|
return _o;
|
||||||
case OperationType.System:
|
case OperationType.System:
|
||||||
|
if (info.sysOp == SystemOperations.FUNCTION)
|
||||||
|
{
|
||||||
|
var funcOp = new FunctionOperationViewModel
|
||||||
|
{
|
||||||
|
Title = info.Title,
|
||||||
|
FunctionName = info.Title
|
||||||
|
};
|
||||||
|
// Add flow connectors (triangle)
|
||||||
|
var funcFlowIn = new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle };
|
||||||
|
var funcFlowOut = new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false };
|
||||||
|
funcOp.Input.Add(funcFlowIn);
|
||||||
|
funcOp.Output.Add(funcFlowOut);
|
||||||
|
|
||||||
|
// Configure typed input/output parameters
|
||||||
|
funcOp.ConfigureParameters(info.FunctionInputs, info.FunctionOutputs);
|
||||||
|
return funcOp;
|
||||||
|
}
|
||||||
|
|
||||||
if (info.sysOp == SystemOperations.AUTH)
|
if (info.sysOp == SystemOperations.AUTH)
|
||||||
{
|
{
|
||||||
var authOp = new AuthOperationViewModel
|
var authOp = new AuthOperationViewModel
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ namespace Nodify.Calculator
|
|||||||
public NodifyObservableCollection<OperationInfoViewModel> SwaggerOperations { get; }
|
public NodifyObservableCollection<OperationInfoViewModel> SwaggerOperations { get; }
|
||||||
public NodifyObservableCollection<OperationInfoViewModel> AvailableModels { get; }
|
public NodifyObservableCollection<OperationInfoViewModel> AvailableModels { get; }
|
||||||
public NodifyObservableCollection<OperationInfoViewModel> AvailableVariables { get; }
|
public NodifyObservableCollection<OperationInfoViewModel> AvailableVariables { get; }
|
||||||
|
public NodifyObservableCollection<OperationInfoViewModel> AvailableFunctions { get; }
|
||||||
public INodifyCommand CreateOperationCommand { get; }
|
public INodifyCommand CreateOperationCommand { get; }
|
||||||
|
|
||||||
[Newtonsoft.Json.JsonIgnore]
|
[Newtonsoft.Json.JsonIgnore]
|
||||||
@@ -125,6 +126,7 @@ namespace Nodify.Calculator
|
|||||||
List<OperationInfoViewModel> operations = new List<OperationInfoViewModel>();
|
List<OperationInfoViewModel> operations = new List<OperationInfoViewModel>();
|
||||||
AvailableModels = new NodifyObservableCollection<OperationInfoViewModel>();
|
AvailableModels = new NodifyObservableCollection<OperationInfoViewModel>();
|
||||||
AvailableVariables = new NodifyObservableCollection<OperationInfoViewModel>();
|
AvailableVariables = new NodifyObservableCollection<OperationInfoViewModel>();
|
||||||
|
AvailableFunctions = new NodifyObservableCollection<OperationInfoViewModel>();
|
||||||
LoadVariablesFromDb();
|
LoadVariablesFromDb();
|
||||||
|
|
||||||
var customModelDir = "CustomModels";
|
var customModelDir = "CustomModels";
|
||||||
@@ -158,6 +160,7 @@ namespace Nodify.Calculator
|
|||||||
CreateOperationCommand = new DelegateCommand<OperationInfoViewModel>(CreateOperation);
|
CreateOperationCommand = new DelegateCommand<OperationInfoViewModel>(CreateOperation);
|
||||||
ImportSwaggerCommand = new DelegateCommand(ImportSwagger);
|
ImportSwaggerCommand = new DelegateCommand(ImportSwagger);
|
||||||
AddVariableCommand = new DelegateCommand(AddVariable);
|
AddVariableCommand = new DelegateCommand(AddVariable);
|
||||||
|
CreateFunctionCommand = new DelegateCommand(CreateFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -180,6 +183,37 @@ namespace Nodify.Calculator
|
|||||||
|
|
||||||
public INodifyCommand ImportSwaggerCommand { get; }
|
public INodifyCommand ImportSwaggerCommand { get; }
|
||||||
public INodifyCommand AddVariableCommand { get; }
|
public INodifyCommand AddVariableCommand { get; }
|
||||||
|
public INodifyCommand CreateFunctionCommand { get; }
|
||||||
|
|
||||||
|
private void CreateFunction()
|
||||||
|
{
|
||||||
|
var dialog = new CreateFunctionDialog();
|
||||||
|
dialog.Owner = System.Windows.Application.Current.MainWindow;
|
||||||
|
if (dialog.ShowDialog() != true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var funcName = dialog.FunctionName;
|
||||||
|
|
||||||
|
if (AvailableFunctions.Any(f => f.Title == funcName))
|
||||||
|
{
|
||||||
|
MessageBox.Show($"A function named '{funcName}' already exists.", "Duplicate Function", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var funcInfo = new OperationInfoViewModel
|
||||||
|
{
|
||||||
|
Title = funcName,
|
||||||
|
Type = OperationType.System,
|
||||||
|
sysOp = SystemOperations.FUNCTION,
|
||||||
|
IsFlowNode = true,
|
||||||
|
IsFunction = true,
|
||||||
|
FunctionInputs = new System.Collections.Generic.List<FunctionParameterInfo>(dialog.InputParameters),
|
||||||
|
FunctionOutputs = new System.Collections.Generic.List<FunctionParameterInfo>(dialog.OutputParameters)
|
||||||
|
};
|
||||||
|
|
||||||
|
AvailableFunctions.Add(funcInfo);
|
||||||
|
AvailableOperations.Add(funcInfo);
|
||||||
|
}
|
||||||
|
|
||||||
private void AddVariable()
|
private void AddVariable()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ namespace Nodify.Calculator
|
|||||||
GET_SET,
|
GET_SET,
|
||||||
PARSEJSON,
|
PARSEJSON,
|
||||||
SPLIT,
|
SPLIT,
|
||||||
AUTH
|
AUTH,
|
||||||
|
FUNCTION
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SystemOperationViewModel : OperationViewModel
|
public class SystemOperationViewModel : OperationViewModel
|
||||||
|
|||||||
Reference in New Issue
Block a user