diff --git a/Library/Api/IDynamicContext.cs b/Library/Api/IDynamicContext.cs index db5b973..dcb1b81 100644 --- a/Library/Api/IDynamicContext.cs +++ b/Library/Api/IDynamicContext.cs @@ -54,22 +54,21 @@ namespace Serein.Library.Api /// /// 获取节点的数据(当前节点需要获取上一节点数据时,需要从 运行时上一节点 的Guid 通过这个方法进行获取 /// - /// + /// /// - object GetFlowData(string nodeGuid); - + FlowResult GetFlowData(NodeModelBase nodeModel); /// /// 上一节点数据透传到下一节点 /// /// - object TransmissionData(NodeModelBase nodeModel); + FlowResult TransmissionData(NodeModelBase nodeModel); /// /// 添加或更新当前节点的数据 /// - /// + /// /// - void AddOrUpdate(string nodeGuid, object flowData); + void AddOrUpdate(NodeModelBase nodeModel, FlowResult flowData); /// /// 重置流程状态(用于对象池回收) diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index 4cf05e8..172b81a 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -950,6 +950,13 @@ namespace Serein.Library.Api #endregion #region 流程运行相关 + /// + /// 获取节点信息 + /// + /// + /// + /// + bool TryGetNodeModel(string nodeGuid, out NodeModelBase nodeModel); /// /// 获取方法描述信息 @@ -1026,7 +1033,7 @@ namespace Serein.Library.Api /// 调用时的上下文 /// 节点Guid /// - Task InvokeNodeAsync(IDynamicContext context, string nodeGuid); + // Task InvokeNodeAsync(IDynamicContext context, string nodeGuid); #endregion diff --git a/Library/Api/IScriptFlowApi.cs b/Library/Api/IScriptFlowApi.cs index 0cc4e4a..c0dd0e8 100644 --- a/Library/Api/IScriptFlowApi.cs +++ b/Library/Api/IScriptFlowApi.cs @@ -26,13 +26,13 @@ namespace Serein.Library.Api /// /// /// - object GetArgData(IDynamicContext context, int index); + //object GetArgData(IDynamicContext context, int index); /// /// 获取流程当前传递的数据 /// /// /// - object GetFlowData(IDynamicContext context); + /// FlowResult GetFlowData(IDynamicContext context); /// /// 获取全局数据 diff --git a/Library/Extension/NodeModelExtension.cs b/Library/Extension/NodeModelExtension.cs new file mode 100644 index 0000000..b400bf4 --- /dev/null +++ b/Library/Extension/NodeModelExtension.cs @@ -0,0 +1,432 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Serein.Library.Api; +using Serein.Library.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Serein.Library +{ + /// + /// 节点方法拓展 + /// + public static class NodeModelExtension + { + /// + /// 输出方法参数信息 + /// + /// + public static ParameterData[] SaveParameterInfo(this NodeModelBase nodeModel) + { + if (nodeModel.MethodDetails.ParameterDetailss == null) + { + return new ParameterData[0]; + } + + if (nodeModel.MethodDetails.ParameterDetailss.Length > 0) + { + return nodeModel.MethodDetails.ParameterDetailss + .Select(it => new ParameterData + { + SourceNodeGuid = it.ArgDataSourceNodeGuid, + SourceType = it.ArgDataSourceType.ToString(), + State = it.IsExplicitData, + ArgName = it.Name, + Value = it.DataValue, + + }) + .ToArray(); + } + else + { + return Array.Empty(); + } + } + + /// + /// 导出为节点信息 + /// + /// + public static NodeInfo ToInfo(this NodeModelBase nodeModel) + { + // if (MethodDetails == null) return null; + + var trueNodes = nodeModel.SuccessorNodes[ConnectionInvokeType.IsSucceed].Select(item => item.Guid); // 真分支 + var falseNodes = nodeModel.SuccessorNodes[ConnectionInvokeType.IsFail].Select(item => item.Guid);// 假分支 + var errorNodes = nodeModel.SuccessorNodes[ConnectionInvokeType.IsError].Select(item => item.Guid);// 异常分支 + var upstreamNodes = nodeModel.SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支 + // 生成参数列表 + ParameterData[] parameterData = nodeModel.SaveParameterInfo(); + + NodeInfo nodeInfo = new NodeInfo + { + Guid = nodeModel.Guid, + AssemblyName = nodeModel.MethodDetails.AssemblyName, + MethodName = nodeModel.MethodDetails?.MethodName, + Label = nodeModel.MethodDetails?.MethodAnotherName, + Type = nodeModel.ControlType.ToString(), //this.GetType().ToString(), + TrueNodes = trueNodes.ToArray(), + FalseNodes = falseNodes.ToArray(), + UpstreamNodes = upstreamNodes.ToArray(), + ParameterData = parameterData.ToArray(), + ErrorNodes = errorNodes.ToArray(), + Position = nodeModel.Position, + IsProtectionParameter = nodeModel.MethodDetails.IsProtectionParameter, + IsInterrupt = nodeModel.DebugSetting.IsInterrupt, + IsEnable = nodeModel.DebugSetting.IsEnable, + ParentNodeGuid = nodeModel.ContainerNode?.Guid, + ChildNodeGuids = nodeModel.ChildrenNode.Select(item => item.Guid).ToArray(), + }; + nodeInfo.Position.X = Math.Round(nodeInfo.Position.X, 1); + nodeInfo.Position.Y = Math.Round(nodeInfo.Position.Y, 1); + nodeInfo = nodeModel.SaveCustomData(nodeInfo); + return nodeInfo; + } + + /// + /// 从节点信息加载节点 + /// + /// + /// + public static void LoadInfo(this NodeModelBase nodeModel, NodeInfo nodeInfo) + { + nodeModel.Guid = nodeInfo.Guid; + nodeModel.Position = nodeInfo.Position ?? new PositionOfUI(0, 0);// 加载位置信息 + var md = nodeModel.MethodDetails; // 当前节点的方法说明 + nodeModel.MethodDetails.IsProtectionParameter = nodeInfo.IsProtectionParameter; // 保护参数 + nodeModel.DebugSetting.IsInterrupt = nodeInfo.IsInterrupt; // 是否中断 + nodeModel.DebugSetting.IsEnable = nodeInfo.IsEnable; // 是否使能 + + if (md != null) + { + if (md.ParameterDetailss == null) + { + md.ParameterDetailss = new ParameterDetails[0]; + } + + var pds = md.ParameterDetailss; // 当前节点的入参描述数组 + #region 类库方法型节点加载参数 + if (nodeInfo.ParameterData.Length > pds.Length && md.HasParamsArg) + { + // 保存的参数信息项数量大于方法本身的方法入参数量(可能存在可变入参) + var length = nodeInfo.ParameterData.Length - pds.Length; // 需要扩容的长度 + nodeModel.MethodDetails.ParameterDetailss = ArrayHelper.Expansion(pds, length); // 扩容入参描述数组 + pds = md.ParameterDetailss; // 当前节点的入参描述数组 + var startParmsPd = pds[md.ParamsArgIndex]; // 获取可变入参参数描述 + for (int i = md.ParamsArgIndex + 1; i <= md.ParamsArgIndex + length; i++) + { + pds[i] = startParmsPd.CloneOfModel(nodeModel); + pds[i].Index = pds[i - 1].Index + 1; + pds[i].IsParams = true; + } + } + + for (int i = 0; i < nodeInfo.ParameterData.Length; i++) + { + if (i >= pds.Length) + { + nodeModel.Env.WriteLine(InfoType.ERROR, $"保存的参数数量大于方法此时的入参参数数量:[{nodeInfo.Guid}][{nodeInfo.MethodName}]"); + break; + } + var pd = pds[i]; + ParameterData pdInfo = nodeInfo.ParameterData[i]; + pd.IsExplicitData = pdInfo.State; + pd.DataValue = pdInfo.Value; + pd.ArgDataSourceType = EnumHelper.ConvertEnum(pdInfo.SourceType); + pd.ArgDataSourceNodeGuid = pdInfo.SourceNodeGuid; + + } + + nodeModel.LoadCustomData(nodeInfo); // 加载自定义数据 + + #endregion + } + } + + + /// + /// 开始执行 + /// + /// + /// 流程运行 + /// + public static async Task StartFlowAsync(this NodeModelBase nodeModel, IDynamicContext context, CancellationToken token) + { + Stack stack = new Stack(); + HashSet processedNodes = new HashSet(); // 用于记录已处理上游节点的节点 + stack.Push(nodeModel); + while (context.RunState != RunState.Completion // 没有完成 + && token.IsCancellationRequested == false // 没有取消 + && stack.Count > 0) // 循环中直到栈为空才会退出循环 + { + + +#if DEBUG + await Task.Delay(1); +#endif + + #region 执行相关 + + // 从栈中弹出一个节点作为当前节点进行处理 + var currentNode = stack.Pop(); + context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态 + + FlowResult flowResult; + try + { + flowResult = await currentNode.ExecutingAsync(context, token); + + if (context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置 + { + context.NextOrientation = ConnectionInvokeType.IsSucceed; + } + } + catch (Exception ex) + { + flowResult = new FlowResult(currentNode,context); + context.Env.WriteLine(InfoType.ERROR, $"节点[{currentNode.Guid}]异常:" + ex); + context.NextOrientation = ConnectionInvokeType.IsError; + context.ExceptionOfRuning = ex; + } + context.AddOrUpdate(currentNode, flowResult); // 上下文中更新数据 + #endregion + + #region 执行完成 + + // 首先将指定类别后继分支的所有节点逆序推入栈中 + var nextNodes = currentNode.SuccessorNodes[context.NextOrientation]; + for (int index = nextNodes.Count - 1; index >= 0; index--) + { + // 筛选出启用的节点的节点 + if (nextNodes[index].DebugSetting.IsEnable) + { + context.SetPreviousNode(nextNodes[index], currentNode); + stack.Push(nextNodes[index]); + } + } + // 然后将指上游分支的所有节点逆序推入栈中 + var upstreamNodes = currentNode.SuccessorNodes[ConnectionInvokeType.Upstream]; + for (int index = upstreamNodes.Count - 1; index >= 0; index--) + { + // 筛选出启用的节点的节点 + if (upstreamNodes[index].DebugSetting.IsEnable) + { + context.SetPreviousNode(upstreamNodes[index], currentNode); + stack.Push(upstreamNodes[index]); + } + } + #endregion + + } + } + + /// + /// 获取对应的参数数组 + /// + public static async Task GetParametersAsync(this NodeModelBase nodeModel, IDynamicContext context, CancellationToken token) + { + if (nodeModel.MethodDetails.ParameterDetailss.Length == 0) + { + return Array.Empty(); // 无参数 + } + var md = nodeModel.MethodDetails; + var pds = md.ParameterDetailss; + + #region 定义返回的参数数组 + object[] args; + Array paramsArgs = null; // 初始化可选参数 + int paramsArgIndex = 0; // 可选参数下标,与 object[] paramsArgs 一起使用 + if (md.ParamsArgIndex >= 0) // 存在可变入参参数 + { + var paramsArgType = pds[md.ParamsArgIndex].DataType; // 获取可变参数的参数类型 + int paramsLength = pds.Length - md.ParamsArgIndex; // 可变参数数组长度 = 方法参数个数 - ( 可选入参下标 + 1 ) + paramsArgs = Array.CreateInstance(paramsArgType, paramsLength);// 可变参数 + args = new object[md.ParamsArgIndex + 1]; // 调用方法的入参数组 + args[md.ParamsArgIndex] = paramsArgs; // 如果存在可选参数,入参参数最后一项则为可变参数 + } + else + { + // 不存在可选参数 + args = new object[pds.Length]; // 调用方法的入参数组 + } + #endregion + + // 常规参数的获取 + for (int i = 0; i < args.Length; i++) + { + var pd = pds[i]; + + args[i] = await pd.ToMethodArgData(context); // 获取数据 + } + + // 可选参数的获取 + if (md.ParamsArgIndex >= 0) + { + for (int i = 0; i < paramsArgs.Length; i++) + { + var pd = md.ParameterDetailss[paramsArgIndex + i]; + var data = await pd.ToMethodArgData(context); // 获取数据 + paramsArgs.SetValue(data, i);// 设置到数组中 + } + args[args.Length - 1] = paramsArgs; + } + + return args; + } + + /// + /// 检查监视表达式是否生效 + /// + /// 节点Moel + /// 上下文 + /// 新的数据 + /// + /*public static async Task CheckExpInterrupt(this NodeModelBase nodeModel, IDynamicContext context, object newData = null) + { + string guid = nodeModel.Guid; + context.AddOrUpdate(guid, newData); // 上下文中更新数据 + if (newData is null) + { + } + else + { + await nodeModel.MonitorObjExpInterrupt(context, newData, 0); // 首先监视对象 + await nodeModel.MonitorObjExpInterrupt(context, newData, 1); // 然后监视节点 + //nodeModel.FlowData = newData; // 替换数据 + } + }*/ + + /// + /// 监视对象表达式中断 + /// + /// + /// + /// + /// + /// + /*private static async Task MonitorObjExpInterrupt(this NodeModelBase nodeModel, IDynamicContext context, object data, int monitorType) + { + MonitorObjectEventArgs.ObjSourceType sourceType; + string key; + if (monitorType == 0) + { + key = data?.GetType()?.FullName; + sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; + } + else + { + key = nodeModel.Guid; + sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; + } + if (string.IsNullOrEmpty(key)) + { + return; + } + //(var isMonitor, var exps) = await context.Env.CheckObjMonitorStateAsync(key); + //if (isMonitor) // 如果新的数据处于查看状态,通知UI进行更新?交给运行环境判断? + //{ + // context.Env.MonitorObjectNotification(nodeModel.Guid, data, sourceType); // 对象处于监视状态,通知UI更新数据显示 + // if (exps.Length > 0) + // { + // // 表达式环境下判断是否需要执行中断 + // bool isExpInterrupt = false; + // string exp = ""; + // // 判断执行监视表达式,直到为 true 时退出 + // for (int i = 0; i < exps.Length && !isExpInterrupt; i++) + // { + // exp = exps[i]; + // if (string.IsNullOrEmpty(exp)) continue; + // // isExpInterrupt = SereinConditionParser.To(data, exp); + // } + + // if (isExpInterrupt) // 触发中断 + // { + // nodeModel.DebugSetting.IsInterrupt = true; + // if (await context.Env.SetNodeInterruptAsync(nodeModel.Guid,true)) + // { + // context.Env.TriggerInterrupt(nodeModel.Guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp); + // var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); + // await Console.Out.WriteLineAsync($"[{data}]中断已{cancelType},开始执行后继分支"); + // nodeModel.DebugSetting.IsInterrupt = false; + // } + // } + // } + + //} + }*/ + + + /// + /// 不再中断 + /// + public static void CancelInterrupt(NodeModelBase nodeModel) + { + nodeModel.DebugSetting.IsInterrupt = false; + nodeModel.DebugSetting.CancelInterrupt?.Invoke(); + } + +#if DEBUG + /// + /// 程序集更新,更新节点方法描述、以及所有入参描述的类型 + /// + /// 节点Model + /// 新的方法描述 + public static void UploadMethod(this NodeModelBase nodeModel, MethodDetails newMd) + { + var thisMd = nodeModel.MethodDetails; + + thisMd.ActingInstanceType = newMd.ActingInstanceType; // 更新方法需要的类型 + + var thisPds = thisMd.ParameterDetailss; + var newPds = newMd.ParameterDetailss; + // 当前存在可变参数,且新的方法也存在可变参数,需要把可变参数的数目与值传递过去 + if (thisMd.HasParamsArg && newMd.HasParamsArg) + { + int paramsLength = thisPds.Length - thisMd.ParamsArgIndex - 1; // 确定扩容长度 + newMd.ParameterDetailss = ArrayHelper.Expansion(newPds, paramsLength);// 为新方法的入参参数描述进行扩容 + newPds = newMd.ParameterDetailss; + int index = newMd.ParamsArgIndex; // 记录 + var templatePd = newPds[newMd.ParamsArgIndex]; // 新的入参模板 + for (int i = thisMd.ParamsArgIndex; i < thisPds.Length; i++) + { + ParameterDetails thisPd = thisPds[i]; + var newPd = templatePd.CloneOfModel(nodeModel); // 复制参数描述 + newPd.Index = i + 1; // 更新索引 + newPd.IsParams = true; + newPd.DataValue = thisPd.DataValue; // 保留参数值 + newPd.ArgDataSourceNodeGuid = thisPd.ArgDataSourceNodeGuid; // 保留参数来源信息 + newPd.ArgDataSourceType = thisPd.ArgDataSourceType; // 保留参数来源信息 + newPd.IsParams = thisPd.IsParams; // 保留显式参数设置 + newPds[index++] = newPd; + } + } + + + var thidPdLength = thisMd.HasParamsArg ? thisMd.ParamsArgIndex : thisPds.Length; + // 遍历当前的参数描述(不包含可变参数),找到匹配项,复制必要的数据进行保留 + for (int i = 0; i < thisPds.Length; i++) + { + ParameterDetails thisPd = thisPds[i]; + var newPd = newPds.FirstOrDefault(t_newPd => !t_newPd.IsParams // 不为可变参数 + && t_newPd.Name.Equals(thisPd.Name, StringComparison.OrdinalIgnoreCase) // 存在相同名称 + && t_newPd.DataType.Name.Equals(thisPd.DataType.Name) // 存在相同入参类型名称(以类型作为区分) + ); + if (newPd != null) // 如果匹配上了 + { + newPd.DataValue = thisPd.DataValue; // 保留参数值 + newPd.ArgDataSourceNodeGuid = thisPd.ArgDataSourceNodeGuid; // 保留参数来源信息 + newPd.ArgDataSourceType = thisPd.ArgDataSourceType; // 保留参数来源信息 + newPd.IsParams = thisPd.IsParams; // 保留显式参数设置 + } + } + thisMd.ReturnType = newMd.ReturnType; + nodeModel.MethodDetails = newMd; + + } +#endif + + } +} diff --git a/Library/Extension/SereinExtension.cs b/Library/Extension/SereinExtension.cs index d04aca5..50b7ec0 100644 --- a/Library/Extension/SereinExtension.cs +++ b/Library/Extension/SereinExtension.cs @@ -13,109 +13,6 @@ namespace Serein.Library /// public static partial class SereinExtension { - /// - /// 字面量转为对应类型 - /// - /// - /// - public static Type ToTypeOfString(this string valueStr) - { - if (valueStr.IndexOf('.') != -1) - { - // 通过指定的类型名称获取类型 - return Type.GetType(valueStr); - } - - - if (valueStr.Equals("bool", StringComparison.OrdinalIgnoreCase)) - { - return typeof(bool); - } - #region 整数型 - else if (valueStr.Equals("sbyte", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(SByte), StringComparison.OrdinalIgnoreCase)) - { - return typeof(SByte); - } - else if (valueStr.Equals("short", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(Int16), StringComparison.OrdinalIgnoreCase)) - { - return typeof(Int16); - } - else if (valueStr.Equals("int", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(Int32), StringComparison.OrdinalIgnoreCase)) - { - return typeof(Int32); - } - else if (valueStr.Equals("long", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(Int64), StringComparison.OrdinalIgnoreCase)) - { - return typeof(Int64); - } - - else if (valueStr.Equals("byte", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(Byte), StringComparison.OrdinalIgnoreCase)) - { - return typeof(Byte); - } - else if (valueStr.Equals("ushort", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(UInt16), StringComparison.OrdinalIgnoreCase)) - { - return typeof(UInt16); - } - else if (valueStr.Equals("uint", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(UInt32), StringComparison.OrdinalIgnoreCase)) - { - return typeof(UInt32); - } - else if (valueStr.Equals("ulong", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(UInt64), StringComparison.OrdinalIgnoreCase)) - { - return typeof(UInt64); - } - #endregion - - #region 浮点型 - else if (valueStr.Equals("float", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(Single), StringComparison.OrdinalIgnoreCase)) - { - return typeof(Single); - } - else if (valueStr.Equals("double", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(Double), StringComparison.OrdinalIgnoreCase)) - { - return typeof(Double); - } - #endregion - - #region 小数型 - - else if (valueStr.Equals("decimal", StringComparison.OrdinalIgnoreCase) - || valueStr.Equals(nameof(Decimal), StringComparison.OrdinalIgnoreCase)) - { - return typeof(Decimal); - } - #endregion - - #region 其他常见的类型 - else if (valueStr.Equals(nameof(DateTime), StringComparison.OrdinalIgnoreCase)) - { - return typeof(DateTime); - } - - else if (valueStr.Equals(nameof(String), StringComparison.OrdinalIgnoreCase)) - { - return typeof(String); - } - #endregion - - else - { - throw new ArgumentException($"无法解析的字面量类型[{valueStr}]"); - } - } - - /// /// 判断连接类型 /// diff --git a/Library/FlowNode/ContainerFlowEnvironment.cs b/Library/FlowNode/ContainerFlowEnvironment.cs index 1677008..607b65c 100644 --- a/Library/FlowNode/ContainerFlowEnvironment.cs +++ b/Library/FlowNode/ContainerFlowEnvironment.cs @@ -378,6 +378,11 @@ namespace Serein.Library sereinIOC.Run(action); return this; } + + public bool TryGetNodeModel(string nodeGuid, out NodeModelBase nodeModel) + { + throw new NotImplementedException(); + } #endregion diff --git a/Library/FlowNode/DynamicContext.cs b/Library/FlowNode/DynamicContext.cs index a1b6bc6..2a43d31 100644 --- a/Library/FlowNode/DynamicContext.cs +++ b/Library/FlowNode/DynamicContext.cs @@ -49,7 +49,7 @@ namespace Serein.Library /// /// 每个流程上下文分别存放节点的当前数据 /// - private readonly ConcurrentDictionary dictNodeFlowData = new ConcurrentDictionary(); + private readonly ConcurrentDictionary dictNodeFlowData = new ConcurrentDictionary(); /// /// 每个流程上下文存储运行时节点的调用关系 @@ -88,7 +88,7 @@ namespace Serein.Library /// /// 节点 /// - public object GetFlowData(string nodeGuid) + public FlowResult GetFlowData(NodeModelBase nodeGuid) { if (dictNodeFlowData.TryGetValue(nodeGuid, out var data)) { @@ -103,29 +103,29 @@ namespace Serein.Library /// /// 添加或更新当前节点数据 /// - /// 节点 + /// 节点 /// 新的数据 - public void AddOrUpdate(string nodeGuid, object flowData) + public void AddOrUpdate(NodeModelBase nodeModel, FlowResult flowData) { // this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData); - dictNodeFlowData.AddOrUpdate(nodeGuid, _ => flowData, (o,n ) => flowData); + dictNodeFlowData.AddOrUpdate(nodeModel, _ => flowData, (o,n ) => flowData); } /// /// 上一节点数据透传到下一节点 /// /// - public object TransmissionData(NodeModelBase nodeModel) + public FlowResult TransmissionData(NodeModelBase nodeModel) { if (dictPreviousNodes.TryGetValue(nodeModel, out var previousNode)) // 首先获取当前节点的上一节点 { - if (dictNodeFlowData.TryGetValue(previousNode.Guid, out var data)) // 其次获取上一节点的数据 + if (dictNodeFlowData.TryGetValue(previousNode, out var data)) // 其次获取上一节点的数据 { return data; //AddOrUpdate(nodeModel.Guid, data); // 然后作为当前节点的数据记录在上下文中 } } - return null; + throw new InvalidOperationException($"透传{nodeModel.Guid}节点数据时发生异常:上一节点不存在数据"); } /// diff --git a/Library/FlowNode/FlowResult.cs b/Library/FlowNode/FlowResult.cs new file mode 100644 index 0000000..350c756 --- /dev/null +++ b/Library/FlowNode/FlowResult.cs @@ -0,0 +1,97 @@ +using Serein.Library.Api; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library +{ + /// + /// 表示空数据 + /// + /*public readonly struct Unit : IEquatable + { + public static readonly Unit Default = default; + public bool Equals(Unit _) => true; + public override bool Equals(object obj) => obj is Unit; + public override int GetHashCode() => 0; + }*/ + + + /// + /// 流程返回值的包装 + /// + public class FlowResult + { + /// + /// 实例化返回值 + /// + /// + /// + public FlowResult(NodeModelBase nodeModel, IDynamicContext context, object value) + { + this.NodeGuid = nodeModel.Guid; + this.ContextGuid = context.Guid; + this.Value = value; + } + /// + /// 空返回值 + /// + /// + /// + public FlowResult(NodeModelBase nodeModel, IDynamicContext context) + { + this.NodeGuid = nodeModel.Guid; + this.ContextGuid = context.Guid; + this.Value = Unit.Default; + } + /// + /// 尝试获取值 + /// + /// 目标类型 + /// 返回值 + /// 指示是否获取成功 + /// 无法转为对应类型 + public bool TryGetValue(Type targetType, out object value) + { + if (targetType is null) + throw new ArgumentNullException(nameof(targetType)); + + if (targetType.IsInstanceOfType(Value)) + { + value = Value; + return true; + } + + value = Unit.Default; + return false; + } + + + + /// + /// 来源节点Guid + /// + public string NodeGuid { get; } + /// + /// 来源上下文Guid + /// + public string ContextGuid { get; } + /// + /// 数据值 + /// + public object Value { get; private set; } + /// + /// 生成时间 + /// + public DateTime ResultTime { get; } = DateTime.MinValue; + + /// + /// 是否自动回收 + /// + public bool IsAutoRecovery { get; set; } + + } +} diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index c836087..fa3a573 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -27,7 +27,6 @@ namespace Serein.Library /// public abstract partial class NodeModelBase : IDynamicFlowNode { - #region 节点相关事件 /// /// 实体节点创建完成后调用的方法,调用时间早于 LoadInfo() 方法 /// @@ -65,7 +64,7 @@ namespace Serein.Library } this.DebugSetting.NodeModel = null; this.DebugSetting = null; - if(this.MethodDetails.ParameterDetailss != null) + if (this.MethodDetails.ParameterDetailss != null) { foreach (var pd in this.MethodDetails.ParameterDetailss) { @@ -79,7 +78,7 @@ namespace Serein.Library pd.InputType = ParameterValueInputType.Input; } } - + this.MethodDetails.ParameterDetailss = null; //this.MethodDetails.ActingInstance = null; this.MethodDetails.NodeModel = null; @@ -96,280 +95,22 @@ namespace Serein.Library this.Env = null; } - /// - /// 输出方法参数信息 - /// - /// - public ParameterData[] SaveParameterInfo() - { - if(MethodDetails.ParameterDetailss == null) - { - return new ParameterData[0]; - } - - if (MethodDetails.ParameterDetailss.Length > 0) - { - return MethodDetails.ParameterDetailss - .Select(it => new ParameterData - { - SourceNodeGuid = it.ArgDataSourceNodeGuid, - SourceType = it.ArgDataSourceType.ToString(), - State = it.IsExplicitData, - ArgName = it.Name, - Value = it.DataValue, - - }) - .ToArray(); - } - else - { - return new ParameterData[0]; - } - } - - /// - /// 导出为节点信息 - /// - /// - public NodeInfo ToInfo() - { - // if (MethodDetails == null) return null; - - var trueNodes = SuccessorNodes[ConnectionInvokeType.IsSucceed].Select(item => item.Guid); // 真分支 - var falseNodes = SuccessorNodes[ConnectionInvokeType.IsFail].Select(item => item.Guid);// 假分支 - var errorNodes = SuccessorNodes[ConnectionInvokeType.IsError].Select(item => item.Guid);// 异常分支 - var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支 - // 生成参数列表 - ParameterData[] parameterData = SaveParameterInfo(); - - NodeInfo nodeInfo = new NodeInfo - { - Guid = Guid, - AssemblyName = MethodDetails.AssemblyName, - MethodName = MethodDetails?.MethodName, - Label = MethodDetails?.MethodAnotherName, - Type = ControlType.ToString() , //this.GetType().ToString(), - TrueNodes = trueNodes.ToArray(), - FalseNodes = falseNodes.ToArray(), - UpstreamNodes = upstreamNodes.ToArray(), - ParameterData = parameterData.ToArray(), - ErrorNodes = errorNodes.ToArray(), - Position = Position, - IsProtectionParameter = this.MethodDetails.IsProtectionParameter, - IsInterrupt = this.DebugSetting.IsInterrupt, - IsEnable = this.DebugSetting.IsEnable, - ParentNodeGuid = ContainerNode?.Guid, - ChildNodeGuids = ChildrenNode.Select(item => item.Guid).ToArray(), - }; - nodeInfo.Position.X = Math.Round(nodeInfo.Position.X, 1); - nodeInfo.Position.Y = Math.Round(nodeInfo.Position.Y, 1); - nodeInfo = SaveCustomData(nodeInfo); - return nodeInfo; - } - - /// - /// 从节点信息加载节点 - /// - /// - /// - public void LoadInfo(NodeInfo nodeInfo) - { - this.Guid = nodeInfo.Guid; - this.Position = nodeInfo.Position ?? new PositionOfUI(0, 0);// 加载位置信息 - var md = this.MethodDetails; // 当前节点的方法说明 - this.MethodDetails.IsProtectionParameter = nodeInfo.IsProtectionParameter; // 保护参数 - this.DebugSetting.IsInterrupt = nodeInfo.IsInterrupt; // 是否中断 - this.DebugSetting.IsEnable = nodeInfo.IsEnable; // 是否使能 - - if (md != null) - { - if(md.ParameterDetailss == null) - { - md.ParameterDetailss = new ParameterDetails[0]; - } - - var pds = md.ParameterDetailss; // 当前节点的入参描述数组 - #region 类库方法型节点加载参数 - if (nodeInfo.ParameterData.Length > pds.Length && md.HasParamsArg) - { - // 保存的参数信息项数量大于方法本身的方法入参数量(可能存在可变入参) - var length = nodeInfo.ParameterData.Length - pds.Length; // 需要扩容的长度 - this.MethodDetails.ParameterDetailss = ArrayHelper.Expansion(pds, length); // 扩容入参描述数组 - pds = md.ParameterDetailss; // 当前节点的入参描述数组 - var startParmsPd = pds[md.ParamsArgIndex]; // 获取可变入参参数描述 - for (int i = md.ParamsArgIndex + 1; i <= md.ParamsArgIndex + length; i++) - { - pds[i] = startParmsPd.CloneOfModel(this); - pds[i].Index = pds[i - 1].Index + 1; - pds[i].IsParams = true; - } - } - - for (int i = 0; i < nodeInfo.ParameterData.Length; i++) - { - if (i >= pds.Length) - { - Env.WriteLine(InfoType.ERROR, $"保存的参数数量大于方法此时的入参参数数量:[{nodeInfo.Guid}][{nodeInfo.MethodName}]"); - break; - } - var pd = pds[i]; - ParameterData pdInfo = nodeInfo.ParameterData[i]; - pd.IsExplicitData = pdInfo.State; - pd.DataValue = pdInfo.Value; - pd.ArgDataSourceType = EnumHelper.ConvertEnum(pdInfo.SourceType); - pd.ArgDataSourceNodeGuid = pdInfo.SourceNodeGuid; - - } - - LoadCustomData(nodeInfo); // 加载自定义数据 - - #endregion - } - } - #endregion - - #region 调试中断 - - /// - /// 不再中断 - /// - public void CancelInterrupt() - { - this.DebugSetting.IsInterrupt = false; - DebugSetting.CancelInterrupt?.Invoke(); - } - - #endregion - - #region 节点方法的执行 - - /// - /// 是否应该退出执行 - /// - /// - /// - /// - //public static bool IsBradk(IDynamicContext context) - //{ - // // 上下文不再执行 - // if (context.RunState == RunState.Completion) - // { - // return true; - // } - - // // 不存在全局触发器时,流程运行状态被设置为完成,退出执行,用于打断无限循环分支。 - // if (flowCts is null && context.Env.FlowState == RunState.Completion) - // { - // return true; - // } - - // // 如果存在全局触发器,且触发器的执行任务已经被取消时,退出执行。 - // if (flowCts != null) - // { - // if (flowCts.IsCancellationRequested) - // return true; - // } - // return false; - //} - - /// - /// 开始执行 - /// - /// - /// 流程运行 - /// - public async Task StartFlowAsync(IDynamicContext context, CancellationToken token) - { - Stack stack = new Stack(); - HashSet processedNodes = new HashSet(); // 用于记录已处理上游节点的节点 - stack.Push(this); - while (context.RunState != RunState.Completion // 没有完成 - && token.IsCancellationRequested == false // 没有取消 - && stack.Count > 0) // 循环中直到栈为空才会退出循环 - { - - -#if DEBUG - await Task.Delay(1); -#endif - - #region 执行相关 - - // 从栈中弹出一个节点作为当前节点进行处理 - var currentNode = stack.Pop(); - context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态 - - object newFlowData; - try - { - newFlowData = await currentNode.ExecutingAsync(context, token); - - if (context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置 - { - context.NextOrientation = ConnectionInvokeType.IsSucceed; - } - } - catch (Exception ex) - { - newFlowData = null; - context.Env.WriteLine(InfoType.ERROR, $"节点[{currentNode.Guid}]异常:" + ex); - context.NextOrientation = ConnectionInvokeType.IsError; - context.ExceptionOfRuning = ex; - } - context.AddOrUpdate(currentNode.Guid, newFlowData); // 上下文中更新数据 - #endregion - - #region 执行完成 - - // 首先将指定类别后继分支的所有节点逆序推入栈中 - var nextNodes = currentNode.SuccessorNodes[context.NextOrientation]; - for (int index = nextNodes.Count - 1; index >= 0; index--) - { - // 筛选出启用的节点的节点 - if (nextNodes[index].DebugSetting.IsEnable) - { - context.SetPreviousNode(nextNodes[index], currentNode); - stack.Push(nextNodes[index]); - } - } - // 然后将指上游分支的所有节点逆序推入栈中 - var upstreamNodes = currentNode.SuccessorNodes[ConnectionInvokeType.Upstream]; - for (int index = upstreamNodes.Count - 1; index >= 0; index--) - { - // 筛选出启用的节点的节点 - if (upstreamNodes[index].DebugSetting.IsEnable) - { - context.SetPreviousNode(upstreamNodes[index], currentNode); - stack.Push(upstreamNodes[index]); - } - } - #endregion - - } - } - /// /// 执行节点对应的方法 /// /// 流程上下文 /// 节点传回数据对象 - public virtual async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + public virtual async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { - - #region 调试中断 // 执行触发检查是否需要中断 - if (DebugSetting.IsInterrupt) + if (DebugSetting.IsInterrupt) { context.Env.TriggerInterrupt(Guid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor); // 通知运行环境该节点中断了 await DebugSetting.GetInterruptTask.Invoke(); - //await fit.WaitTriggerAsync(Guid); // 创建一个等待的中断任务 SereinEnv.WriteLine(InfoType.INFO, $"[{this.MethodDetails?.MethodName}]中断已取消,开始执行后继分支"); - //var flowCts = context.Env.IOC.Get(NodeStaticConfig.FlipFlopCtsName); if (token.IsCancellationRequested) { return null; } } - #endregion - MethodDetails md = MethodDetails; if (md is null) { @@ -377,211 +118,25 @@ namespace Serein.Library } if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行到某个节点 { + throw new Exception($"节点{this.Guid}不存在对应委托"); } + var instance = Env.IOC.Get(md.ActingInstanceType); - if(instance == null) + if (instance is null) { Env.IOC.Register(md.ActingInstanceType).Build(); instance = Env.IOC.Get(md.ActingInstanceType); } - object[] args = await GetParametersAsync(context, token); + object[] args = await this.GetParametersAsync(context, token); var result = await dd.InvokeAsync(instance, args); - return result; + var flowReslt = new FlowResult(this, context, result); + return flowReslt; } - /// - /// 获取对应的参数数组 - /// - public async Task GetParametersAsync(IDynamicContext context, CancellationToken token) - { - if (MethodDetails.ParameterDetailss.Length == 0) - { - return new object[0]; // 无参数 - } - - #region 定义返回的参数数组 - object[] args; - Array paramsArgs = null; // 初始化可选参数 - int paramsArgIndex = 0; // 可选参数下标,与 object[] paramsArgs 一起使用 - if (MethodDetails.ParamsArgIndex >= 0) // 存在可变入参参数 - { - var paramsArgType = MethodDetails.ParameterDetailss[MethodDetails.ParamsArgIndex].DataType; // 获取可变参数的参数类型 - int paramsLength = MethodDetails.ParameterDetailss.Length - MethodDetails.ParamsArgIndex; // 可变参数数组长度 = 方法参数个数 - ( 可选入参下标 + 1 ) - paramsArgs = Array.CreateInstance(paramsArgType, paramsLength);// 可变参数 - args = new object[MethodDetails.ParamsArgIndex + 1]; // 调用方法的入参数组 - args[MethodDetails.ParamsArgIndex] = paramsArgs; // 如果存在可选参数,入参参数最后一项则为可变参数 - } - else - { - // 不存在可选参数 - args = new object[MethodDetails.ParameterDetailss.Length]; // 调用方法的入参数组 - } - #endregion - - // 常规参数的获取 - for (int i = 0; i < args.Length; i++) { - var pd = MethodDetails.ParameterDetailss[i]; - - args[i] = await pd.ToMethodArgData(context); // 获取数据 - } - - // 可选参数的获取 - if(MethodDetails.ParamsArgIndex >= 0) - { - for (int i = 0; i < paramsArgs.Length; i++) - { - var pd = MethodDetails.ParameterDetailss[paramsArgIndex + i]; - var data = await pd.ToMethodArgData(context); // 获取数据 - paramsArgs.SetValue(data, i);// 设置到数组中 - } - args[args.Length - 1] = paramsArgs; - } - - return args; - } - - - - /// - /// 检查监视表达式是否生效 - /// - /// 上下文 - /// 节点Moel - /// 新的数据 - /// - public static async Task CheckExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object newData = null) - { - string guid = nodeModel.Guid; - context.AddOrUpdate(guid, newData); // 上下文中更新数据 - if (newData is null) - { - } - else - { - await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象 - await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点 - //nodeModel.FlowData = newData; // 替换数据 - } - } - - private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object data, int monitorType) - { - MonitorObjectEventArgs.ObjSourceType sourceType; - string key; - if (monitorType == 0) - { - key = data?.GetType()?.FullName; - sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; - } - else - { - key = nodeModel.Guid; - sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; - } - if (string.IsNullOrEmpty(key)) - { - return; - } - //(var isMonitor, var exps) = await context.Env.CheckObjMonitorStateAsync(key); - //if (isMonitor) // 如果新的数据处于查看状态,通知UI进行更新?交给运行环境判断? - //{ - // context.Env.MonitorObjectNotification(nodeModel.Guid, data, sourceType); // 对象处于监视状态,通知UI更新数据显示 - // if (exps.Length > 0) - // { - // // 表达式环境下判断是否需要执行中断 - // bool isExpInterrupt = false; - // string exp = ""; - // // 判断执行监视表达式,直到为 true 时退出 - // for (int i = 0; i < exps.Length && !isExpInterrupt; i++) - // { - // exp = exps[i]; - // if (string.IsNullOrEmpty(exp)) continue; - // // isExpInterrupt = SereinConditionParser.To(data, exp); - // } - - // if (isExpInterrupt) // 触发中断 - // { - // nodeModel.DebugSetting.IsInterrupt = true; - // if (await context.Env.SetNodeInterruptAsync(nodeModel.Guid,true)) - // { - // context.Env.TriggerInterrupt(nodeModel.Guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp); - // var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); - // await Console.Out.WriteLineAsync($"[{data}]中断已{cancelType},开始执行后继分支"); - // nodeModel.DebugSetting.IsInterrupt = false; - // } - // } - // } - - //} - } - - #endregion } -#if false - public static class NodeModelExtension - { - /// - /// 程序集更新,更新节点方法描述、以及所有入参描述的类型 - /// - /// 节点Model - /// 新的方法描述 - public static void UploadMethod(this NodeModelBase nodeModel, MethodDetails newMd) - { - var thisMd = nodeModel.MethodDetails; - - thisMd.ActingInstanceType = newMd.ActingInstanceType; // 更新方法需要的类型 - - var thisPds = thisMd.ParameterDetailss; - var newPds = newMd.ParameterDetailss; - // 当前存在可变参数,且新的方法也存在可变参数,需要把可变参数的数目与值传递过去 - if (thisMd.HasParamsArg && newMd.HasParamsArg) - { - int paramsLength = thisPds.Length - thisMd.ParamsArgIndex - 1; // 确定扩容长度 - newMd.ParameterDetailss = ArrayHelper.Expansion(newPds, paramsLength);// 为新方法的入参参数描述进行扩容 - newPds = newMd.ParameterDetailss; - int index = newMd.ParamsArgIndex; // 记录 - var templatePd = newPds[newMd.ParamsArgIndex]; // 新的入参模板 - for (int i = thisMd.ParamsArgIndex; i < thisPds.Length; i++) - { - ParameterDetails thisPd = thisPds[i]; - var newPd = templatePd.CloneOfModel(nodeModel); // 复制参数描述 - newPd.Index = i + 1; // 更新索引 - newPd.IsParams = true; - newPd.DataValue = thisPd.DataValue; // 保留参数值 - newPd.ArgDataSourceNodeGuid = thisPd.ArgDataSourceNodeGuid; // 保留参数来源信息 - newPd.ArgDataSourceType = thisPd.ArgDataSourceType; // 保留参数来源信息 - newPd.IsParams = thisPd.IsParams; // 保留显式参数设置 - newPds[index++] = newPd; - } - } - - - var thidPdLength = thisMd.HasParamsArg ? thisMd.ParamsArgIndex : thisPds.Length; - // 遍历当前的参数描述(不包含可变参数),找到匹配项,复制必要的数据进行保留 - for (int i = 0; i < thisPds.Length; i++) - { - ParameterDetails thisPd = thisPds[i]; - var newPd = newPds.FirstOrDefault(t_newPd => !t_newPd.IsParams // 不为可变参数 - && t_newPd.Name.Equals(thisPd.Name, StringComparison.OrdinalIgnoreCase) // 存在相同名称 - && t_newPd.DataType.Name.Equals(thisPd.DataType.Name) // 存在相同入参类型名称(以类型作为区分) - ); - if (newPd != null) // 如果匹配上了 - { - newPd.DataValue = thisPd.DataValue; // 保留参数值 - newPd.ArgDataSourceNodeGuid = thisPd.ArgDataSourceNodeGuid; // 保留参数来源信息 - newPd.ArgDataSourceType = thisPd.ArgDataSourceType; // 保留参数来源信息 - newPd.IsParams = thisPd.IsParams; // 保留显式参数设置 - } - } - thisMd.ReturnType = newMd.ReturnType; - nodeModel.MethodDetails = newMd; - - } - } -#endif } diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index 85eb887..6f9b7d9 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading; using System.Threading.Tasks; namespace Serein.Library @@ -237,6 +238,7 @@ namespace Serein.Library // 需要获取预入参数据 object inputParameter; #region (默认的)从运行时上游节点获取其返回值 + if (ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData) { var previousNode = context.GetPreviousNode(nodeModel); @@ -246,40 +248,32 @@ namespace Serein.Library } else { - inputParameter = context.GetFlowData(previousNode.Guid); // 当前传递的数据 + inputParameter = context.GetFlowData(previousNode).Value; // 当前传递的数据 } } - #endregion - #region 从指定节点获取其返回值 - else if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) - { - // 获取指定节点的数据 - // 如果指定节点没有被执行,会返回null - // 如果执行过,会获取上一次执行结果作为预入参数据 - inputParameter = context.GetFlowData(ArgDataSourceNodeGuid); - } - #endregion - #region 立刻执行指定节点,然后获取返回值 - else if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) - { - // 立刻调用对应节点获取数据。 - try - { - var result = await env.InvokeNodeAsync(context, ArgDataSourceNodeGuid); - inputParameter = result; - } - catch (Exception ex) - { - context.NextOrientation = ConnectionInvokeType.IsError; - context.ExceptionOfRuning = ex; - throw; - } - } - #endregion - #region 意料之外的参数 else { - throw new Exception("节点执行方法获取入参参数时,ConnectionArgSourceType枚举是意外的枚举值"); + if(!env.TryGetNodeModel(ArgDataSourceNodeGuid, out var argSourceNodeModel)) + { + throw new Exception($"[arg{Index}][{Name}][{DataType}]需要节点[{ArgDataSourceNodeGuid}]的参数,但节点不存在"); + } + if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) + { + inputParameter = context.GetFlowData(argSourceNodeModel).Value; + } + else if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) + { + // 立刻调用对应节点获取数据。 + var cts = new CancellationTokenSource(); + var result = await argSourceNodeModel.ExecutingAsync(context, cts.Token); + cts?.Cancel(); + cts?.Dispose(); + inputParameter = result.Value; + } + else + { + throw new Exception("节点执行方法获取入参参数时,ConnectionArgSourceType枚举是意外的枚举值"); + } } #endregion #region 判断是否执行表达式 diff --git a/Library/Utils/ConvertHelper.cs b/Library/Utils/ConvertHelper.cs index 8dca4b8..4fc56f2 100644 --- a/Library/Utils/ConvertHelper.cs +++ b/Library/Utils/ConvertHelper.cs @@ -14,6 +14,109 @@ namespace Serein.Library.Utils /// public static class ConvertHelper { + /// + /// 字面量转为对应类型 + /// + /// + /// + public static Type ToTypeOfString(this string valueStr) + { + if (valueStr.IndexOf('.') != -1) + { + // 通过指定的类型名称获取类型 + return Type.GetType(valueStr); + } + + + if (valueStr.Equals("bool", StringComparison.OrdinalIgnoreCase)) + { + return typeof(bool); + } + #region 整数型 + else if (valueStr.Equals("sbyte", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(SByte), StringComparison.OrdinalIgnoreCase)) + { + return typeof(SByte); + } + else if (valueStr.Equals("short", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Int16), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Int16); + } + else if (valueStr.Equals("int", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Int32), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Int32); + } + else if (valueStr.Equals("long", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Int64), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Int64); + } + + else if (valueStr.Equals("byte", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Byte), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Byte); + } + else if (valueStr.Equals("ushort", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(UInt16), StringComparison.OrdinalIgnoreCase)) + { + return typeof(UInt16); + } + else if (valueStr.Equals("uint", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(UInt32), StringComparison.OrdinalIgnoreCase)) + { + return typeof(UInt32); + } + else if (valueStr.Equals("ulong", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(UInt64), StringComparison.OrdinalIgnoreCase)) + { + return typeof(UInt64); + } + #endregion + + #region 浮点型 + else if (valueStr.Equals("float", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Single), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Single); + } + else if (valueStr.Equals("double", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Double), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Double); + } + #endregion + + #region 小数型 + + else if (valueStr.Equals("decimal", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Decimal), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Decimal); + } + #endregion + + #region 其他常见的类型 + else if (valueStr.Equals(nameof(DateTime), StringComparison.OrdinalIgnoreCase)) + { + return typeof(DateTime); + } + + else if (valueStr.Equals(nameof(String), StringComparison.OrdinalIgnoreCase)) + { + return typeof(String); + } + #endregion + + else + { + throw new ArgumentException($"无法解析的字面量类型[{valueStr}]"); + } + } + + /// /// 对象转JSON文本 diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 4c0787e..6a7010f 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -392,7 +392,7 @@ namespace Serein.NodeFlow.Env IOC.Reset(); IOC.Register(); // 注册脚本接口 - var flowTaskOptions = new FlowTaskLibrary + var flowTaskOptions = new FlowWorkLibrary { Environment = this, @@ -440,8 +440,8 @@ namespace Serein.NodeFlow.Env } if (true || FlowState == RunState.Running || FlipFlopState == RunState.Running) { - NodeModelBase? nodeModel = GuidToModel(startNodeGuid); - if (nodeModel is null || nodeModel is SingleFlipflopNode) + + if (!TryGetNodeModel(startNodeGuid,out var nodeModel) || nodeModel is SingleFlipflopNode) { return false; } @@ -460,14 +460,14 @@ namespace Serein.NodeFlow.Env } } - /// + /*/// /// 单独运行一个节点 /// /// /// public async Task InvokeNodeAsync(IDynamicContext context, string nodeGuid) { - object result = new Unit(); + object result = Unit.Default; if (this.NodeModels.TryGetValue(nodeGuid, out var model)) { CancellationTokenSource cts = new CancellationTokenSource(); @@ -475,7 +475,7 @@ namespace Serein.NodeFlow.Env cts?.Cancel(); } return result; - } + }*/ /// /// 结束流程 @@ -496,7 +496,10 @@ namespace Serein.NodeFlow.Env /// public void ActivateFlipflopNode(string nodeGuid) { - var nodeModel = GuidToModel(nodeGuid); + if(!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return; + } if (nodeModel is null) return; if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 { @@ -515,7 +518,10 @@ namespace Serein.NodeFlow.Env /// public void TerminateFlipflopNode(string nodeGuid) { - var nodeModel = GuidToModel(nodeGuid); + if(!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return; + } if (nodeModel is null) return; if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 { @@ -858,7 +864,10 @@ namespace Serein.NodeFlow.Env #region 确定节点之间的方法调用关系 foreach (var nodeInfo in nodeInfos) { - var fromNodeModel = GuidToModel(nodeInfo.Guid); + if (!TryGetNodeModel(nodeInfo.Guid, out var fromNodeModel)) + { + return; + } if (fromNodeModel is null) continue; List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), @@ -869,7 +878,10 @@ namespace Serein.NodeFlow.Env // 遍历当前类型分支的节点(确认连接关系) foreach (var toNodeGuid in item.toNodeGuids) { - var toNodeModel = GuidToModel(toNodeGuid); + if (!TryGetNodeModel(toNodeGuid, out var toNodeModel)) + { + return; + } if (toNodeModel is null) { // 防御性代码,加载正常保存的项目文件不会进入这里 continue; @@ -915,7 +927,7 @@ namespace Serein.NodeFlow.Env && NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode)) { - _ = ConnectArgSourceOfNodeAsync(fromNode, toNode, pd.ArgDataSourceType, pd.Index); + await ConnectArgSourceOfNodeAsync(fromNode, toNode, pd.ArgDataSourceType, pd.Index); } } } @@ -981,20 +993,24 @@ namespace Serein.NodeFlow.Env /// 将节点放置在容器中 /// /// - public async Task PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid) + public Task PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid) { // 获取目标节点与容器节点 - var nodeModel = GuidToModel(nodeGuid); - if (nodeModel is null ) return false; - - if(nodeModel.ContainerNode is INodeContainer tmpContainer) + if (!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return Task.FromResult(false); + } + if (nodeModel.ContainerNode is INodeContainer tmpContainer) { SereinEnv.WriteLine(InfoType.WARN, $"节点放置失败,节点[{nodeGuid}]已经放置于容器节点[{((NodeModelBase)tmpContainer).Guid}]"); - return false; + return Task.FromResult(false); } - var containerNode = GuidToModel(containerNodeGuid); // 获取容器节点 - if (containerNode is not INodeContainer nodeContainer) return false; + if (!TryGetNodeModel(containerNodeGuid, out var containerNode)) + { + return Task.FromResult(false); + } + if (containerNode is not INodeContainer nodeContainer) return Task.FromResult(false); var result = nodeContainer.PlaceNode(nodeModel); // 放置在容器节点 if (result) @@ -1004,7 +1020,7 @@ namespace Serein.NodeFlow.Env OnNodePlace?.Invoke(new NodePlaceEventArgs(nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置 }); } - return result; + return Task.FromResult(result); } @@ -1012,15 +1028,16 @@ namespace Serein.NodeFlow.Env /// 将节点从容器节点中脱离 /// /// - public async Task TakeOutNodeToContainerAsync(string nodeGuid) + public Task TakeOutNodeToContainerAsync(string nodeGuid) { // 获取目标节点与容器节点 - var nodeModel = GuidToModel(nodeGuid); - if (nodeModel is null) return false; - - if(nodeModel.ContainerNode is not INodeContainer nodeContainer) + if (!TryGetNodeModel(nodeGuid, out var nodeModel)) { - return false; + return Task.FromResult(false); + } + if (nodeModel.ContainerNode is not INodeContainer nodeContainer) + { + return Task.FromResult(false); } var result = nodeContainer.TakeOutNode(nodeModel); // 从容器节点取出 if (result) @@ -1030,7 +1047,7 @@ namespace Serein.NodeFlow.Env OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(nodeGuid)); // 重新放置在画布上 }); } - return result; + return Task.FromResult(result); } @@ -1044,9 +1061,10 @@ namespace Serein.NodeFlow.Env /// public async Task RemoveNodeAsync(string nodeGuid) { - var remoteNode = GuidToModel(nodeGuid); - if (remoteNode is null) - return false; + if (!TryGetNodeModel(nodeGuid, out var remoteNode)) + { + return false; + } if (remoteNode is SingleFlipflopNode flipflopNode) { @@ -1103,7 +1121,7 @@ namespace Serein.NodeFlow.Env /// 起始节点控制点 /// 目标节点控制点 /// 连接关系 - public async Task ConnectInvokeNodeAsync(string fromNodeGuid, + public Task ConnectInvokeNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, @@ -1111,17 +1129,19 @@ namespace Serein.NodeFlow.Env { // 获取起始节点与目标节点 - var fromNode = GuidToModel(fromNodeGuid); - var toNode = GuidToModel(toNodeGuid); - if (fromNode is null || toNode is null) return false; + if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return Task.FromResult(false); + } + + if (fromNode is null || toNode is null) return Task.FromResult(false); (var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType); if (!state) { SereinEnv.WriteLine(InfoType.WARN, "出现非预期的连接行为"); - return false; // 出现不符预期的连接行为,忽略此次连接行为 + return Task.FromResult(false); // 出现不符预期的连接行为,忽略此次连接行为 } - if(type == JunctionOfConnectionType.Invoke) { if (fromNodeJunctionType == JunctionType.Execute) @@ -1132,7 +1152,7 @@ namespace Serein.NodeFlow.Env // 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点 state = ConnectInvokeOfNode(fromNode, toNode, invokeType); // 本地环境进行连接 } - return state; + return Task.FromResult(state); } @@ -1147,8 +1167,10 @@ namespace Serein.NodeFlow.Env public Task SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) { // 获取起始节点与目标节点 - var fromNode = GuidToModel(fromNodeGuid); - var toNode = GuidToModel(toNodeGuid); + if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return Task.FromResult(false); + } if (fromNode is null || toNode is null) return Task.FromResult(false); if ( fromNode.SuccessorNodes.TryGetValue(connectionType, out var nodes)) { @@ -1173,8 +1195,10 @@ namespace Serein.NodeFlow.Env public async Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) { // 获取起始节点与目标节点 - var fromNode = GuidToModel(fromNodeGuid); - var toNode = GuidToModel(toNodeGuid); + if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return false; + } if (fromNode is null || toNode is null) return false; var result = await RemoteConnectAsync(fromNode, toNode, connectionType); @@ -1200,8 +1224,10 @@ namespace Serein.NodeFlow.Env { // 获取起始节点与目标节点 - var fromNode = GuidToModel(fromNodeGuid); - var toNode = GuidToModel(toNodeGuid); + if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return false; + } if (fromNode is null || toNode is null) return false; (var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType); if (!state) @@ -1236,8 +1262,10 @@ namespace Serein.NodeFlow.Env public async Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex) { // 获取起始节点与目标节点 - var fromNode = GuidToModel(fromNodeGuid); - var toNode = GuidToModel(toNodeGuid); + if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return false; + } if (fromNode is null || toNode is null) return false; var result = await RemoteConnectAsync(fromNode, toNode, argIndex); return result; @@ -1302,10 +1330,11 @@ namespace Serein.NodeFlow.Env /// public void MoveNode(string nodeGuid, double x, double y) { - NodeModelBase? nodeModel = GuidToModel(nodeGuid); - if (nodeModel is null) return; + if (!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return; + } nodeModel.Position.X = x; - nodeModel.Position.Y = y; UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y))); @@ -1317,9 +1346,10 @@ namespace Serein.NodeFlow.Env /// public Task SetStartNodeAsync(string newNodeGuid) { - var newStartNodeModel = GuidToModel(newNodeGuid); - if (newStartNodeModel is null) - return Task.FromResult(StartNode?.Guid ?? string.Empty); + if (!TryGetNodeModel(newNodeGuid, out var newStartNodeModel)) + { + return Task.FromResult(StartNode?.Guid ?? string.Empty); + } SetStartNode(newStartNodeModel); return Task.FromResult(StartNode?.Guid ?? string.Empty); } @@ -1370,12 +1400,13 @@ namespace Serein.NodeFlow.Env /// 属性路径 /// 变化后的属性值 /// - public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) + public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) { - var nodeModel = GuidToModel(nodeGuid); - if (nodeModel is null) return; - SerinExpressionEvaluator.Evaluate($"@Set .{path} = {value}", nodeModel, out _); // 更改对应的数据 - + if (TryGetNodeModel(nodeGuid, out var nodeModel)) + { + SerinExpressionEvaluator.Evaluate($"@Set .{path} = {value}", nodeModel, out _); // 更改对应的数据 + } + return Task.CompletedTask; //if (NodeValueChangeLogger.Remove((nodeGuid, path, value))) //{ // // 说明存在过重复的修改 @@ -1414,7 +1445,10 @@ namespace Serein.NodeFlow.Env /// public Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex) { - var nodeModel = GuidToModel(nodeGuid); + if (!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return Task.FromResult(false); + } if (nodeModel is null) return Task.FromResult(false); bool isPass; if (isAdd) @@ -1429,30 +1463,26 @@ namespace Serein.NodeFlow.Env } + /// /// Guid 转 NodeModel /// /// 节点Guid /// 节点Model /// 无法获取节点、Guid/节点为null时报错 - private NodeModelBase? GuidToModel(string nodeGuid) + public bool TryGetNodeModel(string nodeGuid,out NodeModelBase nodeModel) { if (string.IsNullOrEmpty(nodeGuid)) { - //throw new ArgumentNullException("not contains - Guid没有对应节点:" + (nodeGuid)); - return null; + nodeModel = null; + return false; } - if (!NodeModels.TryGetValue(nodeGuid, out NodeModelBase? nodeModel) || nodeModel is null) - { - //throw new ArgumentNullException("null - Guid存在对应节点,但节点为null:" + (nodeGuid)); - return null; - } - return nodeModel; + return NodeModels.TryGetValue(nodeGuid, out nodeModel) && nodeModel is not null; + } #endregion - #region 流程依赖类库的接口 diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs index 8cc8b48..fdc2aa0 100644 --- a/NodeFlow/Env/FlowEnvironmentDecorator.cs +++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs @@ -492,10 +492,7 @@ namespace Serein.NodeFlow.Env return await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid); } - public async Task InvokeNodeAsync(IDynamicContext context, string nodeGuid) - { - return await currentFlowEnvironment.InvokeNodeAsync(context, nodeGuid); - } + public async Task StartRemoteServerAsync(int port = 7525) { @@ -524,6 +521,11 @@ namespace Serein.NodeFlow.Env { currentFlowEnvironment.SetUIContextOperation(uiContextOperation); } + + public bool TryGetNodeModel(string nodeGuid, out NodeModelBase nodeModel) + { + return currentFlowEnvironment.TryGetNodeModel(nodeGuid, out nodeModel); + } public bool TryGetDelegateDetails(string libraryName, string methodName, out DelegateDetails del) { return currentFlowEnvironment.TryGetDelegateDetails(libraryName, methodName, out del); diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs index e0d63b9..bddb9c6 100644 --- a/NodeFlow/Env/RemoteFlowEnvironment.cs +++ b/NodeFlow/Env/RemoteFlowEnvironment.cs @@ -1217,14 +1217,13 @@ namespace Serein.NodeFlow.Env this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:SetMonitorObjState"); } - public async Task InvokeNodeAsync(IDynamicContext context, string nodeGuid) - { - // 登录到远程环境后,启动器相关方法无效 - this.WriteLine(InfoType.INFO, "远程环境尚未实现接口 InvokeNodeAsync"); - return null; - } - + public bool TryGetNodeModel(string nodeGuid, out NodeModelBase nodeModel) + { + this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:TryGetNodeModel"); + nodeModel = null; + return false; + } public bool TryGetMethodDetailsInfo(string libraryName, string methodName, out MethodDetailsInfo mdInfo) { diff --git a/NodeFlow/FlowTaskLibrary.cs b/NodeFlow/FlowWorkLibrary.cs similarity index 93% rename from NodeFlow/FlowTaskLibrary.cs rename to NodeFlow/FlowWorkLibrary.cs index f6eb5c1..2e0ec1f 100644 --- a/NodeFlow/FlowTaskLibrary.cs +++ b/NodeFlow/FlowWorkLibrary.cs @@ -9,8 +9,10 @@ using System.Threading.Tasks; namespace Serein.NodeFlow { - - public class FlowTaskLibrary() + /// + /// 节点任务执行依赖 + /// + public class FlowWorkLibrary() { /// /// 流程运行环境 diff --git a/NodeFlow/FlowWorkManagement.cs b/NodeFlow/FlowWorkManagement.cs index 2953505..3d697ed 100644 --- a/NodeFlow/FlowWorkManagement.cs +++ b/NodeFlow/FlowWorkManagement.cs @@ -30,13 +30,13 @@ namespace Serein.NodeFlow /// /// 初始化选项 /// - public FlowTaskLibrary WorkLibrary { get; } + public FlowWorkLibrary WorkLibrary { get; } /// /// 流程任务管理 /// /// - public FlowWorkManagement(FlowTaskLibrary library) + public FlowWorkManagement(FlowWorkLibrary library) { WorkLibrary = library; @@ -301,7 +301,7 @@ namespace Serein.NodeFlow { var context = pool.Allocate(); // 启动全局触发器时新建上下文 var newFlowData = await singleFlipFlopNode.ExecutingAsync(context, singleToken); // 获取触发器等待Task - context.AddOrUpdate(singleFlipFlopNode.Guid, newFlowData); + context.AddOrUpdate(singleFlipFlopNode, newFlowData); if (context.NextOrientation == ConnectionInvokeType.None) { continue; @@ -326,6 +326,14 @@ namespace Serein.NodeFlow } + /// + /// 全局触发器触发后的调用 + /// + /// + /// + /// + /// + /// private static async Task? CallSubsequentNode(SingleFlipflopNode singleFlipFlopNode, CancellationToken singleToken, ObjectPool pool, IDynamicContext context) { var flowState = context.NextOrientation; // 记录一下流程状态 diff --git a/NodeFlow/Model/CompositeConditionNode.cs b/NodeFlow/Model/CompositeConditionNode.cs index 23212a2..520dd08 100644 --- a/NodeFlow/Model/CompositeConditionNode.cs +++ b/NodeFlow/Model/CompositeConditionNode.cs @@ -36,7 +36,7 @@ namespace Serein.NodeFlow.Model /// /// /// - public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { try { @@ -45,7 +45,7 @@ namespace Serein.NodeFlow.Model { if (token.IsCancellationRequested) { - return null; + return new FlowResult(this, context); } var state = await node.ExecutingAsync(context, token); if (context.NextOrientation != ConnectionInvokeType.IsSucceed) @@ -56,7 +56,8 @@ namespace Serein.NodeFlow.Model } //var previousNode = context.GetPreviousNode() - return context.TransmissionData(this); // 条件区域透传上一节点的数据 + var result = context.TransmissionData(this); // 条件区域透传上一节点的数据 + return new FlowResult(this,context, result); } catch (Exception ex) { diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs index 64f4dbd..ac1b76e 100644 --- a/NodeFlow/Model/SingleConditionNode.cs +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -110,9 +110,12 @@ namespace Serein.NodeFlow.Model /// /// /// - public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { - if (token.IsCancellationRequested) return null; + if (token.IsCancellationRequested) + { + return new FlowResult(this, context); + } // 接收上一节点参数or自定义参数内容 object? parameter; object? result = null; @@ -121,17 +124,31 @@ namespace Serein.NodeFlow.Model { // 使用自动取参 var pd = MethodDetails.ParameterDetailss[INDEX_EXPRESSION]; + var hasNode = context.Env.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var argSourceNode); + if (hasNode) + { + context.NextOrientation = ConnectionInvokeType.IsError; + return new FlowResult(this, context); + } + if (hasNode) + { + return new FlowResult(this, context); + } if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) { - result = context.GetFlowData(pd.ArgDataSourceNodeGuid); // 使用自定义节点的参数 + result = context.GetFlowData(argSourceNode).Value; // 使用自定义节点的参数 } else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) { - result = await Env.InvokeNodeAsync(context, pd.ArgDataSourceNodeGuid); // 立刻调用目标节点,然后使用其返回值 + CancellationTokenSource cts = new CancellationTokenSource(); + var nodeResult = await argSourceNode.ExecutingAsync(context, cts.Token); + result = nodeResult.Value; + cts?.Cancel(); + cts?.Dispose(); } else { - result = context.TransmissionData(this); // 条件节点透传上一节点的数据 + result = context.TransmissionData(this).Value; // 条件节点透传上一节点的数据 } parameter = result; // 使用上一节点的参数 @@ -167,7 +184,7 @@ namespace Serein.NodeFlow.Model SereinEnv.WriteLine(InfoType.INFO, $"{result} {Expression} -> " + context.NextOrientation); //return result; - return judgmentResult; + return new FlowResult(this, context, judgmentResult); } diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/SingleExpOpNode.cs index 8935cbf..9b07721 100644 --- a/NodeFlow/Model/SingleExpOpNode.cs +++ b/NodeFlow/Model/SingleExpOpNode.cs @@ -92,22 +92,32 @@ namespace Serein.NodeFlow.Model } - public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { - if(token.IsCancellationRequested) return null; + if(token.IsCancellationRequested) return new FlowResult(this, context); object? parameter = null;// context.TransmissionData(this); // 表达式节点使用上一节点数据 var pd = MethodDetails.ParameterDetailss[0]; + var hasNode = context.Env.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var argSourceNode); + if (hasNode) + { + context.NextOrientation = ConnectionInvokeType.IsError; + return new FlowResult(this, context); + } if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) { // 使用自定义节点的参数 - parameter = context.GetFlowData(pd.ArgDataSourceNodeGuid); + parameter = context.GetFlowData(argSourceNode).Value; } else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) { // 立刻调用目标节点,然后使用其返回值 - parameter = await Env.InvokeNodeAsync(context, pd.ArgDataSourceNodeGuid); + var cts = new CancellationTokenSource(); + var result = await argSourceNode.ExecutingAsync(context, cts.Token); + cts?.Cancel(); + cts?.Dispose(); + parameter = result.Value; } else { @@ -129,13 +139,13 @@ namespace Serein.NodeFlow.Model } context.NextOrientation = ConnectionInvokeType.IsSucceed; - return result; + return new FlowResult(this,context, result); } catch (Exception ex) { context.NextOrientation = ConnectionInvokeType.IsError; context.ExceptionOfRuning = ex; - return parameter; + return new FlowResult(this, context); } } diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs index e68d5aa..f20f15c 100644 --- a/NodeFlow/Model/SingleFlipflopNode.cs +++ b/NodeFlow/Model/SingleFlipflopNode.cs @@ -22,7 +22,7 @@ namespace Serein.NodeFlow.Model /// /// /// - public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { #region 执行前中断 if (DebugSetting.IsInterrupt) // 执行触发前 @@ -40,8 +40,13 @@ namespace Serein.NodeFlow.Model } var instance = context.Env.IOC.Get(md.ActingInstanceType); + if (instance is null) + { + Env.IOC.Register(md.ActingInstanceType).Build(); + instance = Env.IOC.Get(md.ActingInstanceType); + } await dd.InvokeAsync(instance, [context]); - var args = await GetParametersAsync(context, token); + var args = await this.GetParametersAsync(context, token); // 因为这里会返回不确定的泛型 IFlipflopContext // 而我们只需要获取到 State 和 Value(返回的数据) // 所以使用 dynamic 类型接收 @@ -58,7 +63,9 @@ namespace Serein.NodeFlow.Model { throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid); } - return dynamicFlipflopContext.Value; + object result = dynamicFlipflopContext.Value; + var flowReslt = new FlowResult(this, context, result); + return flowReslt; } } diff --git a/NodeFlow/Model/SingleGlobalDataNode.cs b/NodeFlow/Model/SingleGlobalDataNode.cs index 2e3f063..aff7795 100644 --- a/NodeFlow/Model/SingleGlobalDataNode.cs +++ b/NodeFlow/Model/SingleGlobalDataNode.cs @@ -115,33 +115,34 @@ namespace Serein.NodeFlow.Model /// /// /// - public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { - if (token.IsCancellationRequested) return null; + if (token.IsCancellationRequested) return new FlowResult(this, context); if (string.IsNullOrEmpty(KeyName)) { context.NextOrientation = ConnectionInvokeType.IsError; SereinEnv.WriteLine(InfoType.ERROR, $"全局数据的KeyName不能为空[{this.Guid}]"); - return null; + return new FlowResult(this, context); } if (DataNode is null) { context.NextOrientation = ConnectionInvokeType.IsError; SereinEnv.WriteLine(InfoType.ERROR, $"全局数据节点没有设置数据来源[{this.Guid}]"); - return null; + return new FlowResult(this, context); } try { - var result = await context.Env.InvokeNodeAsync(context, DataNode.Guid); - SereinEnv.AddOrUpdateFlowGlobalData(KeyName, result); + + var result = await DataNode.ExecutingAsync(context, token); + SereinEnv.AddOrUpdateFlowGlobalData(KeyName, result.Value); return result; } catch (Exception ex) { context.NextOrientation = ConnectionInvokeType.IsError; context.ExceptionOfRuning = ex; - return null; + return new FlowResult(this, context); } } diff --git a/NodeFlow/Model/SingleScriptNode.cs b/NodeFlow/Model/SingleScriptNode.cs index a0f6129..e9f6f16 100644 --- a/NodeFlow/Model/SingleScriptNode.cs +++ b/NodeFlow/Model/SingleScriptNode.cs @@ -165,12 +165,11 @@ namespace Serein.NodeFlow.Model /// /// /// - public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { - if(token.IsCancellationRequested) return null; - var @params = await GetParametersAsync(context, token); - if(token.IsCancellationRequested) return null; - + if (token.IsCancellationRequested) return new FlowResult(this, context); + var @params = await this.GetParametersAsync(context, token); + if(token.IsCancellationRequested) return new FlowResult(this, context); //context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改 ReloadScript();// 每次都重新解析 @@ -199,9 +198,9 @@ namespace Serein.NodeFlow.Model if (token.IsCancellationRequested) return null; var result = await ScriptInterpreter.InterpretAsync(scriptContext, mainNode); // 从入口节点执行 - envEvent.OnFlowRunComplete -= onFlowStop; + envEvent.OnFlowRunComplete -= onFlowStop; + return new FlowResult(this, context, result); //SereinEnv.WriteLine(InfoType.INFO, "FlowContext Guid : " + context.Guid); - return result; } diff --git a/NodeFlow/Model/SingleUINode.cs b/NodeFlow/Model/SingleUINode.cs index bbf7955..42111b8 100644 --- a/NodeFlow/Model/SingleUINode.cs +++ b/NodeFlow/Model/SingleUINode.cs @@ -15,14 +15,14 @@ namespace Serein.NodeFlow.Model { } - public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { - if (token.IsCancellationRequested) return null; + if (token.IsCancellationRequested) return new FlowResult(this,context); if(Adapter is null) { var result = await base.ExecutingAsync(context, token); - if (result is IEmbeddedContent adapter) + if (result.Value is IEmbeddedContent adapter) { this.Adapter = adapter; context.NextOrientation = ConnectionInvokeType.IsSucceed; @@ -35,12 +35,12 @@ namespace Serein.NodeFlow.Model else { var p = context.GetPreviousNode(this); - var data = context.GetFlowData(p.Guid); + var data = context.GetFlowData(p).Value; var iflowContorl = Adapter.GetFlowControl(); iflowContorl.OnExecuting(data); } - - return null; + + return new FlowResult(this, context); } } } diff --git a/NodeFlow/ScriptFlowApi.cs b/NodeFlow/ScriptFlowApi.cs index c6bf14f..325a976 100644 --- a/NodeFlow/ScriptFlowApi.cs +++ b/NodeFlow/ScriptFlowApi.cs @@ -42,22 +42,21 @@ namespace Serein.NodeFlow throw new NotImplementedException(); } - public object? GetArgData(IDynamicContext context, int index) - { - var _paramsKey = $"{context?.Guid}_{NodeModel.Guid}_Params"; - var obj = context?.GetFlowData(_paramsKey); - if (obj is object[] @params && index < @params.Length) - { - return @params[index]; - } - return null; - } + //public object? GetArgData(IDynamicContext context, int index) + //{ + // var _paramsKey = $"{context?.Guid}_{NodeModel.Guid}_Params"; + // var obj = context?.GetFlowData(_paramsKey); + // if (obj is object[] @params && index < @params.Length) + // { + // return @params[index]; + // } + // return null; + //} - - public object? GetFlowData(IDynamicContext context) - { - return context?.GetFlowData(NodeModel.Guid); - } + //public object? GetFlowData(IDynamicContext context) + //{ + // return context?.GetFlowData(NodeModel.Guid); + //} public object? GetGlobalData(string keyName) { diff --git a/Serein.Script/SereinScriptParser.cs b/Serein.Script/SereinScriptParser.cs index 63505ee..6827674 100644 --- a/Serein.Script/SereinScriptParser.cs +++ b/Serein.Script/SereinScriptParser.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json.Linq; using Serein.Library; +using Serein.Library.Utils; using Serein.Script.Node; using System.Collections.Generic; using System.Linq.Expressions; diff --git a/Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs b/Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs index eac6477..cdd561d 100644 --- a/Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs +++ b/Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs @@ -136,7 +136,7 @@ public class NodeJunctionView : TemplatedControl nodeOperationService.ConnectingManage.Reset(); } - private void CheckJunvtion() + private async void CheckJunvtion() { var myData = nodeOperationService.ConnectingManage; if(myData.StartJunction is null || myData.CurrentJunction is null) @@ -157,7 +157,7 @@ public class NodeJunctionView : TemplatedControl #region ùϵ if (myData.Type == JunctionOfConnectionType.Invoke) { - flowEnvironment.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + await flowEnvironment.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, myData.StartJunction.JunctionType, myData.CurrentJunction.JunctionType, myData.ConnectionInvokeType); @@ -177,7 +177,7 @@ public class NodeJunctionView : TemplatedControl argIndex = myData.CurrentJunction.ArgIndex; } - flowEnvironment.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + await flowEnvironment.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, myData.StartJunction.JunctionType, myData.CurrentJunction.JunctionType, myData.ConnectionArgSourceType, diff --git a/Workbench/MainWindow.xaml.cs b/Workbench/MainWindow.xaml.cs index 12101fd..9c63b62 100644 --- a/Workbench/MainWindow.xaml.cs +++ b/Workbench/MainWindow.xaml.cs @@ -1944,7 +1944,7 @@ namespace Serein.Workbench /// /// /// - private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + private async void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (IsSelectControl) { @@ -1975,7 +1975,7 @@ namespace Serein.Workbench #region 方法调用关系创建 if (myData.Type == JunctionOfConnectionType.Invoke) { - this.EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + await EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, myData.StartJunction.JunctionType, myData.CurrentJunction.JunctionType, myData.ConnectionInvokeType); @@ -1995,7 +1995,7 @@ namespace Serein.Workbench argIndex = argJunction2.ArgIndex; } - this.EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + await EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, myData.StartJunction.JunctionType, myData.CurrentJunction.JunctionType, myData.ConnectionArgSourceType, diff --git a/Workbench/Node/View/UINodeControl.xaml.cs b/Workbench/Node/View/UINodeControl.xaml.cs index 5ba5aa5..4d7beac 100644 --- a/Workbench/Node/View/UINodeControl.xaml.cs +++ b/Workbench/Node/View/UINodeControl.xaml.cs @@ -54,7 +54,6 @@ namespace Serein.Workbench.Node.View EmbedContainer.Child = userControl; }); - } private void NodeControlBase_Initialized(object sender, EventArgs e) diff --git a/Workbench/Node/ViewModel/UINodeControlViewModel.cs b/Workbench/Node/ViewModel/UINodeControlViewModel.cs index 40e74cf..e7a9b3b 100644 --- a/Workbench/Node/ViewModel/UINodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/UINodeControlViewModel.cs @@ -26,7 +26,7 @@ namespace Serein.Workbench.Node.ViewModel { var context = new DynamicContext(NodeModel.Env); var cts = new CancellationTokenSource(); - await NodeModel.ExecutingAsync(context, cts.Token); + var result = await NodeModel.ExecutingAsync(context, cts.Token); cts?.Dispose(); if (context.NextOrientation == ConnectionInvokeType.IsSucceed && NodeModel.Adapter.GetUserControl() is UserControl userControl) diff --git a/Workbench/Views/FlowCanvasView.xaml.cs b/Workbench/Views/FlowCanvasView.xaml.cs index 73d8a09..bcfa112 100644 --- a/Workbench/Views/FlowCanvasView.xaml.cs +++ b/Workbench/Views/FlowCanvasView.xaml.cs @@ -309,7 +309,7 @@ namespace Serein.Workbench.Views /// /// /// - private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + private async void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (IsSelectControl) { @@ -340,7 +340,7 @@ namespace Serein.Workbench.Views #region 方法调用关系创建 if (myData.Type == JunctionOfConnectionType.Invoke) { - this.EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + await EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, myData.StartJunction.JunctionType, myData.CurrentJunction.JunctionType, myData.ConnectionInvokeType); @@ -360,7 +360,7 @@ namespace Serein.Workbench.Views argIndex = argJunction2.ArgIndex; } - this.EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + await EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, myData.StartJunction.JunctionType, myData.CurrentJunction.JunctionType, myData.ConnectionArgSourceType,