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 InvokeAsync(string apiGuid, Dictionary dict) @@ -334,9 +362,41 @@ namespace Serein.Library.FlowNode throw new NotImplementedException(); } - public Task StartFlowFromSelectNodeAsync(string startNodeGuid) + + //private readonly DefaultObjectPool _stackPool = new DefaultObjectPool(new DynamicContext(this)); + + + public async Task StartFlowAsync(string startNodeGuid) { - throw new NotImplementedException(); + IDynamicContext context = new DynamicContext(flowEnvironment); + CancellationTokenSource cts = new CancellationTokenSource(); +#if DEBUG + + FlowResult flowResult = await BenchmarkHelpers.BenchmarkAsync(async () => + { + var node = flowCallTree.Get(startNodeGuid); + var flowResult = await node.StartFlowAsync(context, cts.Token); + return flowResult; + }); +#else + var node = flowCallTree.Get(startNodeGuid); + FlowResult flowResult = await node.StartFlowAsync(context, cts.Token); +#endif + + cts?.Cancel(); + cts?.Dispose(); + if (flowResult.Value is TResult result) + { + return result; + } + else if (flowResult is FlowResult && flowResult is TResult result2) + { + return result2; + } + else + { + throw new ArgumentNullException($"类型转换失败,流程返回数据与泛型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}]。"); + } } public async Task StartFlowAsync(string[] canvasGuids) { @@ -501,6 +561,14 @@ namespace Serein.Library.FlowNode /// 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 @@ - +