From 678b01f2fef5e47f593e16e4cf5dbdf0cd4b8f70 Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Mon, 7 Jul 2025 20:40:24 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=8A=82=E7=82=B9=E5=9B=BE?=
=?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Library/Api/IDynamicContext.cs | 10 +-
Library/Api/IFlowControl.cs | 2 +-
Library/Extension/FlowModelExtension.cs | 2 +-
Library/FlowNode/DynamicContext.cs | 13 +-
.../FlowNode/LightweightFlowEnvironment.cs | 282 +++++---
Library/FlowNode/MethodDetails.cs | 8 +
Library/FlowNode/MethodDetailsInfo.cs | 7 +-
Library/Serein.Library.csproj | 3 +
Library/SereinBaseFunction.cs | 16 +-
Library/Utils/BenchmarkHelpers.cs | 133 ++++
Library/Utils/SereinIoc.cs | 23 +-
NodeFlow/Env/FlowControl.cs | 45 +-
NodeFlow/Env/FlowEdit.cs | 12 +-
NodeFlow/Env/FlowEnvironment.cs | 7 +-
NodeFlow/FlowNodeExtension.cs | 42 +-
NodeFlow/Model/Node/NodeModelBaseFunc.cs | 2 +-
NodeFlow/Model/Node/SingleFlowCallNode.cs | 2 +-
.../ChangeNodeConnectionOperation.cs | 20 +-
NodeFlow/Model/Operation/OperationBase.cs | 1 -
.../Model/Operation/RemoveNodeOperation.cs | 41 +-
NodeFlow/Serein.NodeFlow.csproj | 2 +-
.../{Tool => Services}/FlowLibraryService.cs | 34 +-
NodeFlow/Services/FlowModelService.cs | 650 +++++++++++++++++-
NodeFlow/Services/FlowWorkManagement.cs | 6 +-
NodeFlow/Tool/FlowLibrary.cs | 25 +-
NodeFlow/Tool/NodeMethodDetailsHelper.cs | 17 +-
Workbench/App.xaml.cs | 2 +
Workbench/Serein.Workbench.csproj | 2 +-
Workbench/ServiceCollectionExtensions.cs | 1 -
.../Themes/NodeTreeItemViewControl.xaml.cs | 2 +-
Workbench/ViewModels/MainMenuBarViewModel.cs | 16 +
Workbench/Views/FlowCanvasView.xaml.cs | 3 +-
Workbench/Views/MainMenuBarView.xaml | 2 +-
33 files changed, 1219 insertions(+), 214 deletions(-)
create mode 100644 Library/Utils/BenchmarkHelpers.cs
rename NodeFlow/{Tool => Services}/FlowLibraryService.cs (91%)
diff --git a/Library/Api/IDynamicContext.cs b/Library/Api/IDynamicContext.cs
index ca3667f..4487eda 100644
--- a/Library/Api/IDynamicContext.cs
+++ b/Library/Api/IDynamicContext.cs
@@ -87,7 +87,15 @@ namespace Serein.Library.Api
///
///
///
- void AddOrUpdate(string nodeModel, FlowResult flowData);
+ void AddOrUpdateFlowData(string nodeModel, FlowResult flowData);
+
+ ///
+ /// 添加或更新当前节点的数据
+ ///
+ ///
+ ///
+ void AddOrUpdate(string nodeModel, object data);
+
///
/// 重置流程状态(用于对象池回收)
diff --git a/Library/Api/IFlowControl.cs b/Library/Api/IFlowControl.cs
index 53e0823..cfdbf86 100644
--- a/Library/Api/IFlowControl.cs
+++ b/Library/Api/IFlowControl.cs
@@ -29,7 +29,7 @@ namespace Serein.Library.Api
///
///
///
- Task StartFlowFromSelectNodeAsync(string startNodeGuid);
+ Task StartFlowAsync(string startNodeGuid);
///
/// 结束运行
diff --git a/Library/Extension/FlowModelExtension.cs b/Library/Extension/FlowModelExtension.cs
index 70d066a..a3890c9 100644
--- a/Library/Extension/FlowModelExtension.cs
+++ b/Library/Extension/FlowModelExtension.cs
@@ -236,7 +236,7 @@ namespace Serein.Library
#endregion
#region 执行完成时更新栈
- context.AddOrUpdate(currentNode.Guid, flowResult); // 上下文中更新数据
+ context.AddOrUpdateFlowData(currentNode.Guid, flowResult); // 上下文中更新数据
// 首先将指定类别后继分支的所有节点逆序推入栈中
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
diff --git a/Library/FlowNode/DynamicContext.cs b/Library/FlowNode/DynamicContext.cs
index a274482..c32262c 100644
--- a/Library/FlowNode/DynamicContext.cs
+++ b/Library/FlowNode/DynamicContext.cs
@@ -217,12 +217,23 @@ namespace Serein.Library
///
/// 节点
/// 新的数据
- public void AddOrUpdate(string nodeModel, FlowResult flowData)
+ public void AddOrUpdateFlowData(string nodeModel, FlowResult flowData)
{
// this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData);
dictNodeFlowData.AddOrUpdate(nodeModel, _ => flowData, (o,n ) => flowData);
}
+ ///
+ /// 添加或更新当前节点的数据
+ ///
+ ///
+ ///
+ public void AddOrUpdate(string nodeModel, object data)
+ {
+ var flowData = new FlowResult(nodeModel, this, data);
+ dictNodeFlowData.AddOrUpdate(nodeModel, _ => flowData, (o, n) => flowData);
+ }
+
///
/// 上一节点数据透传到下一节点
///
diff --git a/Library/FlowNode/LightweightFlowEnvironment.cs b/Library/FlowNode/LightweightFlowEnvironment.cs
index 46c650c..95557e5 100644
--- a/Library/FlowNode/LightweightFlowEnvironment.cs
+++ b/Library/FlowNode/LightweightFlowEnvironment.cs
@@ -1,101 +1,89 @@
-using Microsoft.VisualBasic;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.Extensions.ObjectPool;
+using Microsoft.VisualBasic;
using Serein.Library.Api;
using Serein.Library.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-namespace Serein.Library.FlowNode
+namespace Serein.Library
{
-
-
- /*public class FlowControl
+/*
+ public class CallNodeLookup : IFlowCallTree
{
- private DynamicFlowTemplate dynamicFlowTemplate;
- private LightweightEnvironment environment;
- public FlowControl(DynamicFlowTemplate dynamicFlowTemplate,
- LightweightEnvironment environment)
+ private static readonly string[] _keys = new[]
{
- var callTree = new CallTree()
- callTree.SetMainNode("node1_guid");
+ "Start", // 0
+ "Stop", // 1
+ "Reset", // 2
+ "Pause", // 3
+ "Resume", // 4
+ "Check", // 5
+ "Init", // 6
+ "Load", // 7
+ "Save", // 8
+ "Clear" // 9
+ };
- callTree.AddCallNode("node1_guid", (context) => DynamicFlowTemplate.Method(context, ...));
- callTree.AddCallNode("node2_guid", (context) => DynamicFlowTemplate.Method(context, ...));
- callTree.AddCallNode("node3_guid", (context) => DynamicFlowTemplate.Method(context, ...));
- callTree.AddCallNode("node4_guid", (context) => DynamicFlowTemplate.Method(context, ...));
+ private static readonly CallNode[] _values = new CallNode[10];
- callTree["node1_guid"].AddFlowUpstream("node2_guid");
- callTree["node1_guid"].AddFlowSucceed("node2_guid");
- callTree["node2_guid"].AddFlowSucceed("node3_guid");
- callTree["node2_guid"].AddFlowError("node4_guid");
-
- LightweightEnvironment.LoadProject(callTree);
- }
- }*/
-
-
- /*public class DynamicFlowTemplate
- {
- private readonly class1_type class1_instance;
-
- public DynamicFlowTemplate(class1_type class_instance){
- this.class1_instance = class1_instance;
+ static CallNodeLookup()
+ {
+ *//*_values[0] = new CallNode("Start");
+ _values[1] = new CallNode("Stop");
+ _values[2] = new CallNode("Reset");
+ _values[3] = new CallNode("Pause");
+ _values[4] = new CallNode("Resume");
+ _values[5] = new CallNode("Check");
+ _values[6] = new CallNode("Init");
+ _values[7] = new CallNode("Load");
+ _values[8] = new CallNode("Save");
+ _values[9] = new CallNode("Clear");*//*
}
- [Description("node1_guid")]
- [Description("assembly_name.class_name.method_name")]
- private [methodReturnType / void] NodeMethod_[method_name](IDynamicContext context, type1 param1, type2 param2)
+ // 最小冲突哈希函数(简单示例,固定键集有效)
+ private static int PerfectHash(string key)
{
- class1_instance.method(params);
- context.SetData("node_guid", null);
+ return key switch
+ {
+ "Start" => 0,
+ "Stop" => 1,
+ "Reset" => 2,
+ "Pause" => 3,
+ "Resume" => 4,
+ "Check" => 5,
+ "Init" => 6,
+ "Load" => 7,
+ "Save" => 8,
+ "Clear" => 9,
+ _ => -1
+ };
}
- [Description("node2_guid")]
- [Description("assembly_name.class_name.method_name")]
- private [methodReturnType] NodeMethod_[method_name](IDynamicContext context, type1 param1)
+ public CallNode Get(string key)
{
- param1 = context.GetData("node1_guid") as type1;
- var result = class1_instance.method(params, param1);
- context.SetData("node2_guid", result);
- }
-
-
- [Description("node3_guid")]
- public [methodReturnType] FlowCall_[method_name](IDynamicContext context, type1 param1, type2 param2)
- {
- param1 = context.GetData("node3_guid") as type1;
- var result = class1_instance.method(params, param1);
- context.SetData("node3_guid", result);
+ int index = PerfectHash(key);
+ if (index >= 0 && _keys[index] == key)
+ return _values[index];
+ return null;
}
}
- */
+
+*/
-
-
-
-
-
-
-
-
-
- public class CallTree
+ public class FlowCallTree : IFlowCallTree
{
- public CallTree(string canvasGuid, string startNodeGuid)
- {
- _canvas_guid = canvasGuid;
- _start_node_guid = startNodeGuid;
- }
+ private readonly SortedDictionary _callNodes = new SortedDictionary();
+ //private readonly Dictionary _callNodes = new Dictionary();
- private readonly ConcurrentDictionary _callNodes = new ConcurrentDictionary();
- private readonly string _start_node_guid;
- private readonly string _canvas_guid ;
public CallNode this[string index]
{
get
@@ -106,45 +94,57 @@ namespace Serein.Library.FlowNode
set
{
// 设置指定索引的值
- _callNodes.AddOrUpdate(index, value, (key, oldValue) => value);
+ _callNodes.Add(index, value);
}
}
public void AddCallNode(string nodeGuid, Action action)
{
- var node = new CallNode(this, nodeGuid, action);
+ var node = new CallNode(nodeGuid, action);
_callNodes[nodeGuid] = node;
}
public void AddCallNode(string nodeGuid, Func func)
{
- var node = new CallNode(this, nodeGuid, func);
+ var node = new CallNode(nodeGuid, func);
_callNodes[nodeGuid] = node;
}
+ public CallNode Get(string key)
+ {
+ return _callNodes.TryGetValue(key, out CallNode callNode) ? callNode : null;
+ }
}
+
+
+
public class CallNode
{
- private readonly Func func;
- private readonly CallTree callTree;
- private readonly Action action;
+ private Func taskFunc;
+ private Action action;
- public CallNode(CallTree callTree, string nodeGuid, Action action)
- {
- this.callTree = callTree;
- Guid = nodeGuid;
- this.action = action;
- }
-
- public CallNode(CallTree callTree, string nodeGuid, Func func)
+ public CallNode(string nodeGuid)
{
Guid = nodeGuid;
- this.func = func;
Init();
}
+ public CallNode(string nodeGuid, Action action)
+ {
+ Guid = nodeGuid;
+ this.action = action;
+ Init();
+ }
+
+ public CallNode(string nodeGuid, Func func)
+ {
+ Guid = nodeGuid;
+ this.taskFunc = func;
+ Init();
+ }
+
private void Init()
{
@@ -157,6 +157,16 @@ namespace Serein.Library.FlowNode
}
}
+
+ public void SetAction(Action action)
+ {
+ this.action = action;
+ }
+ public void SetAction(Func taskFunc)
+ {
+ this.taskFunc = taskFunc;
+ }
+
///
/// 对应的节点
@@ -173,28 +183,30 @@ namespace Serein.Library.FlowNode
public Dictionary> SuccessorNodes { get; private set; }
- public CallNode AddChildNodeUpstream(string nodeGuid)
+ public CallNode AddChildNodeUpstream(CallNode callNode)
{
var connectionInvokeType = ConnectionInvokeType.Upstream;
- PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]);
+ SuccessorNodes[connectionInvokeType].Add(callNode);
return this;
}
- public CallNode AddChildNodeSucceed(string nodeGuid)
+ public CallNode AddChildNodeSucceed(CallNode callNode)
{
- var connectionInvokeType = ConnectionInvokeType.IsSucceed;
- PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]);
+ var connectionInvokeType = ConnectionInvokeType.IsSucceed;
+ SuccessorNodes[connectionInvokeType].Add(callNode);
+
return this;
}
- public CallNode AddChildNodeFail(string nodeGuid)
+ public CallNode AddChildNodeFail(CallNode callNode)
{
- var connectionInvokeType = ConnectionInvokeType.IsFail;
- PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]);
+ var connectionInvokeType = ConnectionInvokeType.IsFail;
+ SuccessorNodes[connectionInvokeType].Add(callNode);
+
return this;
}
- public CallNode AddChildNodeError(string nodeGuid)
+ public CallNode AddChildNodeError(CallNode callNode)
{
var connectionInvokeType = ConnectionInvokeType.IsError;
- PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]);
+ SuccessorNodes[connectionInvokeType].Add(callNode);
return this;
}
@@ -216,9 +228,9 @@ namespace Serein.Library.FlowNode
{
action(context);
}
- else if (func is not null)
+ else if (taskFunc is not null)
{
- await func(context);
+ await taskFunc(context);
}
else
{
@@ -226,6 +238,10 @@ namespace Serein.Library.FlowNode
}
}
+ private static readonly DefaultObjectPool> _stackPool = new DefaultObjectPool>(new DefaultPooledObjectPolicy>());
+
+
+
///
/// 开始执行
///
@@ -234,10 +250,8 @@ namespace Serein.Library.FlowNode
///
public async Task StartFlowAsync(IDynamicContext context, CancellationToken token)
{
- Stack stack = new Stack();
- HashSet processedNodes = new HashSet(); // 用于记录已处理上游节点的节点
+ var stack = _stackPool.Get();
stack.Push(this);
-
while (true)
{
if (token.IsCancellationRequested)
@@ -253,7 +267,6 @@ namespace Serein.Library.FlowNode
try
{
await currentNode.InvokeAsync(context, token);
-
if (context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置
{
context.NextOrientation = ConnectionInvokeType.IsSucceed;
@@ -269,7 +282,6 @@ namespace Serein.Library.FlowNode
#endregion
#region 执行完成时更新栈
- context.AddOrUpdate(currentNode.Guid, flowResult); // 上下文中更新数据
// 首先将指定类别后继分支的所有节点逆序推入栈中
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
@@ -293,35 +305,51 @@ namespace Serein.Library.FlowNode
if (stack.Count == 0)
{
+ _stackPool.Return(stack);
+ flowResult = context.GetFlowData(currentNode.Guid);
return flowResult; // 说明流程到了终点
}
if (context.RunState == RunState.Completion)
{
+
+ _stackPool.Return(stack);
context.Env.WriteLine(InfoType.INFO, $"流程执行到节点[{currentNode.Guid}]时提前结束,将返回当前执行结果。");
+ flowResult = context.GetFlowData(currentNode.Guid); _stackPool.Return(stack);
return flowResult; // 流程执行完成,返回结果
}
if (token.IsCancellationRequested)
{
+ _stackPool.Return(stack);
throw new Exception($"流程执行到节点[{currentNode.Guid}]时被取消,未能获取到流程结果。");
}
#endregion
}
+
}
}
+
+ public interface IFlowCallTree
+ {
+ CallNode Get(string key);
+ }
+
///
/// 轻量级流程控制器
///
public class LightweightFlowControl : IFlowControl
{
- private readonly Dictionary callTree = new Dictionary();
+ private readonly IFlowCallTree flowCallTree;
+ private readonly IFlowEnvironment flowEnvironment;
- public LightweightFlowControl()
+ public LightweightFlowControl(IFlowCallTree flowCallTree, IFlowEnvironment flowEnvironment)
{
+ this.flowCallTree = flowCallTree;
+ this.flowEnvironment = flowEnvironment;
}
public Task
public class LightweightFlowEnvironment : IFlowEnvironment
{
+
+ public LightweightFlowEnvironment(IFlowEnvironmentEvent lightweightFlowEnvironmentEvent)
+ {
+ this.Event = lightweightFlowEnvironmentEvent;
+ }
+
+
+
public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial)
{
Console.WriteLine(message);
@@ -513,7 +581,7 @@ namespace Serein.Library.FlowNode
public IFlowControl FlowControl => throw new NotImplementedException();
- public IFlowEnvironmentEvent Event => throw new NotImplementedException();
+ public IFlowEnvironmentEvent Event { get; private set; }
public string EnvName => throw new NotImplementedException();
diff --git a/Library/FlowNode/MethodDetails.cs b/Library/FlowNode/MethodDetails.cs
index 1b380c2..7269b59 100644
--- a/Library/FlowNode/MethodDetails.cs
+++ b/Library/FlowNode/MethodDetails.cs
@@ -82,6 +82,12 @@ namespace Serein.Library
[PropertyInfo]
private int _paramsArgIndex = -1;
+ ///
+ /// 是否为异步方法(如果为异步方法,则返回值类型为Task或Task<T>)
+ ///
+ [PropertyInfo]
+ private bool _isAsync = false;
+
///
/// 出参类型
///
@@ -269,6 +275,7 @@ namespace Serein.Library
ParameterDetailsInfos = this.ParameterDetailss?.Select(p => p.ToInfo()).ToArray(),
ReturnTypeFullName = this.ReturnType?.FullName,
IsParamsArgIndex = this.ParamsArgIndex,
+ IsAsync = this.IsAsync,
};
}
@@ -291,6 +298,7 @@ namespace Serein.Library
MethodLockName = this.MethodLockName, // 拷贝
ParamsArgIndex = this.ParamsArgIndex, // 拷贝
ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(), // 拷贝属于节点方法的新入参描述
+ IsAsync = this.IsAsync, // 拷贝
};
return md;
diff --git a/Library/FlowNode/MethodDetailsInfo.cs b/Library/FlowNode/MethodDetailsInfo.cs
index e8856fb..783b69d 100644
--- a/Library/FlowNode/MethodDetailsInfo.cs
+++ b/Library/FlowNode/MethodDetailsInfo.cs
@@ -38,10 +38,15 @@ namespace Serein.Library
public ParameterDetailsInfo[] ParameterDetailsInfos { get; set; }
///
- /// 可选参数信息
+ /// 可选参数信息(-1表示不存在)
///
public int IsParamsArgIndex { get; set; }
+ ///
+ /// 是否为异步方法
+ ///
+ public bool IsAsync{ get; set; }
+
///
/// 出参类型
///
diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj
index c54d5e4..a17a143 100644
--- a/Library/Serein.Library.csproj
+++ b/Library/Serein.Library.csproj
@@ -14,6 +14,9 @@
latest
no
+
+ true
+
true
true
.\obj\g
diff --git a/Library/SereinBaseFunction.cs b/Library/SereinBaseFunction.cs
index 6be3ab2..24e026a 100644
--- a/Library/SereinBaseFunction.cs
+++ b/Library/SereinBaseFunction.cs
@@ -15,13 +15,13 @@ namespace Serein.Library
///
[DynamicFlow(Name ="[基础功能]")]
- public class SereinBaseFunction
+ public static class SereinBaseFunction
{
[NodeAction(NodeType.Action, "键值对组装")]
- private Dictionary SereinKvDataCollectionNode(string argName,
+ public static Dictionary SereinKvDataCollectionNode(string argName,
params object[] value)
{
@@ -36,13 +36,13 @@ namespace Serein.Library
}
[NodeAction(NodeType.Action, "数组组装")]
- private object[] SereinListDataCollectionNode(params object[] value)
+ public static object[] SereinListDataCollectionNode(params object[] value)
{
return value;
}
[NodeAction(NodeType.Action, "输出")]
- private object[] SereinConsoleNode(params object[] value)
+ public static object[] SereinConsoleNode(params object[] value)
{
foreach (var item in value)
{
@@ -52,7 +52,7 @@ namespace Serein.Library
}
[NodeAction(NodeType.Action, "逻辑分支")]
- private object SereinLogicalBranch([NodeParam(IsExplicit = false)]bool @bool,
+ public static object SereinLogicalBranch([NodeParam(IsExplicit = false)]bool @bool,
object t_value,
object f_value)
{
@@ -60,7 +60,7 @@ namespace Serein.Library
}
[NodeAction(NodeType.Action, "文本拼接")]
- private string SereinTextJoin(params object[] value)
+ public static string SereinTextJoin(params object[] value)
{
StringBuilder sb = new StringBuilder();
foreach (var item in value)
@@ -84,7 +84,7 @@ namespace Serein.Library
[NodeAction(NodeType.Action, "键值对动态构建对象")]
- private object SereinKvDataToObject(Dictionary dict,
+ public static object SereinKvDataToObject(Dictionary dict,
string classTypeName = "newClass_dynamic",
bool IsPrint = false)
{
@@ -105,7 +105,7 @@ namespace Serein.Library
[NodeAction(NodeType.Action, "设置/更新全局数据")]
- private object SereinAddOrUpdateFlowGlobalData(string name, object data)
+ public static object SereinAddOrUpdateFlowGlobalData(string name, object data)
{
SereinEnv.AddOrUpdateFlowGlobalData(name, data);
return data;
diff --git a/Library/Utils/BenchmarkHelpers.cs b/Library/Utils/BenchmarkHelpers.cs
new file mode 100644
index 0000000..99eb65e
--- /dev/null
+++ b/Library/Utils/BenchmarkHelpers.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Library.Utils
+{
+ ///
+ /// 代码计时工具类
+ ///
+ public static class BenchmarkHelpers
+ {
+ ///
+ /// 运行指定异步方法多次并输出耗时的最大、最小和平均值。
+ ///
+ /// 需要执行的异步方法
+ /// 执行次数,默认10000
+ public static void Benchmark(Action action, int count = 10000)
+ {
+ double max = double.MinValue;
+ double min = double.MaxValue;
+ double total = 0;
+
+ for (int i = 0; i < count; i++)
+ {
+ var sw = Stopwatch.StartNew();
+ action();
+ sw.Stop();
+
+ double ms = sw.Elapsed.TotalMilliseconds;
+ if (ms > max) max = ms;
+ if (ms < min) min = ms;
+ total += ms;
+ }
+
+ double avg = total / count;
+
+ Console.WriteLine($"运行 {count} 次:");
+ Console.WriteLine($"最大耗时:{max} 毫秒");
+ Console.WriteLine($"最小耗时:{min} 毫秒");
+ Console.WriteLine($"平均耗时:{avg} 毫秒");
+ }
+
+ ///
+ /// 运行指定异步方法多次并输出耗时的最大、最小和平均值。
+ ///
+ /// 需要执行的异步方法
+ /// 执行次数,默认10000
+ public static async Task BenchmarkAsync(Func func, int count = 10000)
+ {
+ double max = double.MinValue;
+ double min = double.MaxValue;
+ double total = 0;
+
+ for (int i = 0; i < count; i++)
+ {
+ var sw = Stopwatch.StartNew();
+ await func();
+ sw.Stop();
+
+ double ms = sw.Elapsed.TotalMilliseconds;
+ if (ms > max) max = ms;
+ if (ms < min) min = ms;
+ total += ms;
+ }
+
+ double avg = total / count;
+ Console.WriteLine($"运行 {count} 次:");
+ Console.WriteLine($"总耗时 :{total} 毫秒:");
+ Console.WriteLine($"最大耗时:{max} 毫秒");
+ Console.WriteLine($"最小耗时:{min} 毫秒");
+ Console.WriteLine($"平均耗时:{avg} 毫秒");
+ }
+
+ ///
+ /// 运行指定异步方法多次并输出耗时的最大、最小和平均值。
+ ///
+ /// 需要执行的异步方法
+ /// 执行次数,默认10000
+ public static async Task BenchmarkAsync(Func> func, int count = 10000)
+ {
+ double max = double.MinValue;
+ double min = double.MaxValue;
+ double total = 0;
+ TReult result = default;
+ for (int i = 0; i < count; i++)
+ {
+ var sw = Stopwatch.StartNew();
+ result = await func();
+ sw.Stop();
+
+ double ms = sw.Elapsed.TotalMilliseconds;
+ if (ms > max) max = ms;
+ if (ms < min) min = ms;
+ total += ms;
+ //Console.WriteLine($"第{count}次: 耗时 {ms} ms");
+ }
+
+ double avg = total / count;
+ Console.WriteLine($"运行 {count} 次:");
+ Console.WriteLine($"总耗时 :{total} 毫秒:");
+ Console.WriteLine($"最大耗时:{max} 毫秒");
+ Console.WriteLine($"最小耗时:{min} 毫秒");
+ Console.WriteLine($"平均耗时:{avg} 毫秒");
+ return result;
+ }
+
+ ///
+ /// 运行指定异步方法多次并输出耗时的最大、最小和平均值。
+ ///
+ /// 需要执行的异步方法
+ public static async Task BenchmarkAsync(Func> func)
+ {
+ double max = double.MinValue;
+ double min = double.MaxValue;
+ double total = 0;
+ TReult result = default;
+ var sw = Stopwatch.StartNew();
+ result = await func();
+ sw.Stop();
+
+ double ms = sw.Elapsed.TotalMilliseconds;
+ if (ms > max) max = ms;
+ if (ms < min) min = ms;
+ total += ms;
+
+ Console.WriteLine($"运行1次耗时 :{total} 毫秒:");
+ return result;
+ }
+ }
+}
diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs
index a071b8f..4ace00b 100644
--- a/Library/Utils/SereinIoc.cs
+++ b/Library/Utils/SereinIoc.cs
@@ -31,6 +31,7 @@ namespace Serein.Library.Utils
///
private readonly ConcurrentDictionary> _registerCallback;
+
///
/// 未完成注入的实例集合。
/// 键:需要的类型名称
@@ -83,7 +84,6 @@ namespace Serein.Library.Utils
/// 向容器注册类型,并指定其实例成员
///
/// 需要注册的类型
- /// 获取实例的回调函数
///
public ISereinIOC Register()
{
@@ -117,6 +117,7 @@ namespace Serein.Library.Utils
public ISereinIOC Register(Func getInstance)
where TImplementation : TService
{
+
RegisterType(typeof(TService).FullName, typeof(TImplementation), () => getInstance.Invoke());
return this;
}
@@ -130,6 +131,7 @@ namespace Serein.Library.Utils
public ISereinIOC Register()
where TImplementation : TService
{
+
RegisterType(typeof(TService).FullName, typeof(TImplementation));
return this;
}
@@ -137,7 +139,7 @@ namespace Serein.Library.Utils
#endregion
- #region 示例的创建
+ #region 实例的创建
///
/// 用于临时实例的创建,不登记到IOC容器中,依赖项注入失败时也不记录。
///
@@ -335,7 +337,8 @@ namespace Serein.Library.Utils
{
if (!dependencyMap[IOC_MAIN].Contains(type.FullName))
{
- dependencyMap[IOC_MAIN].Add(type.FullName);
+ //dependencyMap[IOC_MAIN].Add(type.FullName);
+ dependencyMap[IOC_MAIN].Add(typeFullName);
}
continue;
}
@@ -459,6 +462,7 @@ namespace Serein.Library.Utils
///
private object CreateInstance(string typeName)
{
+
if (!_typeMappings.TryGetValue(typeName, out var type)) // 获取类型
{
return null;
@@ -535,8 +539,8 @@ namespace Serein.Library.Utils
///
public ISereinIOC Build()
{
- var dependencyTree = BuildDependencyTree(); // 生成类型依赖关系
- var creationOrder = GetCreationOrder(dependencyTree); // 生成创建顺序
+ Dictionary> dependencyTree = BuildDependencyTree(); // 生成类型依赖关系
+ List creationOrder = GetCreationOrder(dependencyTree); // 生成创建顺序
// 输出创建顺序
Debug.WriteLine("创建顺序: " + string.Join($"{Environment.NewLine}↓ {Environment.NewLine}", creationOrder));
@@ -544,7 +548,10 @@ namespace Serein.Library.Utils
// 创建对象
foreach (var typeName in creationOrder)
{
-
+ if (typeName.Equals("Serein.Library.LightweightFlowEnvironment"))
+ {
+
+ }
if (_dependencies.ContainsKey(typeName))
{
continue;
@@ -554,13 +561,15 @@ namespace Serein.Library.Utils
continue;
}
var value = CreateInstance(typeName);
- if(value is null)
+
+ if (value is null)
{
SereinEnv.WriteLine(InfoType.ERROR, $"IOC容器无法创建对象:{typeName}");
continue;
}
_dependencies[typeName] = value;
OnIOCMembersChanged?.Invoke(new IOCMembersChangedEventArgs(typeName, value));
+
}
_typeMappings.Clear();
return this;
diff --git a/NodeFlow/Env/FlowControl.cs b/NodeFlow/Env/FlowControl.cs
index e192815..9b37153 100644
--- a/NodeFlow/Env/FlowControl.cs
+++ b/NodeFlow/Env/FlowControl.cs
@@ -3,7 +3,6 @@ using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Services;
-using Serein.NodeFlow.Tool;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -126,7 +125,6 @@ namespace Serein.NodeFlow.Env
};
-
flowWorkManagement = new FlowWorkManagement(flowTaskOptions);
var cts = new CancellationTokenSource();
try
@@ -152,7 +150,7 @@ namespace Serein.NodeFlow.Env
///
///
///
- public async Task StartFlowFromSelectNodeAsync(string startNodeGuid)
+ public async Task StartFlowAsync(string startNodeGuid)
{
var flowTaskOptions = new FlowWorkOptions
@@ -162,19 +160,34 @@ namespace Serein.NodeFlow.Env
};
var flowTaskManagement = new FlowWorkManagement(flowTaskOptions);
- if (true || flowEnvironment.FlowState == RunState.Running || FlipFlopState == RunState.Running)
+ if (!flowModelService.TryGetNodeModel(startNodeGuid, out var nodeModel) || nodeModel is SingleFlipflopNode)
{
+ throw new Exception();
+ }
- if (!flowModelService.TryGetNodeModel(startNodeGuid, out var nodeModel) || nodeModel is SingleFlipflopNode)
- {
- return false;
- }
- await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
- return true;
+#if DEBUG
+
+ FlowResult flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
+ {
+ var flowResult = await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
+ return flowResult;
+ });
+#else
+ FlowResult flowResult = await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
+#endif
+
+
+ if (flowResult.Value is TResult result)
+ {
+ return result;
+ }
+ else if (flowResult is FlowResult && flowResult is TResult result2)
+ {
+ return result2;
}
else
{
- return false;
+ throw new ArgumentNullException($"类型转换失败,流程返回数据与泛型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}]。");
}
}
@@ -331,7 +344,17 @@ namespace Serein.NodeFlow.Env
}
}
CancellationTokenSource cts = new CancellationTokenSource();
+
+#if DEBUG
+
+ FlowResult flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
+ {
+ var flowResult = await flowCallNode.StartFlowAsync(context, cts.Token);
+ return flowResult;
+ });
+#else
var flowResult = await flowCallNode.StartFlowAsync(context, cts.Token);
+#endif
cts?.Cancel();
cts?.Dispose();
if (flowResult.Value is TResult result)
diff --git a/NodeFlow/Env/FlowEdit.cs b/NodeFlow/Env/FlowEdit.cs
index 4ed8eb3..a809a04 100644
--- a/NodeFlow/Env/FlowEdit.cs
+++ b/NodeFlow/Env/FlowEdit.cs
@@ -5,7 +5,6 @@ using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Operation;
using Serein.NodeFlow.Services;
-using Serein.NodeFlow.Tool;
using System.Diagnostics;
using System.Threading.Tasks;
using static Serein.Library.Api.IFlowEnvironment;
@@ -261,7 +260,7 @@ namespace Serein.NodeFlow.Env
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Create,
JunctionOfConnectionType = JunctionOfConnectionType.Arg,
};
- flowOperationService.Execute(operation);
+ flowOperationService.Execute(operation);
}
public void RemoveInvokeConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
@@ -335,6 +334,15 @@ namespace Serein.NodeFlow.Env
public void SetStartNode(string canvasGuid, string nodeGuid)
{
+
+ IOperation operation = new SetStartNodeOperation
+ {
+ CanvasGuid = canvasGuid,
+ NewNodeGuid = nodeGuid,
+ };
+ _ = flowOperationService.Execute(operation);
+
+ return;
if (!TryGetCanvasModel(canvasGuid, out var canvasModel) || !TryGetNodeModel(nodeGuid, out var newStartNodeModel))
{
return;
diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs
index 096ea89..7cfdc6c 100644
--- a/NodeFlow/Env/FlowEnvironment.cs
+++ b/NodeFlow/Env/FlowEnvironment.cs
@@ -3,7 +3,6 @@ using Serein.Library.Api;
using Serein.Library.FlowNode;
using Serein.Library.Utils;
using Serein.NodeFlow.Services;
-using Serein.NodeFlow.Tool;
using System.Reflection;
namespace Serein.NodeFlow.Env
@@ -58,9 +57,9 @@ namespace Serein.NodeFlow.Env
.Register()
.Register()
.Register()
+ .Register()
.Register()
.Register()
- .Register()
.Build();
// 默认使用本地环境
currentFlowEnvironment = ioc.Get();
@@ -283,9 +282,9 @@ namespace Serein.NodeFlow.Env
}
///
- public async Task StartFlowFromSelectNodeAsync(string startNodeGuid)
+ public async Task StartFlowAsync(string startNodeGuid)
{
- return await currentFlowEnvironment.FlowControl.StartFlowFromSelectNodeAsync(startNodeGuid);
+ return await currentFlowEnvironment.FlowControl.StartFlowAsync(startNodeGuid);
}
///
diff --git a/NodeFlow/FlowNodeExtension.cs b/NodeFlow/FlowNodeExtension.cs
index 4a26815..ea2c446 100644
--- a/NodeFlow/FlowNodeExtension.cs
+++ b/NodeFlow/FlowNodeExtension.cs
@@ -29,6 +29,24 @@ namespace Serein.NodeFlow
return false;
}
+ ///
+ /// 是否为根节点
+ ///
+ ///
+ ///
+ public static bool IsRoot(this IFlowNode node)
+ {
+ var cts = NodeStaticConfig.ConnectionTypes;
+ foreach (var ct in cts)
+ {
+ if (node.PreviousNodes[ct].Count > 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
///
/// 创建节点
@@ -89,6 +107,8 @@ namespace Serein.NodeFlow
};
}
+
+
///
/// 触发器运行后状态转为对应的后继分支类别
///
@@ -107,6 +127,8 @@ namespace Serein.NodeFlow
};
}
+
+
///
/// 判断 触发器节点 是否存在上游分支
///
@@ -136,19 +158,27 @@ namespace Serein.NodeFlow
/// 缩进次数(4个空格)
/// 要添加的代码
/// 字符串构建器本身
- public static StringBuilder AddCode(this StringBuilder sb,
+ public static StringBuilder AppendCode(this StringBuilder sb,
int retractCount = 0,
- string code = null)
+ string code = null,
+ bool isWrapping = true)
{
if (!string.IsNullOrWhiteSpace(code))
{
- var retract = new string(' ', retractCount * 4);
- sb.AppendLine(retract + code);
+ string retract = new string(' ', retractCount * 4);
+ sb.Append(retract);
+ if (isWrapping)
+ {
+ sb.AppendLine(code);
+ }
+ else
+ {
+ sb.Append(code);
+ }
}
return sb;
}
-
-
+
diff --git a/NodeFlow/Model/Node/NodeModelBaseFunc.cs b/NodeFlow/Model/Node/NodeModelBaseFunc.cs
index 3cbdb2e..b38d5be 100644
--- a/NodeFlow/Model/Node/NodeModelBaseFunc.cs
+++ b/NodeFlow/Model/Node/NodeModelBaseFunc.cs
@@ -112,6 +112,7 @@ namespace Serein.NodeFlow.Model
/// 节点传回数据对象
public virtual async Task ExecutingAsync(IDynamicContext context, CancellationToken token)
{
+
// 执行触发检查是否需要中断
if (DebugSetting.IsInterrupt)
{
@@ -131,7 +132,6 @@ namespace Serein.NodeFlow.Model
throw new Exception($"节点{this.Guid}不存在对应委托");
}
-
var instance = Env.IOC.Get(md.ActingInstanceType);
if (instance is null)
{
diff --git a/NodeFlow/Model/Node/SingleFlowCallNode.cs b/NodeFlow/Model/Node/SingleFlowCallNode.cs
index f50f17c..11a01f8 100644
--- a/NodeFlow/Model/Node/SingleFlowCallNode.cs
+++ b/NodeFlow/Model/Node/SingleFlowCallNode.cs
@@ -266,7 +266,7 @@ namespace Serein.NodeFlow.Model
// 此处代码与SereinFlow.Library.FlowNode.ParameterDetails
// ToMethodArgData()方法中判断流程接口节点分支逻辑耦合
// 不要轻易修改
- context.AddOrUpdate(targetNode.Guid, flowData);
+ context.AddOrUpdateFlowData(targetNode.Guid, flowData);
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
{
if (this.SuccessorNodes[ctType] == null) continue;
diff --git a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs
index 434bf0e..cf7a0e8 100644
--- a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs
+++ b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs
@@ -298,25 +298,37 @@ namespace Serein.NodeFlow.Model.Operation
///
///
private async Task CreateArgConnection()
- {
+ {/*
IFlowNode fromNodeControl = ToNode;
- IFlowNode toNodeControl = ToNode;
+ IFlowNode toNodeControl = ToNode;*/
ConnectionArgSourceType type = ConnectionArgSourceType;
int index = ArgIndex;
-
+ /*FromNode.NeedResultNodes[type].Remove(ToNode); // 从起始节点的参数来源中移除目标节点
+ if (FromNode.Guid == ToNode.Guid) // 不能连接到自己
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, $"起始节点与目标节点不能是同一个节点" +
+ $"{Environment.NewLine}起始节点:{FromNode.Guid}" +
+ $"{Environment.NewLine}目标节点:{ToNode.Guid}");
+ return false;
+ }*/
var toNodeArgSourceGuid = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid; // 目标节点对应参数可能已经有其它连接
var toNodeArgSourceType = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType;
if (FromNode.Guid == toNodeArgSourceGuid
&& toNodeArgSourceType == ConnectionArgSourceType)
{
- SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系" +
+ if (FromNode.NeedResultNodes[type].Contains(ToNode))
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系" +
$"起始节点:{FromNode.Guid}" +
$"目标节点:{ToNode.Guid}" +
$"参数索引:{ArgIndex}" +
$"参数类型:{ConnectionArgSourceType}");
+ return false;
+ }
+ FromNode.NeedResultNodes[type].Add(ToNode);
await TriggerEvent(() =>
{
flowEnvironmentEvent.OnNodeConnectChanged(
diff --git a/NodeFlow/Model/Operation/OperationBase.cs b/NodeFlow/Model/Operation/OperationBase.cs
index 1e4c501..d1ea2fe 100644
--- a/NodeFlow/Model/Operation/OperationBase.cs
+++ b/NodeFlow/Model/Operation/OperationBase.cs
@@ -2,7 +2,6 @@
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Services;
-using Serein.NodeFlow.Tool;
using System;
using System.Collections.Generic;
using System.Diagnostics;
diff --git a/NodeFlow/Model/Operation/RemoveNodeOperation.cs b/NodeFlow/Model/Operation/RemoveNodeOperation.cs
index 2a363b9..b501b01 100644
--- a/NodeFlow/Model/Operation/RemoveNodeOperation.cs
+++ b/NodeFlow/Model/Operation/RemoveNodeOperation.cs
@@ -115,9 +115,8 @@ namespace Serein.NodeFlow.Model.Operation
#endregion
#region 移除参数获取关系
- // 需要找到有哪些节点的入参参数,被设置为了该节点,然后将其删除
- // 因为节点自身没有记录哪些节点选取了自己作为参数来源节点,所以需要遍历所有节点
+ // 遍历需要该节点返回值的节点,移除与其的连接
foreach (var item in flowNode.NeedResultNodes)
{
var connectionType = item.Key; // 参数来源连接类型
@@ -128,14 +127,14 @@ namespace Serein.NodeFlow.Model.Operation
if (md is null) continue;
var pds = md.ParameterDetailss;
if (pds is null || pds.Length == 0) continue;
- foreach(var parameter in pds)
+ foreach (var parameter in pds)
{
if (!parameter.ArgDataSourceNodeGuid.Equals(flowNode.Guid)) continue;
// 找到了对应的入参控制点了
var e = new NodeConnectChangeEventArgs(
CanvasGuid, // 画布
- flowNode.Guid, // 被移除的节点Guid
- argNode.Guid, // 子节点Guid
+ flowNode.Guid, // 数据来源节点(被移除的节点Guid)
+ argNode.Guid, // 需要数据的节点
parameter.Index, // 作用在第几个参数上,用于指示移除第几个参数的连线
JunctionOfConnectionType.Arg, // 指示移除的是参数连接线
connectionType, // 对应的连接关系
@@ -148,6 +147,34 @@ namespace Serein.NodeFlow.Model.Operation
}
}
}
+
+ // 遍历该节点参数详情,获取来源节点,移除与其的连接
+
+ if (flowNode.MethodDetails?.ParameterDetailss != null)
+ {
+ var pds = flowNode.MethodDetails.ParameterDetailss;
+ foreach (var pd in pds)
+ {
+ if(flowModelService.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var argSourceNode))
+ {
+ // 找到了对应的入参控制点了
+ var e = new NodeConnectChangeEventArgs(
+ CanvasGuid, // 画布
+ argSourceNode.Guid, // 数据来源节点
+ flowNode.Guid, // 需要数据的节点(被移除的节点Guid)
+ pd.Index, // 作用在第几个参数上,用于指示移除第几个参数的连线
+ JunctionOfConnectionType.Arg, // 指示移除的是参数连接线
+ pd.ArgDataSourceType, // 对应的连接关系
+ NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
+ EventArgs.Add(e); // 缓存事件参数
+ await TriggerEvent(() =>
+ {
+ flowEnvironmentEvent.OnNodeConnectChanged(e);
+ });
+ }
+ }
+ }
+
#endregion
flowModelService.RemoveNodeModel(flowNode); // 从记录中移除
@@ -163,7 +190,9 @@ namespace Serein.NodeFlow.Model.Operation
// 为了避免直接修改 ObservableCollection 集合导致异常产生,故而使用UI线程上下文操作运行
await TriggerEvent(() =>
{
- flowCanvasDetails?.Nodes.Remove(flowNode);
+ var lsit = flowCanvasDetails.Nodes.ToList();
+ lsit.Remove(flowNode);
+ flowCanvasDetails.Nodes = lsit;
});
}
diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj
index d2ae59b..881dca4 100644
--- a/NodeFlow/Serein.NodeFlow.csproj
+++ b/NodeFlow/Serein.NodeFlow.csproj
@@ -16,7 +16,7 @@
MIT
true
True
-
+
true
true
.\obj\g
diff --git a/NodeFlow/Tool/FlowLibraryService.cs b/NodeFlow/Services/FlowLibraryService.cs
similarity index 91%
rename from NodeFlow/Tool/FlowLibraryService.cs
rename to NodeFlow/Services/FlowLibraryService.cs
index 818aee6..c114c1a 100644
--- a/NodeFlow/Tool/FlowLibraryService.cs
+++ b/NodeFlow/Services/FlowLibraryService.cs
@@ -13,12 +13,12 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
-namespace Serein.NodeFlow.Tool
+namespace Serein.NodeFlow.Services
{
///
/// 管理加载在运行环境中的外部程序集
///
- internal class FlowLibraryService
+ public class FlowLibraryService
{
public FlowLibraryService(IFlowEnvironment flowEnvironment)
{
@@ -111,6 +111,32 @@ namespace Serein.NodeFlow.Tool
/// 方法名称
/// 返回的方法描述
/// 是否获取成功
+ public bool TryGetMethodInfo(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodInfo methodInfo)
+ {
+ if (string.IsNullOrEmpty(assemblyName) || string.IsNullOrEmpty(methodName))
+ {
+ methodInfo = null;
+ return false;
+ }
+ if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
+ && flowLibrary.MethodInfos.TryGetValue(methodName, out methodInfo))
+ {
+ return true;
+ }
+ else
+ {
+ methodInfo = null;
+ return false;
+ }
+ }
+
+ ///
+ /// 获取方法描述
+ ///
+ /// 程序集名称
+ /// 方法名称
+ /// 返回的方法描述
+ /// 是否获取成功
public bool TryGetMethodDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodDetails md)
{
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
@@ -238,7 +264,7 @@ namespace Serein.NodeFlow.Tool
///
/// 基础依赖
///
- public readonly static string SereinBaseLibrary = $"{nameof(Serein)}.{nameof(Serein.Library)}.dll";
+ public readonly static string SereinBaseLibrary = $"{nameof(Serein)}.{nameof(Library)}.dll";
//private (NodeLibraryInfo, List) LoadDllNodeInfo(Assembly assembly)
//{
@@ -331,7 +357,7 @@ namespace Serein.NodeFlow.Tool
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (!string.IsNullOrEmpty(assemblyPath))
{
- var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
+ var assembly = Default.LoadFromAssemblyPath(assemblyPath);
//var assembly = LoadFromAssemblyPath(assemblyPath);
return assembly;
}
diff --git a/NodeFlow/Services/FlowModelService.cs b/NodeFlow/Services/FlowModelService.cs
index 3cf039b..d1ca2d3 100644
--- a/NodeFlow/Services/FlowModelService.cs
+++ b/NodeFlow/Services/FlowModelService.cs
@@ -1,9 +1,14 @@
-using Serein.Library;
+using Newtonsoft.Json.Linq;
+using Serein.Library;
using Serein.Library.Api;
+using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using System;
using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
@@ -16,10 +21,12 @@ namespace Serein.NodeFlow.Services
public class FlowModelService
{
private readonly IFlowEnvironment environment;
+ private readonly FlowLibraryService flowLibraryService;
- public FlowModelService(IFlowEnvironment environment)
+ public FlowModelService(IFlowEnvironment environment, FlowLibraryService flowLibraryService )
{
this.environment = environment;
+ this.flowLibraryService = flowLibraryService;
}
///
@@ -114,53 +121,646 @@ namespace Serein.NodeFlow.Services
return flowCanvasDetails.Nodes.Count > 0;
}
+ #region 代码生成
- public void ToCsharpCoreFile()
+ public string ToCsharpCoreFile()
{
- // TODO: 实现将流程模型转换为C# Core文件的逻辑
- // 遍历每个画布
- int canvas_index = 0;
-
+ #region 生成类和方法
+#if false
HashSet assemblyFlowClasss = new HashSet(); // 用于创建依赖注入项
+ assemblyFlowClasss.Add(typeof(IFlowCallTree)); // 调用树
StringBuilder stringBuilder = new StringBuilder();
-
foreach (var canvas in FlowCanvass.Values)
{
+ if (canvas.StartNode is null)
+ {
+ continue;
+ }
int flowTemplateId = canvas_index++;
- string flowTemplateClassName = $"FlowTemplate{flowTemplateId}";
+ string flowTemplateClassName = $"FlowTemplate_{canvas.Guid.Replace("-", "")}";
HashSet flowClasss = new HashSet();
-
+ flowClasss.Add(typeof(IFlowCallTree)); // 调用树
// 收集程序集信息
foreach (var node in canvas.Nodes)
{
var instanceType = node.MethodDetails.ActingInstanceType;
- if(instanceType is not null)
- {
- flowClasss.Add(instanceType);
- assemblyFlowClasss.Add(instanceType);
+ if (instanceType is not null)
+ {
+ flowClasss.Add(instanceType);
+ assemblyFlowClasss.Add(instanceType);
}
}
- // 生成方法信息
+ stringBuilder.AppendCode(0, $"public class {flowTemplateClassName}");
+ stringBuilder.AppendCode(0, $"{{");
+
+ // 构造函数及依赖注入字段
+ GenerateCtor(stringBuilder, flowTemplateClassName, flowClasss);
+ //GenerateNodeIndexLookup(stringBuilder, flowTemplateClassName, );
+ GenerateInitMethod(stringBuilder);
+ GenerateCallTree(stringBuilder, canvas);
+
+ // 节点生成方法信息
foreach (var node in canvas.Nodes)
{
- var instanceType = node.MethodDetails.ActingInstanceType;
- var returnType = node.MethodDetails.ReturnType;
- var methodName = node.MethodDetails.MethodAnotherName;
-
+ GenerateMethod(stringBuilder, node);
+ }
+ stringBuilder.AppendCode(0, $"}}");
+
+ }
+#else
+ StringBuilder stringBuilder = new StringBuilder();
+ HashSet assemblyFlowClasss = new HashSet(); // 用于创建依赖注入项
+ assemblyFlowClasss.Add(typeof(IFlowCallTree)); // 调用树
+ var flowNodes = NodeModels.Values.ToArray();
+ // 收集程序集信息
+ foreach (var node in flowNodes)
+ {
+ var instanceType = node.MethodDetails.ActingInstanceType;
+ if (instanceType is not null)
+ {
+ assemblyFlowClasss.Add(instanceType);
+ }
+
+ }
+
+ string flowTemplateClassName = $"FlowTemplate"; // 类名
+ stringBuilder.AppendCode(0, $"public class {flowTemplateClassName} : global::{typeof(IFlowCallTree).FullName}");
+ stringBuilder.AppendCode(0, $"{{");
+ GenerateCtor(stringBuilder, flowTemplateClassName, assemblyFlowClasss); // 生成构造方法
+ GenerateInitMethod(stringBuilder); // 生成初始化方法
+ GenerateCallTree(stringBuilder, flowNodes); // 生成调用树
+ GenerateNodeIndexLookup(stringBuilder, flowTemplateClassName, flowNodes); // 初始化节点缓存
+ foreach (var node in flowNodes)
+ {
+ GenerateMethod(stringBuilder, node); // 生成每个节点的方法
+ }
+ stringBuilder.AppendCode(0, $"}}");
+#endif
+ #endregion
- var instanceTypeFullName = instanceType.FullName;
- var returnTypeFullName = returnType == typeof(void) ? "void" : returnType.FullName;
-
- string methodContext = $"private {returnTypeFullName} NodeMethod_{methodName}({nameof(IDynamicContext)} context)";
- SereinEnv.WriteLine(InfoType.INFO, methodContext);
+ return stringBuilder.ToString();
+ }
+
+ ///
+ /// 生成构造函数代码
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void GenerateCtor(StringBuilder sb, string className, HashSet assemblyFlowClasss)
+ {
+ if (assemblyFlowClasss.Count == 0)
+ {
+ return;
+ }
+ var instanceTypes = assemblyFlowClasss.Where(x => !IsStaticClass(x)).ToArray();
+
+ for (int index = 0; index < instanceTypes.Length; index++)
+ {
+ var type = instanceTypes[index];
+ var ctor_parms_name = GetCamelCase(type);
+ sb.AppendCode(2, $"private readonly global::{type.FullName} {ctor_parms_name};");
+ }
+
+ sb.AppendLine();
+
+ sb.AppendCode(2, $"public {className}(", false);
+ for (int index = 0; index < instanceTypes.Length; index++)
+ {
+ var type = instanceTypes[index];
+ var ctor_parms_name = GetCamelCase(type);
+ sb.Append($"global::{type.FullName} {ctor_parms_name}{(index < instanceTypes.Length - 1 ? "," : "")}");
+ }
+ sb.AppendCode(0, $")");
+ sb.AppendCode(2, $"{{");
+ for (int index = 0; index < instanceTypes.Length; index++)
+ {
+ var type = instanceTypes[index];
+ var ctor_parms_name = GetCamelCase(type);
+ sb.AppendCode(3, $"this.{ctor_parms_name} = {ctor_parms_name};");
+ }
+ sb.AppendLine();
+ sb.AppendCode(3, $"Init();"); // 初始化调用树
+ sb.AppendCode(2, $"}}");
+ sb.AppendLine();
+ }
+
+ ///
+ /// 生成方法调用逻辑
+ ///
+ ///
+ ///
+ ///
+ private void GenerateMethod(StringBuilder sb_main, IFlowNode flowNode)
+ {
+ string? dynamicContextTypeName = typeof(IDynamicContext).FullName;
+ string? flowContext = nameof(flowContext);
+
+ if (flowNode.ControlType == NodeControlType.Action)
+ {
+ #region 生成 Action 节点类型的调用过程
+ if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
+ flowNode.MethodDetails.MethodName,
+ out var methodInfo) || methodInfo is null)
+ {
+ return;
+ }
+
+ var isRootNode = flowNode.IsRoot();
+
+ var instanceType = flowNode.MethodDetails.ActingInstanceType;
+ var returnType = methodInfo.ReturnType;
+
+ var instanceName = GetCamelCase(instanceType);// $"instance_{instanceType.Name}";
+
+ var instanceTypeFullName = instanceType.FullName;
+ var returnTypeFullName = returnType == typeof(void) ? "void" : returnType.FullName;
+
+ #region 方法内部逻辑
+ StringBuilder sb_invoke_login = new StringBuilder();
+ if (flowNode.MethodDetails is null) return;
+ var param = methodInfo.GetParameters();
+ var md = flowNode.MethodDetails;
+ var pds = flowNode.MethodDetails.ParameterDetailss;
+ if (param is null) return;
+ if (pds is null) return;
+
+ bool isGetPreviousNode = false;
+ for (int index = 0; index < pds.Length; index++)
+ {
+ ParameterDetails? pd = pds[index];
+ ParameterInfo parameterInfo = param[index];
+ var paramtTypeFullName = parameterInfo.ParameterType.FullName;
+
+ if (pd.IsExplicitData)
+ {
+ // 只能是 数值、 文本、枚举, 才能作为显式参数
+ if (parameterInfo.ParameterType.IsValueType)
+ {
+ if (parameterInfo.ParameterType.IsEnum)
+ {
+ sb_invoke_login.AppendCode(3, $"global::{paramtTypeFullName} value{index} = global::{paramtTypeFullName}.{pd.DataValue}; // 获取当前节点的上一节点数据");
+ }
+ else
+ {
+ var value = pd.DataValue.ToConvert(parameterInfo.ParameterType);
+ sb_invoke_login.AppendCode(3, $"global::{paramtTypeFullName} value{index} = (global::{paramtTypeFullName}){value}; // 获取当前节点的上一节点数据");
+
+ }
+ }
+ else if (parameterInfo.ParameterType == typeof(string))
+ {
+ sb_invoke_login.AppendCode(3, $"global::{paramtTypeFullName} value{index} = \"{pd.DataValue}\"; // 获取当前节点的上一节点数据");
+ }
+ else
+ {
+ // 处理表达式
+ }
+
+ }
+ else
+ {
+ #region 非显式设置的参数以正常方式获取
+ if (pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
+ {
+ var previousNode = $"previousNode{index}";
+ var valueType = pd.IsParams ? $"global::{pd.DataType.FullName}" : $"global::{paramtTypeFullName}";
+ sb_invoke_login.AppendCode(3, $"global::System.String {previousNode} = {flowContext}.GetPreviousNode(\"{flowNode.Guid}\");"); // 获取运行时上一节点Guid
+ sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {previousNode} == null ? default : ({valueType}){flowContext}.{nameof(IDynamicContext.GetFlowData)}({previousNode}).Value; // 获取运行时上一节点的数据");
+ }
+ else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
+ {
+ if (this.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var otherNode))
+ {
+ var valueType = pd.IsParams ? $"global::{pd.DataType.FullName}" : $"global::{paramtTypeFullName}";
+ var otherNodeReturnType = otherNode.MethodDetails.ReturnType;
+ if (otherNodeReturnType == typeof(object))
+ {
+ sb_invoke_login.AppendCode(3, $"{valueType} value{index} = ({valueType}){flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据");
+ }
+ else if (pd.DataType.IsAssignableFrom(otherNodeReturnType))
+ {
+ sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据");
+ }
+ else
+ {
+ // 获取的数据无法转换为目标方法入参类型
+ throw new Exception("获取的数据无法转换为目标方法入参类型");
+ }
+ }
+ else
+ {
+ // 指定了Guid,但项目中不存在对应的节点,需要抛出异常
+ throw new Exception("指定了Guid,但项目中不存在对应的节点");
+ }
+ }
+ else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
+ {
+ if (this.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var otherNode)) // 获取指定节点
+ {
+ var otherNodeReturnType = otherNode.MethodDetails.ReturnType;
+ var valueType = pd.IsParams ? $"global::{pd.DataType.FullName}" : $"global::{otherNode.MethodDetails.ReturnType.FullName}";
+ if (otherNodeReturnType == typeof(object))
+ {
+ sb_invoke_login.AppendCode(3, $"{valueType} value{index} = ({valueType}){flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据");
+ }
+ else if (pd.DataType.IsAssignableFrom(otherNodeReturnType))
+ {
+ sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {GetNodeMethodName(otherNode)}({flowContext}); // 获取指定节点的数据");
+ }
+ else
+ {
+ // 获取的数据无法转换为目标方法入参类型
+ throw new Exception("获取的数据无法转换为目标方法入参类型");
+ }
+
+ }
+ else
+ {
+ // 指定了Guid,但项目中不存在对应的节点,需要抛出异常
+ throw new Exception("指定了Guid,但项目中不存在对应的节点");
+ }
+ }
+ #endregion
+
+ }
+ }
+
+
+
+ if (methodInfo.ReturnType == typeof(void))
+ {
+ if (methodInfo.IsStatic)
+ {
+ sb_invoke_login.AppendCode(3, $"global::{instanceType}.{methodInfo.Name}(", false);
+ for (int index = 0; index < pds.Length; index++)
+ {
+ sb_invoke_login.Append($"{(index == 0 ? "" : ",")}value{index}");
+ }
+ sb_invoke_login.AppendCode(0, $"); // 调用方法 {md.MethodAnotherName}");
+ }
+ else
+ {
+ sb_invoke_login.AppendCode(3, $"{instanceName}.{methodInfo.Name}(", false);
+ for (int index = 0; index < pds.Length; index++)
+ {
+ sb_invoke_login.Append($"{(index == 0 ? "" : ",")}value{index}");
+ }
+ sb_invoke_login.AppendCode(0, $"); // 调用方法 {md.MethodAnotherName}");
+ }
+ }
+ else
+ {
+ if (methodInfo.IsStatic)
+ {
+ sb_invoke_login.AppendCode(3, $"var result = global::{instanceType}.{methodInfo.Name}(", false);
+ for (int index = 0; index < pds.Length; index++)
+ {
+ sb_invoke_login.Append($"{(index == 0 ? "" : ",")}value{index}");
+ }
+ sb_invoke_login.AppendCode(0, $"); // 调用方法 {md.MethodAnotherName}");
+ }
+ else
+ {
+ sb_invoke_login.AppendCode(3, $"var result = {instanceName}.{methodInfo.Name}(", false);
+ for (int index = 0; index < pds.Length; index++)
+ {
+ sb_invoke_login.Append($"{(index == 0 ? "" : ",")}value{index}");
+ }
+ sb_invoke_login.AppendCode(0, $"); // 调用方法 {md.MethodAnotherName}");
+ }
+
+ sb_invoke_login.AppendCode(3, $"{flowContext}.{nameof(IDynamicContext.AddOrUpdate)}(\"{flowNode.Guid}\", result);", false);
+ //sb_invoke_login.AppendCode(3, $"return result;", false);
+ }
+ #endregion
+
+ // global::{returnTypeFullName}
+
+ sb_main.AppendCode(2, $"[Description(\"{instanceTypeFullName}.{methodInfo.Name}\")]");
+ sb_main.AppendCode(2, $"public void {GetNodeMethodName(flowNode)}(global::{dynamicContextTypeName} {flowContext})");
+ sb_main.AppendCode(2, $"{{");
+ sb_main.AppendCode(0, sb_invoke_login.ToString());
+ sb_main.AppendCode(2, $"}}"); // 方法结束
+ sb_main.AppendLine(); // 方法结束
+
+
+
+
+ #endregion
+ }
+ else if (flowNode.ControlType == NodeControlType.Flipflop)
+ {
+ }
+ else if (flowNode.ControlType == NodeControlType.Script)
+ {
+ }
+ else if (flowNode.ControlType == NodeControlType.UI)
+ {
+ }
+ else if (flowNode.ControlType == NodeControlType.ExpCondition)
+ {
+
+ }
+ else if (flowNode.ControlType == NodeControlType.ExpOp)
+ {
+ }
+
+ return;
+ throw new Exception("无法为该节点生成调用逻辑");
+ }
+
+ private void GenerateInitMethod(StringBuilder sb)
+ {
+ sb.AppendCode(2, $"public void Init()");
+ sb.AppendCode(2, $"{{");
+ sb.AppendCode(3, $"{nameof(GenerateCallTree)}(); // 初始化调用树"); // 初始化调用树
+ sb.AppendCode(2, $"}}");
+ }
+
+ private void GenerateCallTree(StringBuilder sb, IFlowNode[] flowNodes)
+ {
+ // Get("0fa6985b-4b63-4499-80b2-76401669292d").AddChildNodeSucceed(Get("acdbe7ea-eb27-4a3e-9cc9-c48f642ee4f5"));
+
+ sb.AppendCode(2, $"private void {nameof(GenerateCallTree)}()");
+ sb.AppendCode(2, $"{{");
+
+ foreach (var node in flowNodes)
+ {
+ var nodeMethod = GetNodeMethodName(node); // 节点对应的方法名称
+ sb.AppendCode(3, $"Get(\"{node.Guid}\").SetAction({nodeMethod});");
+ }
+
+ foreach (var node in flowNodes)
+ {
+ var nodeMethod = GetNodeMethodName(node); // 节点对应的方法名称
+ var cts = NodeStaticConfig.ConnectionTypes;
+ foreach (var ct in cts)
+ {
+ var childNodes = node.SuccessorNodes[ct];
+ var AddChildNodeMethodName = ct switch
+ {
+ ConnectionInvokeType.IsSucceed => nameof(CallNode.AddChildNodeSucceed),
+ ConnectionInvokeType.IsFail => nameof(CallNode.AddChildNodeFail),
+ ConnectionInvokeType.IsError => nameof(CallNode.AddChildNodeError),
+ ConnectionInvokeType.Upstream => nameof(CallNode.AddChildNodeUpstream),
+ _ => throw new ArgumentOutOfRangeException(nameof(ct), ct, null)
+ };
+ foreach (var childNode in childNodes)
+ {
+ sb.AppendCode(3, $"Get(\"{node.Guid}\").{AddChildNodeMethodName}(Get(\"{childNode.Guid}\"));");
+ }
+ }
+
+ }
+ sb.AppendCode(2, $"}}");
+ sb.AppendLine();
+
+ /*string? dynamicContextTypeName = typeof(IDynamicContext).FullName;
+ string? flowContext = nameof(flowContext);
+ var callTreeType = typeof(IFlowCallTree);
+ var callTreeName = GetCamelCase(callTreeType);
+ //var canvasGuid = flowCanvas.Guid;
+ //var startNodeGuid = flowCanvas.StartNode.Guid;
+
+ sb.AppendCode(2, $"private void {nameof(GenerateCallTree)}()");
+ sb.AppendCode(2, $"{{");
+ //sb.AppendCode(3, $"global::{callTreeType.FullName} {callTreeName} = new global::{callTreeType.FullName}()\";");
+
+ // 注册节点
+ *//* foreach (var node in flowCanvas.Nodes)
+ {
+ var nodeMethod = GetNodeMethodName(node);
+ var call = $"{flowContext} => {nodeMethod}({flowContext})";
+ sb.AppendCode(3, $"{callTreeName}.{nameof(FlowCallTree.AddCallNode)}(\"{node.Guid}\", {call});");
+ }*//*
+
+ sb.AppendLine();
+ foreach (var node in flowNodes)
+ {
+ var nodeMethod = GetNodeMethodName(node);
+ var cts = NodeStaticConfig.ConnectionTypes;
+ foreach (var ct in cts)
+ {
+ var childNodes = node.SuccessorNodes[ct];
+ var addType = ct switch
+ {
+ ConnectionInvokeType.IsSucceed => nameof(CallNode.AddChildNodeSucceed),
+ ConnectionInvokeType.IsFail => nameof(CallNode.AddChildNodeFail),
+ ConnectionInvokeType.IsError => nameof(CallNode.AddChildNodeError),
+ ConnectionInvokeType.Upstream => nameof(CallNode.AddChildNodeUpstream),
+ _ => throw new ArgumentOutOfRangeException(nameof(ct), ct, null)
+ };
+ foreach (var childNode in childNodes)
+ {
+ sb.AppendCode(3, $"{callTreeName}[\"{node.Guid}\"].{addType}(\"{childNode.Guid}\");");
+ }
+ }
+
+ }
+ sb.AppendCode(2, $"}}");
+ sb.AppendLine();*/
+ }
+
+ private void GenerateNodeIndexLookup(StringBuilder sb, string className, IFlowNode[] flowNodes)
+ {
+ // 初始化Id
+ nodeIdMap.Clear();
+ for (int index = 0; index < flowNodes.Length; index++)
+ {
+ var flowNode = flowNodes[index];
+ GetNodeId(flowNode);
+ }
+
+ var valueArrayName = "_values";
+
+ // 生成 _values
+ sb.AppendCode(2, $"private readonly static global::Serein.Library.CallNode[] {valueArrayName} = new global::Serein.Library.CallNode[{flowNodes.Length}];");
+
+ /*sb.AppendCode(2, $"private readonly static global::System.String[] _keys = new global::System.String[]");
+ sb.AppendCode(2, $"{{");
+ for (int index = 0; index < flowNodes.Length; index++)
+ {
+ var flowNode = flowNodes[index];
+ sb.AppendCode(3, $"\"{flowNode.Guid}\", // {index} : {flowNode.MethodDetails.MethodName}");
+ }
+ sb.AppendCode(2, $"}};");*/
+
+ // 生成静态构造函数
+ sb.AppendCode(2, $"static {className}()");
+ sb.AppendCode(2, $"{{");
+ for (int index = 0; index < flowNodes.Length; index++)
+ {
+ var flowNode = flowNodes[index];
+ sb.AppendCode(3, $"{valueArrayName}[{index}] = new global::Serein.Library.CallNode(\"{flowNode.Guid}\"); // {index} : {flowNode.MethodDetails.MethodName}");
+ }
+ sb.AppendCode(2, $"}}");
+
+ // 初始化 Get 函数
+ var nodeIndexName = "node_index";
+ sb.AppendCode(2, $" [MethodImpl(MethodImplOptions.AggressiveInlining)]"); // 内联优化
+ sb.AppendCode(2, $"public global::Serein.Library.CallNode {nameof(IFlowCallTree.Get)}( global::System.String key)");
+ sb.AppendCode(2, $"{{");
+ sb.AppendCode(3, $"global::System.Int32 {nodeIndexName};");
+ sb.AppendCode(3, $"switch (key)");
+ sb.AppendCode(3, $"{{");
+
+ for (int index = 0; index < flowNodes.Length; index++)
+ {
+ var flowNode = flowNodes[index];
+ sb.AppendCode(4, $"case \"{flowNode.Guid}\":");
+ sb.AppendCode(5, $"{nodeIndexName} = {index};");
+ sb.AppendCode(5, $"break;");
+ }
+ sb.AppendCode(4, $"default:");
+ sb.AppendCode(4, $"{nodeIndexName} = -1;");
+ sb.AppendCode(5, $"break;");
+ sb.AppendCode(3, $"}}");
+ sb.AppendCode(3, $"return {valueArrayName}[{nodeIndexName}];");
+ sb.AppendCode(2, $"}}");
+ }
+
+ ///
+ /// 生成方法名称
+ ///
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private string GetNodeMethodName(IFlowNode flowNode)
+ {
+ /*if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
+ flowNode.MethodDetails.MethodName,
+ out var methodInfo))
+ {
+ throw new Exception();
+ }*/
+ var guid = flowNode.Guid;
+ var tmp = guid.Replace("-", "");
+ var methodName = $"FlowMethod_{tmp}";
+ return methodName;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool IsStaticClass(Type type)
+ {
+ return type.IsAbstract && type.IsSealed && type.IsClass;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private string GetCamelCase(Type type)
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+
+ // 获取类型名称(不包括命名空间)
+ string typeName = type.Name;
+
+ if (string.IsNullOrEmpty(typeName))
+ {
+ return string.Empty;
+ }
+
+ // 处理泛型类型(去掉后面的`N)
+ int indexOfBacktick = typeName.IndexOf('`');
+ if (indexOfBacktick > 0)
+ {
+ typeName = typeName.Substring(0, indexOfBacktick);
+ }
+
+ // 如果是接口且以"I"开头,去掉第一个字母
+ if (type.IsInterface && typeName.Length > 1 && typeName[0] == 'I' && char.IsUpper(typeName[1]))
+ {
+ typeName = typeName.Substring(1);
+ }
+
+ // 转换为驼峰命名法:首字母小写,其余不变
+ if (typeName.Length > 0)
+ {
+ return char.ToLowerInvariant(typeName[0]) + typeName.Substring(1);
+ }
+
+ return typeName;
+ }
+
+ private Dictionary nodeIdMap = new Dictionary();
+ private int GetNodeId(IFlowNode flowNode)
+ {
+ if (nodeIdMap.ContainsKey(flowNode))
+ {
+ return nodeIdMap[flowNode];
+ }
+ else
+ {
+ lock (nodeIdMap)
+ {
+ int id = nodeIdMap.Count + 1; // 从1开始计数
+ nodeIdMap[flowNode] = id;
+ return id;
}
}
}
-
+ #endregion
}
}
+
+
+/* ///
+ /// 生成方法名称
+ ///
+ ///
+ ///
+ ///
+ private string GetNodeMethodName(IFlowNode flowNode)
+ {
+ return $"FlowMethod_{flowNode.Guid.Remove('-')}";
+
+ if (flowNode.ControlType == NodeControlType.Action)
+ {
+ if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
+ flowNode.MethodDetails.MethodName,
+ out var methodInfo))
+ {
+ throw new Exception();
+ }
+ return $"FlowMethod_{nameof(NodeControlType.Action)}_{methodInfo.Name}";
+ }
+ else if (flowNode.ControlType == NodeControlType.Flipflop)
+ {
+ if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
+ flowNode.MethodDetails.MethodName,
+ out var methodInfo))
+ {
+ throw new Exception();
+ }
+ return $"FlowMethod_{nameof(NodeControlType.Flipflop)}_{methodInfo.Name}";
+
+ }
+ else if (flowNode.ControlType == NodeControlType.Script)
+ {
+ return $"FlowMethod_{flowNode.Guid.Remove('-')}";
+ }
+ else if (flowNode.ControlType == NodeControlType.UI)
+ {
+
+ }
+ else if (flowNode.ControlType == NodeControlType.ExpCondition)
+ {
+
+ }
+ else if (flowNode.ControlType == NodeControlType.ExpOp)
+ {
+
+ }
+ else
+ {
+ throw new Exception("无法为该节点生成方法名称");
+ }
+ }*/
\ No newline at end of file
diff --git a/NodeFlow/Services/FlowWorkManagement.cs b/NodeFlow/Services/FlowWorkManagement.cs
index e664b44..4395a9f 100644
--- a/NodeFlow/Services/FlowWorkManagement.cs
+++ b/NodeFlow/Services/FlowWorkManagement.cs
@@ -268,14 +268,16 @@ namespace Serein.NodeFlow.Services
///
///
///
- public async Task StartFlowInSelectNodeAsync(IFlowNode startNode)
+ public async Task StartFlowInSelectNodeAsync(IFlowNode startNode)
{
var pool = WorkOptions.FlowContextPool;
var context = pool.Allocate();
var token = WorkOptions.CancellationTokenSource.Token;
var result = await startNode.StartFlowAsync(context, token); // 开始运行时从选定节点开始运行
+
context.Reset();
pool.Free(context);
+ return result;
}
///
@@ -342,7 +344,7 @@ namespace Serein.NodeFlow.Services
{
var context = pool.Allocate(); // 启动全局触发器时新建上下文
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context, singleToken); // 获取触发器等待Task
- context.AddOrUpdate(singleFlipFlopNode.Guid, newFlowData);
+ context.AddOrUpdateFlowData(singleFlipFlopNode.Guid, newFlowData);
if (context.NextOrientation == ConnectionInvokeType.None)
{
continue;
diff --git a/NodeFlow/Tool/FlowLibrary.cs b/NodeFlow/Tool/FlowLibrary.cs
index eff5b15..0ab4664 100644
--- a/NodeFlow/Tool/FlowLibrary.cs
+++ b/NodeFlow/Tool/FlowLibrary.cs
@@ -14,10 +14,18 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Serein.NodeFlow
{
- public class LibraryMdDd (MethodDetails methodDetails,DelegateDetails delegateDetails)
+ public class LibraryMdDd
{
- public MethodDetails MethodDetails { get; } = methodDetails;
- public DelegateDetails DelegateDetails { get; } = delegateDetails;
+ public MethodDetails MethodDetails { get; }
+ public MethodInfo MethodInfo { get; }
+ public DelegateDetails DelegateDetails { get; }
+
+ public LibraryMdDd(MethodInfo methodInfo, MethodDetails methodDetails, DelegateDetails delegateDetails)
+ {
+ MethodDetails = methodDetails;
+ MethodInfo = methodInfo;
+ DelegateDetails = delegateDetails;
+ }
}
@@ -28,8 +36,6 @@ namespace Serein.NodeFlow
{
public Assembly Assembly { get; private set; }
-
-
//private readonly Action actionOfUnloadAssmbly;
/*, Action actionOfUnloadAssmbly*/
//this.actionOfUnloadAssmbly = actionOfUnloadAssmbly;
@@ -60,6 +66,7 @@ namespace Serein.NodeFlow
/// Value :方法详情
///
public ConcurrentDictionary MethodDetailss { get; } = new ConcurrentDictionary();
+ public ConcurrentDictionary MethodInfos { get; } = new ConcurrentDictionary();
///
/// 管理通过Emit动态构建的委托
@@ -112,6 +119,7 @@ namespace Serein.NodeFlow
public bool LoadAssembly()
{
Assembly assembly = this.Assembly;
+
#region 检查入参
// 加载DLL,创建 MethodDetails、实例作用对象、委托方法
@@ -142,7 +150,7 @@ namespace Serein.NodeFlow
#endregion
-
+
#region 获取 DynamicFlow 特性的流程控制器,如果没有退出
// Type : 具有 DynamicFlowAttribute 标记的类型
// string : 类型元数据 DynamicFlowAttribute 特性中的 Name 属性 (用于生成方法描述时,添加在方法别名中提高可读性)
@@ -183,13 +191,13 @@ namespace Serein.NodeFlow
{
// 尝试创建
if (!NodeMethodDetailsHelper.TryCreateDetails(type, methodInfo, assemblyName,
- out var md, out var dd)) // 返回的描述
+ out var mi, out var md, out var dd)) // 返回的描述
{
SereinEnv.WriteLine(InfoType.ERROR, $"无法加载方法信息:{assemblyName}-{type}-{methodInfo}");
continue;
}
md.MethodAnotherName = flowName + md.MethodAnotherName; // 方法别名
- detailss.Add(new LibraryMdDd(md, dd));
+ detailss.Add(new LibraryMdDd(mi, md, dd));
}
}
@@ -219,6 +227,7 @@ namespace Serein.NodeFlow
{
var key = item.MethodDetails.MethodName;
MethodDetailss.TryAdd(key, item.MethodDetails);
+ MethodInfos.TryAdd(key, item.MethodInfo);
DelegateDetailss.TryAdd(key, item.DelegateDetails);
}
diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs
index 7fbfe1f..80e0cb3 100644
--- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs
+++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs
@@ -35,6 +35,7 @@ public static class NodeMethodDetailsHelper
public static bool TryCreateDetails(Type type,
MethodInfo methodInfo,
string assemblyName,
+ [MaybeNullWhen(false)] out MethodInfo outMethodInfo,
[MaybeNullWhen(false)] out MethodDetails methodDetails,
[MaybeNullWhen(false)] out DelegateDetails delegateDetails)
{
@@ -43,6 +44,7 @@ public static class NodeMethodDetailsHelper
var attribute = methodInfo.GetCustomAttribute();
if(attribute is null || attribute.Scan == false)
{
+ outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
@@ -62,17 +64,18 @@ public static class NodeMethodDetailsHelper
Type? returnType;
- bool isTask = IsGenericTask(methodInfo.ReturnType, out var taskResult);
+ bool isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult);
if (attribute.MethodDynamicType == Library.NodeType.UI)
{
- if (isTask)
+ if (isAsync)
{
var innerType = methodInfo.ReturnType.GetGenericArguments()[0];
if (innerType.IsGenericType && innerType != typeof(IEmbeddedContent))
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,因为UI方法的返回值并非IEmbeddedContent,流程工作台将无法正确显示自定义控件界面以及传递数据。");
+ outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
@@ -83,6 +86,7 @@ public static class NodeMethodDetailsHelper
if (methodInfo.ReturnType != typeof(IEmbeddedContent))
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,因为UI方法的返回值并非IEmbeddedContent,流程工作台将无法正确显示自定义控件界面以及传递数据。");
+ outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
@@ -106,6 +110,7 @@ public static class NodeMethodDetailsHelper
else
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,返回类型非预期的Task>。");
+ outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
@@ -114,13 +119,14 @@ public static class NodeMethodDetailsHelper
else
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,因为触发器方法的返回值并非Task<>,将无法等待。");
+ outMethodInfo = null;
methodDetails = null;
delegateDetails = null;
return false;
}
}
- else if(isTask)
+ else if(isAsync)
{
returnType = taskResult is null ? typeof(Task) : taskResult;
}
@@ -134,7 +140,7 @@ public static class NodeMethodDetailsHelper
}
var asyncPrefix = "[异步]"; // IsGenericTask(returnType) ? "[async]" : ;
- var methodMethodAnotherName = isTask ? asyncPrefix + attribute.AnotherName : attribute.AnotherName;
+ var methodMethodAnotherName = isAsync ? asyncPrefix + attribute.AnotherName : attribute.AnotherName;
bool hasParamsArg = false;
if (explicitDataOfParameters.Length > 0)
@@ -155,13 +161,14 @@ public static class NodeMethodDetailsHelper
ReturnType = returnType,
// 如果存在可变参数,取最后一个元素的下标,否则为-1;
ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length - 1 : -1,
+ IsAsync = isAsync,
};
//var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var methodDelegate);// 返回值
var dd = new DelegateDetails(methodInfo) ; // 构造委托
-
+ outMethodInfo = methodInfo;
methodDetails = md;
delegateDetails = dd;
return true;
diff --git a/Workbench/App.xaml.cs b/Workbench/App.xaml.cs
index 30ce730..ce1654c 100644
--- a/Workbench/App.xaml.cs
+++ b/Workbench/App.xaml.cs
@@ -4,6 +4,7 @@ using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Env;
+using Serein.NodeFlow.Services;
using Serein.Workbench.Api;
using Serein.Workbench.Services;
using Serein.Workbench.ViewModels;
@@ -54,6 +55,7 @@ namespace Serein.Workbench
// 这里是测试代码,可以删除
private async Task LoadLocalProjectAsync()
{
+
if (1 == 11)
{
var projectService = App.GetService();
diff --git a/Workbench/Serein.Workbench.csproj b/Workbench/Serein.Workbench.csproj
index cdb1c5c..88ec9f3 100644
--- a/Workbench/Serein.Workbench.csproj
+++ b/Workbench/Serein.Workbench.csproj
@@ -75,7 +75,7 @@
-
+
diff --git a/Workbench/ServiceCollectionExtensions.cs b/Workbench/ServiceCollectionExtensions.cs
index a27b616..3c6e873 100644
--- a/Workbench/ServiceCollectionExtensions.cs
+++ b/Workbench/ServiceCollectionExtensions.cs
@@ -74,7 +74,6 @@ namespace Serein.Workbench
collection.AddSingleton(flowEnvironment.Event); // 注册运行环境事件
#endregion
-
}
}
diff --git a/Workbench/Themes/NodeTreeItemViewControl.xaml.cs b/Workbench/Themes/NodeTreeItemViewControl.xaml.cs
index be6f0f1..36914d0 100644
--- a/Workbench/Themes/NodeTreeItemViewControl.xaml.cs
+++ b/Workbench/Themes/NodeTreeItemViewControl.xaml.cs
@@ -142,7 +142,7 @@ namespace Serein.Workbench.Themes
{
try
{
- await flowEnvironment.FlowControl.StartFlowFromSelectNodeAsync(tmpNodeTreeModel.RootNode.Guid);
+ await flowEnvironment.FlowControl.StartFlowAsync(tmpNodeTreeModel.RootNode.Guid);
}
catch (Exception ex)
{
diff --git a/Workbench/ViewModels/MainMenuBarViewModel.cs b/Workbench/ViewModels/MainMenuBarViewModel.cs
index 2f25672..e8cafac 100644
--- a/Workbench/ViewModels/MainMenuBarViewModel.cs
+++ b/Workbench/ViewModels/MainMenuBarViewModel.cs
@@ -1,7 +1,9 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Serein.Library.Api;
+using Serein.NodeFlow.Services;
using Serein.Workbench.Services;
+using System.Diagnostics;
using System.Windows.Input;
namespace Serein.Workbench.ViewModels
@@ -130,6 +132,20 @@ namespace Serein.Workbench.ViewModels
private void OpenRemoteServer()
{
+ try
+ {
+
+
+ var env = App.GetService();
+ var flowModelService = env.IOC.Get();
+ var text = flowModelService.ToCsharpCoreFile(); ;
+ Debug.WriteLine(text);
+
+ }
+ catch (Exception ex)
+ {
+
+ }
flowEnvironment.StartRemoteServerAsync();
}
diff --git a/Workbench/Views/FlowCanvasView.xaml.cs b/Workbench/Views/FlowCanvasView.xaml.cs
index 6e33c43..3433c33 100644
--- a/Workbench/Views/FlowCanvasView.xaml.cs
+++ b/Workbench/Views/FlowCanvasView.xaml.cs
@@ -551,7 +551,6 @@ namespace Serein.Workbench.Views
}
-
if (key == Key.F5)
{
if (keyEventService.GetKeyState(Key.LeftCtrl) || keyEventService.GetKeyState(Key.RightCtrl))
@@ -564,7 +563,7 @@ namespace Serein.Workbench.Views
// F5 调试当前选定节点
var nodeModel = selectNodeControls[0].ViewModel.NodeModel;
SereinEnv.WriteLine(InfoType.INFO, $"调试运行当前节点:{nodeModel.Guid}");
- _ = flowEnvironment.FlowControl.StartFlowFromSelectNodeAsync(nodeModel.Guid);
+ _ = flowEnvironment.FlowControl.StartFlowAsync(nodeModel.Guid);
//_ = nodeModel.StartFlowAsync(new DynamicContext(flowEnvironment), new CancellationToken());
}
diff --git a/Workbench/Views/MainMenuBarView.xaml b/Workbench/Views/MainMenuBarView.xaml
index 56395c6..b1686e5 100644
--- a/Workbench/Views/MainMenuBarView.xaml
+++ b/Workbench/Views/MainMenuBarView.xaml
@@ -35,7 +35,7 @@