mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-05-01 21:01:28 +08:00
流程返回值改为FlowResult,记录节点信息、上下文信息,为以后的流程调用回溯做准备
This commit is contained in:
@@ -54,22 +54,21 @@ namespace Serein.Library.Api
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取节点的数据(当前节点需要获取上一节点数据时,需要从 运行时上一节点 的Guid 通过这个方法进行获取
|
/// 获取节点的数据(当前节点需要获取上一节点数据时,需要从 运行时上一节点 的Guid 通过这个方法进行获取
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeGuid"></param>
|
/// <param name="nodeModel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
object GetFlowData(string nodeGuid);
|
FlowResult GetFlowData(NodeModelBase nodeModel);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上一节点数据透传到下一节点
|
/// 上一节点数据透传到下一节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeModel"></param>
|
/// <param name="nodeModel"></param>
|
||||||
object TransmissionData(NodeModelBase nodeModel);
|
FlowResult TransmissionData(NodeModelBase nodeModel);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加或更新当前节点的数据
|
/// 添加或更新当前节点的数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeGuid"></param>
|
/// <param name="nodeModel"></param>
|
||||||
/// <param name="flowData"></param>
|
/// <param name="flowData"></param>
|
||||||
void AddOrUpdate(string nodeGuid, object flowData);
|
void AddOrUpdate(NodeModelBase nodeModel, FlowResult flowData);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 重置流程状态(用于对象池回收)
|
/// 重置流程状态(用于对象池回收)
|
||||||
|
|||||||
@@ -950,6 +950,13 @@ namespace Serein.Library.Api
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 流程运行相关
|
#region 流程运行相关
|
||||||
|
/// <summary>
|
||||||
|
/// 获取节点信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeGuid"></param>
|
||||||
|
/// <param name="nodeModel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool TryGetNodeModel(string nodeGuid, out NodeModelBase nodeModel);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取方法描述信息
|
/// 获取方法描述信息
|
||||||
@@ -1026,7 +1033,7 @@ namespace Serein.Library.Api
|
|||||||
/// <param name="context">调用时的上下文</param>
|
/// <param name="context">调用时的上下文</param>
|
||||||
/// <param name="nodeGuid">节点Guid</param>
|
/// <param name="nodeGuid">节点Guid</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid);
|
// Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ namespace Serein.Library.Api
|
|||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <param name="index"></param>
|
/// <param name="index"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
object GetArgData(IDynamicContext context, int index);
|
//object GetArgData(IDynamicContext context, int index);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取流程当前传递的数据
|
/// 获取流程当前传递的数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
object GetFlowData(IDynamicContext context);
|
/// FlowResult GetFlowData(IDynamicContext context);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取全局数据
|
/// 获取全局数据
|
||||||
|
|||||||
432
Library/Extension/NodeModelExtension.cs
Normal file
432
Library/Extension/NodeModelExtension.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 节点方法拓展
|
||||||
|
/// </summary>
|
||||||
|
public static class NodeModelExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 输出方法参数信息
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
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<ParameterData>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导出为节点信息
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从节点信息加载节点
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeInfo"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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<ConnectionArgSourceType>(pdInfo.SourceType);
|
||||||
|
pd.ArgDataSourceNodeGuid = pdInfo.SourceNodeGuid;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeModel.LoadCustomData(nodeInfo); // 加载自定义数据
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始执行
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="token">流程运行</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task StartFlowAsync(this NodeModelBase nodeModel, IDynamicContext context, CancellationToken token)
|
||||||
|
{
|
||||||
|
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
||||||
|
HashSet<NodeModelBase> processedNodes = new HashSet<NodeModelBase>(); // 用于记录已处理上游节点的节点
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取对应的参数数组
|
||||||
|
/// </summary>
|
||||||
|
public static async Task<object[]> GetParametersAsync(this NodeModelBase nodeModel, IDynamicContext context, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (nodeModel.MethodDetails.ParameterDetailss.Length == 0)
|
||||||
|
{
|
||||||
|
return Array.Empty<object>(); // 无参数
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查监视表达式是否生效
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeModel">节点Moel</param>
|
||||||
|
/// <param name="context">上下文</param>
|
||||||
|
/// <param name="newData">新的数据</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/*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; // 替换数据
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 监视对象表达式中断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeModel"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="monitorType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/*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;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 不再中断
|
||||||
|
/// </summary>
|
||||||
|
public static void CancelInterrupt(NodeModelBase nodeModel)
|
||||||
|
{
|
||||||
|
nodeModel.DebugSetting.IsInterrupt = false;
|
||||||
|
nodeModel.DebugSetting.CancelInterrupt?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
/// <summary>
|
||||||
|
/// 程序集更新,更新节点方法描述、以及所有入参描述的类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeModel">节点Model</param>
|
||||||
|
/// <param name="newMd">新的方法描述</param>
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,109 +13,6 @@ namespace Serein.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class SereinExtension
|
public static partial class SereinExtension
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 字面量转为对应类型
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="valueStr"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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}]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 判断连接类型
|
/// 判断连接类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -378,6 +378,11 @@ namespace Serein.Library
|
|||||||
sereinIOC.Run(action);
|
sereinIOC.Run(action);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryGetNodeModel(string nodeGuid, out NodeModelBase nodeModel)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace Serein.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 每个流程上下文分别存放节点的当前数据
|
/// 每个流程上下文分别存放节点的当前数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<string, object> dictNodeFlowData = new ConcurrentDictionary<string, object>();
|
private readonly ConcurrentDictionary<NodeModelBase, FlowResult> dictNodeFlowData = new ConcurrentDictionary<NodeModelBase, FlowResult>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 每个流程上下文存储运行时节点的调用关系
|
/// 每个流程上下文存储运行时节点的调用关系
|
||||||
@@ -88,7 +88,7 @@ namespace Serein.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeGuid">节点</param>
|
/// <param name="nodeGuid">节点</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public object GetFlowData(string nodeGuid)
|
public FlowResult GetFlowData(NodeModelBase nodeGuid)
|
||||||
{
|
{
|
||||||
if (dictNodeFlowData.TryGetValue(nodeGuid, out var data))
|
if (dictNodeFlowData.TryGetValue(nodeGuid, out var data))
|
||||||
{
|
{
|
||||||
@@ -103,29 +103,29 @@ namespace Serein.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加或更新当前节点数据
|
/// 添加或更新当前节点数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeGuid">节点</param>
|
/// <param name="nodeModel">节点</param>
|
||||||
/// <param name="flowData">新的数据</param>
|
/// <param name="flowData">新的数据</param>
|
||||||
public void AddOrUpdate(string nodeGuid, object flowData)
|
public void AddOrUpdate(NodeModelBase nodeModel, FlowResult flowData)
|
||||||
{
|
{
|
||||||
// this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData);
|
// this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData);
|
||||||
dictNodeFlowData.AddOrUpdate(nodeGuid, _ => flowData, (o,n ) => flowData);
|
dictNodeFlowData.AddOrUpdate(nodeModel, _ => flowData, (o,n ) => flowData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上一节点数据透传到下一节点
|
/// 上一节点数据透传到下一节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeModel"></param>
|
/// <param name="nodeModel"></param>
|
||||||
public object TransmissionData(NodeModelBase nodeModel)
|
public FlowResult TransmissionData(NodeModelBase nodeModel)
|
||||||
{
|
{
|
||||||
if (dictPreviousNodes.TryGetValue(nodeModel, out var previousNode)) // 首先获取当前节点的上一节点
|
if (dictPreviousNodes.TryGetValue(nodeModel, out var previousNode)) // 首先获取当前节点的上一节点
|
||||||
{
|
{
|
||||||
if (dictNodeFlowData.TryGetValue(previousNode.Guid, out var data)) // 其次获取上一节点的数据
|
if (dictNodeFlowData.TryGetValue(previousNode, out var data)) // 其次获取上一节点的数据
|
||||||
{
|
{
|
||||||
return data;
|
return data;
|
||||||
//AddOrUpdate(nodeModel.Guid, data); // 然后作为当前节点的数据记录在上下文中
|
//AddOrUpdate(nodeModel.Guid, data); // 然后作为当前节点的数据记录在上下文中
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
throw new InvalidOperationException($"透传{nodeModel.Guid}节点数据时发生异常:上一节点不存在数据");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
97
Library/FlowNode/FlowResult.cs
Normal file
97
Library/FlowNode/FlowResult.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表示空数据
|
||||||
|
/// </summary>
|
||||||
|
/*public readonly struct Unit : IEquatable<Unit>
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 流程返回值的包装
|
||||||
|
/// </summary>
|
||||||
|
public class FlowResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 实例化返回值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeModel"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public FlowResult(NodeModelBase nodeModel, IDynamicContext context, object value)
|
||||||
|
{
|
||||||
|
this.NodeGuid = nodeModel.Guid;
|
||||||
|
this.ContextGuid = context.Guid;
|
||||||
|
this.Value = value;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 空返回值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeModel"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public FlowResult(NodeModelBase nodeModel, IDynamicContext context)
|
||||||
|
{
|
||||||
|
this.NodeGuid = nodeModel.Guid;
|
||||||
|
this.ContextGuid = context.Guid;
|
||||||
|
this.Value = Unit.Default;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试获取值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="targetType">目标类型</param>
|
||||||
|
/// <param name="value">返回值</param>
|
||||||
|
/// <returns>指示是否获取成功</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">无法转为对应类型</exception>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 来源节点Guid
|
||||||
|
/// </summary>
|
||||||
|
public string NodeGuid { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// 来源上下文Guid
|
||||||
|
/// </summary>
|
||||||
|
public string ContextGuid { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// 数据值
|
||||||
|
/// </summary>
|
||||||
|
public object Value { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 生成时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime ResultTime { get; } = DateTime.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否自动回收
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAutoRecovery { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,6 @@ namespace Serein.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||||
{
|
{
|
||||||
#region 节点相关事件
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 实体节点创建完成后调用的方法,调用时间早于 LoadInfo() 方法
|
/// 实体节点创建完成后调用的方法,调用时间早于 LoadInfo() 方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -65,7 +64,7 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
this.DebugSetting.NodeModel = null;
|
this.DebugSetting.NodeModel = null;
|
||||||
this.DebugSetting = null;
|
this.DebugSetting = null;
|
||||||
if(this.MethodDetails.ParameterDetailss != null)
|
if (this.MethodDetails.ParameterDetailss != null)
|
||||||
{
|
{
|
||||||
foreach (var pd in this.MethodDetails.ParameterDetailss)
|
foreach (var pd in this.MethodDetails.ParameterDetailss)
|
||||||
{
|
{
|
||||||
@@ -79,7 +78,7 @@ namespace Serein.Library
|
|||||||
pd.InputType = ParameterValueInputType.Input;
|
pd.InputType = ParameterValueInputType.Input;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.MethodDetails.ParameterDetailss = null;
|
this.MethodDetails.ParameterDetailss = null;
|
||||||
//this.MethodDetails.ActingInstance = null;
|
//this.MethodDetails.ActingInstance = null;
|
||||||
this.MethodDetails.NodeModel = null;
|
this.MethodDetails.NodeModel = null;
|
||||||
@@ -96,280 +95,22 @@ namespace Serein.Library
|
|||||||
this.Env = null;
|
this.Env = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 输出方法参数信息
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 导出为节点信息
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从节点信息加载节点
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nodeInfo"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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<ConnectionArgSourceType>(pdInfo.SourceType);
|
|
||||||
pd.ArgDataSourceNodeGuid = pdInfo.SourceNodeGuid;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadCustomData(nodeInfo); // 加载自定义数据
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 调试中断
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 不再中断
|
|
||||||
/// </summary>
|
|
||||||
public void CancelInterrupt()
|
|
||||||
{
|
|
||||||
this.DebugSetting.IsInterrupt = false;
|
|
||||||
DebugSetting.CancelInterrupt?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 节点方法的执行
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否应该退出执行
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <param name="flowCts"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
//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;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 开始执行
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <param name="token">流程运行</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task StartFlowAsync(IDynamicContext context, CancellationToken token)
|
|
||||||
{
|
|
||||||
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
|
||||||
HashSet<NodeModelBase> processedNodes = new HashSet<NodeModelBase>(); // 用于记录已处理上游节点的节点
|
|
||||||
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
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 执行节点对应的方法
|
/// 执行节点对应的方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">流程上下文</param>
|
/// <param name="context">流程上下文</param>
|
||||||
/// <returns>节点传回数据对象</returns>
|
/// <returns>节点传回数据对象</returns>
|
||||||
public virtual async Task<object> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
public virtual async Task<FlowResult> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
|
||||||
#region 调试中断
|
|
||||||
// 执行触发检查是否需要中断
|
// 执行触发检查是否需要中断
|
||||||
if (DebugSetting.IsInterrupt)
|
if (DebugSetting.IsInterrupt)
|
||||||
{
|
{
|
||||||
context.Env.TriggerInterrupt(Guid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor); // 通知运行环境该节点中断了
|
context.Env.TriggerInterrupt(Guid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor); // 通知运行环境该节点中断了
|
||||||
await DebugSetting.GetInterruptTask.Invoke();
|
await DebugSetting.GetInterruptTask.Invoke();
|
||||||
//await fit.WaitTriggerAsync(Guid); // 创建一个等待的中断任务
|
|
||||||
SereinEnv.WriteLine(InfoType.INFO, $"[{this.MethodDetails?.MethodName}]中断已取消,开始执行后继分支");
|
SereinEnv.WriteLine(InfoType.INFO, $"[{this.MethodDetails?.MethodName}]中断已取消,开始执行后继分支");
|
||||||
//var flowCts = context.Env.IOC.Get<CancellationTokenSource>(NodeStaticConfig.FlipFlopCtsName);
|
|
||||||
if (token.IsCancellationRequested) { return null; }
|
if (token.IsCancellationRequested) { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
MethodDetails md = MethodDetails;
|
MethodDetails md = MethodDetails;
|
||||||
if (md is null)
|
if (md is null)
|
||||||
{
|
{
|
||||||
@@ -377,211 +118,25 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行到某个节点
|
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行到某个节点
|
||||||
{
|
{
|
||||||
|
|
||||||
throw new Exception($"节点{this.Guid}不存在对应委托");
|
throw new Exception($"节点{this.Guid}不存在对应委托");
|
||||||
}
|
}
|
||||||
|
|
||||||
var instance = Env.IOC.Get(md.ActingInstanceType);
|
var instance = Env.IOC.Get(md.ActingInstanceType);
|
||||||
if(instance == null)
|
if (instance is null)
|
||||||
{
|
{
|
||||||
Env.IOC.Register(md.ActingInstanceType).Build();
|
Env.IOC.Register(md.ActingInstanceType).Build();
|
||||||
instance = Env.IOC.Get(md.ActingInstanceType);
|
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);
|
var result = await dd.InvokeAsync(instance, args);
|
||||||
return result;
|
var flowReslt = new FlowResult(this, context, result);
|
||||||
|
return flowReslt;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取对应的参数数组
|
|
||||||
/// </summary>
|
|
||||||
public async Task<object[]> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查监视表达式是否生效
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">上下文</param>
|
|
||||||
/// <param name="nodeModel">节点Moel</param>
|
|
||||||
/// <param name="newData">新的数据</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 程序集更新,更新节点方法描述、以及所有入参描述的类型
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nodeModel">节点Model</param>
|
|
||||||
/// <param name="newMd">新的方法描述</param>
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Serein.Library
|
namespace Serein.Library
|
||||||
@@ -237,6 +238,7 @@ namespace Serein.Library
|
|||||||
// 需要获取预入参数据
|
// 需要获取预入参数据
|
||||||
object inputParameter;
|
object inputParameter;
|
||||||
#region (默认的)从运行时上游节点获取其返回值
|
#region (默认的)从运行时上游节点获取其返回值
|
||||||
|
|
||||||
if (ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
|
if (ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
|
||||||
{
|
{
|
||||||
var previousNode = context.GetPreviousNode(nodeModel);
|
var previousNode = context.GetPreviousNode(nodeModel);
|
||||||
@@ -246,40 +248,32 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
else
|
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
|
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
|
#endregion
|
||||||
#region 判断是否执行表达式
|
#region 判断是否执行表达式
|
||||||
|
|||||||
@@ -14,6 +14,109 @@ namespace Serein.Library.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ConvertHelper
|
public static class ConvertHelper
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 字面量转为对应类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="valueStr"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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}]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对象转JSON文本
|
/// 对象转JSON文本
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
IOC.Reset();
|
IOC.Reset();
|
||||||
IOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
|
IOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
|
||||||
|
|
||||||
var flowTaskOptions = new FlowTaskLibrary
|
var flowTaskOptions = new FlowWorkLibrary
|
||||||
{
|
{
|
||||||
|
|
||||||
Environment = this,
|
Environment = this,
|
||||||
@@ -440,8 +440,8 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
if (true || FlowState == RunState.Running || FlipFlopState == RunState.Running)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -460,14 +460,14 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/*/// <summary>
|
||||||
/// 单独运行一个节点
|
/// 单独运行一个节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeGuid"></param>
|
/// <param name="nodeGuid"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
||||||
{
|
{
|
||||||
object result = new Unit();
|
object result = Unit.Default;
|
||||||
if (this.NodeModels.TryGetValue(nodeGuid, out var model))
|
if (this.NodeModels.TryGetValue(nodeGuid, out var model))
|
||||||
{
|
{
|
||||||
CancellationTokenSource cts = new CancellationTokenSource();
|
CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
@@ -475,7 +475,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
cts?.Cancel();
|
cts?.Cancel();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 结束流程
|
/// 结束流程
|
||||||
@@ -496,7 +496,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="nodeGuid"></param>
|
/// <param name="nodeGuid"></param>
|
||||||
public void ActivateFlipflopNode(string nodeGuid)
|
public void ActivateFlipflopNode(string nodeGuid)
|
||||||
{
|
{
|
||||||
var nodeModel = GuidToModel(nodeGuid);
|
if(!TryGetNodeModel(nodeGuid, out var nodeModel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (nodeModel is null) return;
|
if (nodeModel is null) return;
|
||||||
if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
||||||
{
|
{
|
||||||
@@ -515,7 +518,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="nodeGuid"></param>
|
/// <param name="nodeGuid"></param>
|
||||||
public void TerminateFlipflopNode(string nodeGuid)
|
public void TerminateFlipflopNode(string nodeGuid)
|
||||||
{
|
{
|
||||||
var nodeModel = GuidToModel(nodeGuid);
|
if(!TryGetNodeModel(nodeGuid, out var nodeModel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (nodeModel is null) return;
|
if (nodeModel is null) return;
|
||||||
if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
||||||
{
|
{
|
||||||
@@ -858,7 +864,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
#region 确定节点之间的方法调用关系
|
#region 确定节点之间的方法调用关系
|
||||||
foreach (var nodeInfo in nodeInfos)
|
foreach (var nodeInfo in nodeInfos)
|
||||||
{
|
{
|
||||||
var fromNodeModel = GuidToModel(nodeInfo.Guid);
|
if (!TryGetNodeModel(nodeInfo.Guid, out var fromNodeModel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (fromNodeModel is null) continue;
|
if (fromNodeModel is null) continue;
|
||||||
List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes),
|
List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes),
|
||||||
(ConnectionInvokeType.IsFail, nodeInfo.FalseNodes),
|
(ConnectionInvokeType.IsFail, nodeInfo.FalseNodes),
|
||||||
@@ -869,7 +878,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
// 遍历当前类型分支的节点(确认连接关系)
|
// 遍历当前类型分支的节点(确认连接关系)
|
||||||
foreach (var toNodeGuid in item.toNodeGuids)
|
foreach (var toNodeGuid in item.toNodeGuids)
|
||||||
{
|
{
|
||||||
var toNodeModel = GuidToModel(toNodeGuid);
|
if (!TryGetNodeModel(toNodeGuid, out var toNodeModel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (toNodeModel is null) {
|
if (toNodeModel is null) {
|
||||||
// 防御性代码,加载正常保存的项目文件不会进入这里
|
// 防御性代码,加载正常保存的项目文件不会进入这里
|
||||||
continue;
|
continue;
|
||||||
@@ -915,7 +927,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
&& NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode))
|
&& 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
|
|||||||
/// 将节点放置在容器中
|
/// 将节点放置在容器中
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid)
|
public Task<bool> PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid)
|
||||||
{
|
{
|
||||||
// 获取目标节点与容器节点
|
// 获取目标节点与容器节点
|
||||||
var nodeModel = GuidToModel(nodeGuid);
|
if (!TryGetNodeModel(nodeGuid, out var nodeModel))
|
||||||
if (nodeModel is null ) return false;
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
if(nodeModel.ContainerNode is INodeContainer tmpContainer)
|
}
|
||||||
|
if (nodeModel.ContainerNode is INodeContainer tmpContainer)
|
||||||
{
|
{
|
||||||
SereinEnv.WriteLine(InfoType.WARN, $"节点放置失败,节点[{nodeGuid}]已经放置于容器节点[{((NodeModelBase)tmpContainer).Guid}]");
|
SereinEnv.WriteLine(InfoType.WARN, $"节点放置失败,节点[{nodeGuid}]已经放置于容器节点[{((NodeModelBase)tmpContainer).Guid}]");
|
||||||
return false;
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var containerNode = GuidToModel(containerNodeGuid); // 获取容器节点
|
if (!TryGetNodeModel(containerNodeGuid, out var containerNode))
|
||||||
if (containerNode is not INodeContainer nodeContainer) return false;
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
if (containerNode is not INodeContainer nodeContainer) return Task.FromResult(false);
|
||||||
|
|
||||||
var result = nodeContainer.PlaceNode(nodeModel); // 放置在容器节点
|
var result = nodeContainer.PlaceNode(nodeModel); // 放置在容器节点
|
||||||
if (result)
|
if (result)
|
||||||
@@ -1004,7 +1020,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
OnNodePlace?.Invoke(new NodePlaceEventArgs(nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置
|
OnNodePlace?.Invoke(new NodePlaceEventArgs(nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return Task.FromResult(result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1012,15 +1028,16 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// 将节点从容器节点中脱离
|
/// 将节点从容器节点中脱离
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> TakeOutNodeToContainerAsync(string nodeGuid)
|
public Task<bool> TakeOutNodeToContainerAsync(string nodeGuid)
|
||||||
{
|
{
|
||||||
// 获取目标节点与容器节点
|
// 获取目标节点与容器节点
|
||||||
var nodeModel = GuidToModel(nodeGuid);
|
if (!TryGetNodeModel(nodeGuid, out var nodeModel))
|
||||||
if (nodeModel is null) return false;
|
|
||||||
|
|
||||||
if(nodeModel.ContainerNode is not INodeContainer nodeContainer)
|
|
||||||
{
|
{
|
||||||
return false;
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
if (nodeModel.ContainerNode is not INodeContainer nodeContainer)
|
||||||
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
var result = nodeContainer.TakeOutNode(nodeModel); // 从容器节点取出
|
var result = nodeContainer.TakeOutNode(nodeModel); // 从容器节点取出
|
||||||
if (result)
|
if (result)
|
||||||
@@ -1030,7 +1047,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(nodeGuid)); // 重新放置在画布上
|
OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(nodeGuid)); // 重新放置在画布上
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return Task.FromResult(result);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1044,9 +1061,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <exception cref="NotImplementedException"></exception>
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
public async Task<bool> RemoveNodeAsync(string nodeGuid)
|
public async Task<bool> RemoveNodeAsync(string nodeGuid)
|
||||||
{
|
{
|
||||||
var remoteNode = GuidToModel(nodeGuid);
|
if (!TryGetNodeModel(nodeGuid, out var remoteNode))
|
||||||
if (remoteNode is null)
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (remoteNode is SingleFlipflopNode flipflopNode)
|
if (remoteNode is SingleFlipflopNode flipflopNode)
|
||||||
{
|
{
|
||||||
@@ -1103,7 +1121,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="fromNodeJunctionType">起始节点控制点</param>
|
/// <param name="fromNodeJunctionType">起始节点控制点</param>
|
||||||
/// <param name="toNodeJunctionType">目标节点控制点</param>
|
/// <param name="toNodeJunctionType">目标节点控制点</param>
|
||||||
/// <param name="invokeType">连接关系</param>
|
/// <param name="invokeType">连接关系</param>
|
||||||
public async Task<bool> ConnectInvokeNodeAsync(string fromNodeGuid,
|
public Task<bool> ConnectInvokeNodeAsync(string fromNodeGuid,
|
||||||
string toNodeGuid,
|
string toNodeGuid,
|
||||||
JunctionType fromNodeJunctionType,
|
JunctionType fromNodeJunctionType,
|
||||||
JunctionType toNodeJunctionType,
|
JunctionType toNodeJunctionType,
|
||||||
@@ -1111,17 +1129,19 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
|
|
||||||
// 获取起始节点与目标节点
|
// 获取起始节点与目标节点
|
||||||
var fromNode = GuidToModel(fromNodeGuid);
|
if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode))
|
||||||
var toNode = GuidToModel(toNodeGuid);
|
{
|
||||||
if (fromNode is null || toNode is null) return false;
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromNode is null || toNode is null) return Task.FromResult(false);
|
||||||
(var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType);
|
(var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType);
|
||||||
if (!state)
|
if (!state)
|
||||||
{
|
{
|
||||||
SereinEnv.WriteLine(InfoType.WARN, "出现非预期的连接行为");
|
SereinEnv.WriteLine(InfoType.WARN, "出现非预期的连接行为");
|
||||||
return false; // 出现不符预期的连接行为,忽略此次连接行为
|
return Task.FromResult(false); // 出现不符预期的连接行为,忽略此次连接行为
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(type == JunctionOfConnectionType.Invoke)
|
if(type == JunctionOfConnectionType.Invoke)
|
||||||
{
|
{
|
||||||
if (fromNodeJunctionType == JunctionType.Execute)
|
if (fromNodeJunctionType == JunctionType.Execute)
|
||||||
@@ -1132,7 +1152,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
// 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点
|
// 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点
|
||||||
state = ConnectInvokeOfNode(fromNode, toNode, invokeType); // 本地环境进行连接
|
state = ConnectInvokeOfNode(fromNode, toNode, invokeType); // 本地环境进行连接
|
||||||
}
|
}
|
||||||
return state;
|
return Task.FromResult(state);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1147,8 +1167,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
public Task<bool> SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
|
public Task<bool> SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
// 获取起始节点与目标节点
|
// 获取起始节点与目标节点
|
||||||
var fromNode = GuidToModel(fromNodeGuid);
|
if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode))
|
||||||
var toNode = GuidToModel(toNodeGuid);
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
if (fromNode is null || toNode is null) return Task.FromResult(false);
|
if (fromNode is null || toNode is null) return Task.FromResult(false);
|
||||||
if ( fromNode.SuccessorNodes.TryGetValue(connectionType, out var nodes))
|
if ( fromNode.SuccessorNodes.TryGetValue(connectionType, out var nodes))
|
||||||
{
|
{
|
||||||
@@ -1173,8 +1195,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
public async Task<bool> RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
|
public async Task<bool> RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
// 获取起始节点与目标节点
|
// 获取起始节点与目标节点
|
||||||
var fromNode = GuidToModel(fromNodeGuid);
|
if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode))
|
||||||
var toNode = GuidToModel(toNodeGuid);
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (fromNode is null || toNode is null) return false;
|
if (fromNode is null || toNode is null) return false;
|
||||||
|
|
||||||
var result = await RemoteConnectAsync(fromNode, toNode, connectionType);
|
var result = await RemoteConnectAsync(fromNode, toNode, connectionType);
|
||||||
@@ -1200,8 +1224,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
|
|
||||||
// 获取起始节点与目标节点
|
// 获取起始节点与目标节点
|
||||||
var fromNode = GuidToModel(fromNodeGuid);
|
if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode))
|
||||||
var toNode = GuidToModel(toNodeGuid);
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (fromNode is null || toNode is null) return false;
|
if (fromNode is null || toNode is null) return false;
|
||||||
(var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType);
|
(var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType);
|
||||||
if (!state)
|
if (!state)
|
||||||
@@ -1236,8 +1262,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
public async Task<bool> RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex)
|
public async Task<bool> RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex)
|
||||||
{
|
{
|
||||||
// 获取起始节点与目标节点
|
// 获取起始节点与目标节点
|
||||||
var fromNode = GuidToModel(fromNodeGuid);
|
if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode))
|
||||||
var toNode = GuidToModel(toNodeGuid);
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (fromNode is null || toNode is null) return false;
|
if (fromNode is null || toNode is null) return false;
|
||||||
var result = await RemoteConnectAsync(fromNode, toNode, argIndex);
|
var result = await RemoteConnectAsync(fromNode, toNode, argIndex);
|
||||||
return result;
|
return result;
|
||||||
@@ -1302,10 +1330,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="y"></param>
|
/// <param name="y"></param>
|
||||||
public void MoveNode(string nodeGuid, double x, double y)
|
public void MoveNode(string nodeGuid, double x, double y)
|
||||||
{
|
{
|
||||||
NodeModelBase? nodeModel = GuidToModel(nodeGuid);
|
if (!TryGetNodeModel(nodeGuid, out var nodeModel))
|
||||||
if (nodeModel is null) return;
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
nodeModel.Position.X = x;
|
nodeModel.Position.X = x;
|
||||||
|
|
||||||
nodeModel.Position.Y = y;
|
nodeModel.Position.Y = y;
|
||||||
UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y)));
|
UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y)));
|
||||||
|
|
||||||
@@ -1317,9 +1346,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="newNodeGuid"></param>
|
/// <param name="newNodeGuid"></param>
|
||||||
public Task<string> SetStartNodeAsync(string newNodeGuid)
|
public Task<string> SetStartNodeAsync(string newNodeGuid)
|
||||||
{
|
{
|
||||||
var newStartNodeModel = GuidToModel(newNodeGuid);
|
if (!TryGetNodeModel(newNodeGuid, out var newStartNodeModel))
|
||||||
if (newStartNodeModel is null)
|
{
|
||||||
return Task.FromResult(StartNode?.Guid ?? string.Empty);
|
return Task.FromResult(StartNode?.Guid ?? string.Empty);
|
||||||
|
}
|
||||||
SetStartNode(newStartNodeModel);
|
SetStartNode(newStartNodeModel);
|
||||||
return Task.FromResult(StartNode?.Guid ?? string.Empty);
|
return Task.FromResult(StartNode?.Guid ?? string.Empty);
|
||||||
}
|
}
|
||||||
@@ -1370,12 +1400,13 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="path">属性路径</param>
|
/// <param name="path">属性路径</param>
|
||||||
/// <param name="value">变化后的属性值</param>
|
/// <param name="value">变化后的属性值</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
||||||
{
|
{
|
||||||
var nodeModel = GuidToModel(nodeGuid);
|
if (TryGetNodeModel(nodeGuid, out var nodeModel))
|
||||||
if (nodeModel is null) return;
|
{
|
||||||
SerinExpressionEvaluator.Evaluate($"@Set .{path} = {value}", nodeModel, out _); // 更改对应的数据
|
SerinExpressionEvaluator.Evaluate($"@Set .{path} = {value}", nodeModel, out _); // 更改对应的数据
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
//if (NodeValueChangeLogger.Remove((nodeGuid, path, value)))
|
//if (NodeValueChangeLogger.Remove((nodeGuid, path, value)))
|
||||||
//{
|
//{
|
||||||
// // 说明存在过重复的修改
|
// // 说明存在过重复的修改
|
||||||
@@ -1414,7 +1445,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
public Task<bool> 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);
|
if (nodeModel is null) return Task.FromResult(false);
|
||||||
bool isPass;
|
bool isPass;
|
||||||
if (isAdd)
|
if (isAdd)
|
||||||
@@ -1429,30 +1463,26 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Guid 转 NodeModel
|
/// Guid 转 NodeModel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeGuid">节点Guid</param>
|
/// <param name="nodeGuid">节点Guid</param>
|
||||||
/// <returns>节点Model</returns>
|
/// <returns>节点Model</returns>
|
||||||
/// <exception cref="ArgumentNullException">无法获取节点、Guid/节点为null时报错</exception>
|
/// <exception cref="ArgumentNullException">无法获取节点、Guid/节点为null时报错</exception>
|
||||||
private NodeModelBase? GuidToModel(string nodeGuid)
|
public bool TryGetNodeModel(string nodeGuid,out NodeModelBase nodeModel)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(nodeGuid))
|
if (string.IsNullOrEmpty(nodeGuid))
|
||||||
{
|
{
|
||||||
//throw new ArgumentNullException("not contains - Guid没有对应节点:" + (nodeGuid));
|
nodeModel = null;
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
if (!NodeModels.TryGetValue(nodeGuid, out NodeModelBase? nodeModel) || nodeModel is null)
|
return NodeModels.TryGetValue(nodeGuid, out nodeModel) && nodeModel is not null;
|
||||||
{
|
|
||||||
//throw new ArgumentNullException("null - Guid存在对应节点,但节点为null:" + (nodeGuid));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return nodeModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 流程依赖类库的接口
|
#region 流程依赖类库的接口
|
||||||
|
|
||||||
|
|||||||
@@ -492,10 +492,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
return await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid);
|
return await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
|
||||||
{
|
|
||||||
return await currentFlowEnvironment.InvokeNodeAsync(context, nodeGuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task StartRemoteServerAsync(int port = 7525)
|
public async Task StartRemoteServerAsync(int port = 7525)
|
||||||
{
|
{
|
||||||
@@ -524,6 +521,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
currentFlowEnvironment.SetUIContextOperation(uiContextOperation);
|
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)
|
public bool TryGetDelegateDetails(string libraryName, string methodName, out DelegateDetails del)
|
||||||
{
|
{
|
||||||
return currentFlowEnvironment.TryGetDelegateDetails(libraryName, methodName, out del);
|
return currentFlowEnvironment.TryGetDelegateDetails(libraryName, methodName, out del);
|
||||||
|
|||||||
@@ -1217,14 +1217,13 @@ namespace Serein.NodeFlow.Env
|
|||||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:SetMonitorObjState");
|
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:SetMonitorObjState");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object> 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)
|
public bool TryGetMethodDetailsInfo(string libraryName, string methodName, out MethodDetailsInfo mdInfo)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Serein.NodeFlow
|
namespace Serein.NodeFlow
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
public class FlowTaskLibrary()
|
/// 节点任务执行依赖
|
||||||
|
/// </summary>
|
||||||
|
public class FlowWorkLibrary()
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 流程运行环境
|
/// 流程运行环境
|
||||||
@@ -30,13 +30,13 @@ namespace Serein.NodeFlow
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化选项
|
/// 初始化选项
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public FlowTaskLibrary WorkLibrary { get; }
|
public FlowWorkLibrary WorkLibrary { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 流程任务管理
|
/// 流程任务管理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="library"></param>
|
/// <param name="library"></param>
|
||||||
public FlowWorkManagement(FlowTaskLibrary library)
|
public FlowWorkManagement(FlowWorkLibrary library)
|
||||||
{
|
{
|
||||||
WorkLibrary = library;
|
WorkLibrary = library;
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
var context = pool.Allocate(); // 启动全局触发器时新建上下文
|
var context = pool.Allocate(); // 启动全局触发器时新建上下文
|
||||||
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context, singleToken); // 获取触发器等待Task
|
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context, singleToken); // 获取触发器等待Task
|
||||||
context.AddOrUpdate(singleFlipFlopNode.Guid, newFlowData);
|
context.AddOrUpdate(singleFlipFlopNode, newFlowData);
|
||||||
if (context.NextOrientation == ConnectionInvokeType.None)
|
if (context.NextOrientation == ConnectionInvokeType.None)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -326,6 +326,14 @@ namespace Serein.NodeFlow
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 全局触发器触发后的调用
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="singleFlipFlopNode"></param>
|
||||||
|
/// <param name="singleToken"></param>
|
||||||
|
/// <param name="pool"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
private static async Task? CallSubsequentNode(SingleFlipflopNode singleFlipFlopNode, CancellationToken singleToken, ObjectPool<IDynamicContext> pool, IDynamicContext context)
|
private static async Task? CallSubsequentNode(SingleFlipflopNode singleFlipFlopNode, CancellationToken singleToken, ObjectPool<IDynamicContext> pool, IDynamicContext context)
|
||||||
{
|
{
|
||||||
var flowState = context.NextOrientation; // 记录一下流程状态
|
var flowState = context.NextOrientation; // 记录一下流程状态
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
public override async Task<FlowResult> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -45,7 +45,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return null;
|
return new FlowResult(this, context);
|
||||||
}
|
}
|
||||||
var state = await node.ExecutingAsync(context, token);
|
var state = await node.ExecutingAsync(context, token);
|
||||||
if (context.NextOrientation != ConnectionInvokeType.IsSucceed)
|
if (context.NextOrientation != ConnectionInvokeType.IsSucceed)
|
||||||
@@ -56,7 +56,8 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
//var previousNode = context.GetPreviousNode()
|
//var previousNode = context.GetPreviousNode()
|
||||||
return context.TransmissionData(this); // 条件区域透传上一节点的数据
|
var result = context.TransmissionData(this); // 条件区域透传上一节点的数据
|
||||||
|
return new FlowResult(this,context, result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -110,9 +110,12 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
public override async Task<FlowResult> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested) return null;
|
if (token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return new FlowResult(this, context);
|
||||||
|
}
|
||||||
// 接收上一节点参数or自定义参数内容
|
// 接收上一节点参数or自定义参数内容
|
||||||
object? parameter;
|
object? parameter;
|
||||||
object? result = null;
|
object? result = null;
|
||||||
@@ -121,17 +124,31 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
// 使用自动取参
|
// 使用自动取参
|
||||||
var pd = MethodDetails.ParameterDetailss[INDEX_EXPRESSION];
|
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)
|
if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
|
||||||
{
|
{
|
||||||
result = context.GetFlowData(pd.ArgDataSourceNodeGuid); // 使用自定义节点的参数
|
result = context.GetFlowData(argSourceNode).Value; // 使用自定义节点的参数
|
||||||
}
|
}
|
||||||
else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
|
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
|
else
|
||||||
{
|
{
|
||||||
result = context.TransmissionData(this); // 条件节点透传上一节点的数据
|
result = context.TransmissionData(this).Value; // 条件节点透传上一节点的数据
|
||||||
}
|
}
|
||||||
parameter = result; // 使用上一节点的参数
|
parameter = result; // 使用上一节点的参数
|
||||||
|
|
||||||
@@ -167,7 +184,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
|
|
||||||
SereinEnv.WriteLine(InfoType.INFO, $"{result} {Expression} -> " + context.NextOrientation);
|
SereinEnv.WriteLine(InfoType.INFO, $"{result} {Expression} -> " + context.NextOrientation);
|
||||||
//return result;
|
//return result;
|
||||||
return judgmentResult;
|
return new FlowResult(this, context, judgmentResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -92,22 +92,32 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
public override async Task<FlowResult> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
if(token.IsCancellationRequested) return null;
|
if(token.IsCancellationRequested) return new FlowResult(this, context);
|
||||||
|
|
||||||
object? parameter = null;// context.TransmissionData(this); // 表达式节点使用上一节点数据
|
object? parameter = null;// context.TransmissionData(this); // 表达式节点使用上一节点数据
|
||||||
var pd = MethodDetails.ParameterDetailss[0];
|
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)
|
if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
|
||||||
{
|
{
|
||||||
// 使用自定义节点的参数
|
// 使用自定义节点的参数
|
||||||
parameter = context.GetFlowData(pd.ArgDataSourceNodeGuid);
|
parameter = context.GetFlowData(argSourceNode).Value;
|
||||||
}
|
}
|
||||||
else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -129,13 +139,13 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||||
return result;
|
return new FlowResult(this,context, result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
context.ExceptionOfRuning = ex;
|
context.ExceptionOfRuning = ex;
|
||||||
return parameter;
|
return new FlowResult(this, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="Exception"></exception>
|
/// <exception cref="Exception"></exception>
|
||||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
public override async Task<FlowResult> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
#region 执行前中断
|
#region 执行前中断
|
||||||
if (DebugSetting.IsInterrupt) // 执行触发前
|
if (DebugSetting.IsInterrupt) // 执行触发前
|
||||||
@@ -40,8 +40,13 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
var instance = context.Env.IOC.Get(md.ActingInstanceType);
|
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]);
|
await dd.InvokeAsync(instance, [context]);
|
||||||
var args = await GetParametersAsync(context, token);
|
var args = await this.GetParametersAsync(context, token);
|
||||||
// 因为这里会返回不确定的泛型 IFlipflopContext<TRsult>
|
// 因为这里会返回不确定的泛型 IFlipflopContext<TRsult>
|
||||||
// 而我们只需要获取到 State 和 Value(返回的数据)
|
// 而我们只需要获取到 State 和 Value(返回的数据)
|
||||||
// 所以使用 dynamic 类型接收
|
// 所以使用 dynamic 类型接收
|
||||||
@@ -58,7 +63,9 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,33 +115,34 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
public override async Task<FlowResult> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested) return null;
|
if (token.IsCancellationRequested) return new FlowResult(this, context);
|
||||||
if (string.IsNullOrEmpty(KeyName))
|
if (string.IsNullOrEmpty(KeyName))
|
||||||
{
|
{
|
||||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
SereinEnv.WriteLine(InfoType.ERROR, $"全局数据的KeyName不能为空[{this.Guid}]");
|
SereinEnv.WriteLine(InfoType.ERROR, $"全局数据的KeyName不能为空[{this.Guid}]");
|
||||||
return null;
|
return new FlowResult(this, context);
|
||||||
}
|
}
|
||||||
if (DataNode is null)
|
if (DataNode is null)
|
||||||
{
|
{
|
||||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
SereinEnv.WriteLine(InfoType.ERROR, $"全局数据节点没有设置数据来源[{this.Guid}]");
|
SereinEnv.WriteLine(InfoType.ERROR, $"全局数据节点没有设置数据来源[{this.Guid}]");
|
||||||
return null;
|
return new FlowResult(this, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
context.ExceptionOfRuning = ex;
|
context.ExceptionOfRuning = ex;
|
||||||
return null;
|
return new FlowResult(this, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,12 +165,11 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
public override async Task<FlowResult> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
if(token.IsCancellationRequested) return null;
|
if (token.IsCancellationRequested) return new FlowResult(this, context);
|
||||||
var @params = await GetParametersAsync(context, token);
|
var @params = await this.GetParametersAsync(context, token);
|
||||||
if(token.IsCancellationRequested) return null;
|
if(token.IsCancellationRequested) return new FlowResult(this, context);
|
||||||
|
|
||||||
|
|
||||||
//context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改
|
//context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改
|
||||||
ReloadScript();// 每次都重新解析
|
ReloadScript();// 每次都重新解析
|
||||||
@@ -199,9 +198,9 @@ namespace Serein.NodeFlow.Model
|
|||||||
if (token.IsCancellationRequested) return null;
|
if (token.IsCancellationRequested) return null;
|
||||||
|
|
||||||
var result = await ScriptInterpreter.InterpretAsync(scriptContext, mainNode); // 从入口节点执行
|
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);
|
//SereinEnv.WriteLine(InfoType.INFO, "FlowContext Guid : " + context.Guid);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
public override async Task<FlowResult> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested) return null;
|
if (token.IsCancellationRequested) return new FlowResult(this,context);
|
||||||
if(Adapter is null)
|
if(Adapter is null)
|
||||||
{
|
{
|
||||||
|
|
||||||
var result = await base.ExecutingAsync(context, token);
|
var result = await base.ExecutingAsync(context, token);
|
||||||
if (result is IEmbeddedContent adapter)
|
if (result.Value is IEmbeddedContent adapter)
|
||||||
{
|
{
|
||||||
this.Adapter = adapter;
|
this.Adapter = adapter;
|
||||||
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||||
@@ -35,12 +35,12 @@ namespace Serein.NodeFlow.Model
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var p = context.GetPreviousNode(this);
|
var p = context.GetPreviousNode(this);
|
||||||
var data = context.GetFlowData(p.Guid);
|
var data = context.GetFlowData(p).Value;
|
||||||
var iflowContorl = Adapter.GetFlowControl();
|
var iflowContorl = Adapter.GetFlowControl();
|
||||||
iflowContorl.OnExecuting(data);
|
iflowContorl.OnExecuting(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return new FlowResult(this, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,22 +42,21 @@ namespace Serein.NodeFlow
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public object? GetArgData(IDynamicContext context, int index)
|
//public object? GetArgData(IDynamicContext context, int index)
|
||||||
{
|
//{
|
||||||
var _paramsKey = $"{context?.Guid}_{NodeModel.Guid}_Params";
|
// var _paramsKey = $"{context?.Guid}_{NodeModel.Guid}_Params";
|
||||||
var obj = context?.GetFlowData(_paramsKey);
|
// var obj = context?.GetFlowData(_paramsKey);
|
||||||
if (obj is object[] @params && index < @params.Length)
|
// if (obj is object[] @params && index < @params.Length)
|
||||||
{
|
// {
|
||||||
return @params[index];
|
// return @params[index];
|
||||||
}
|
// }
|
||||||
return null;
|
// return null;
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
//public object? GetFlowData(IDynamicContext context)
|
||||||
public object? GetFlowData(IDynamicContext context)
|
//{
|
||||||
{
|
// return context?.GetFlowData(NodeModel.Guid);
|
||||||
return context?.GetFlowData(NodeModel.Guid);
|
//}
|
||||||
}
|
|
||||||
|
|
||||||
public object? GetGlobalData(string keyName)
|
public object? GetGlobalData(string keyName)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
|
using Serein.Library.Utils;
|
||||||
using Serein.Script.Node;
|
using Serein.Script.Node;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ public class NodeJunctionView : TemplatedControl
|
|||||||
nodeOperationService.ConnectingManage.Reset();
|
nodeOperationService.ConnectingManage.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckJunvtion()
|
private async void CheckJunvtion()
|
||||||
{
|
{
|
||||||
var myData = nodeOperationService.ConnectingManage;
|
var myData = nodeOperationService.ConnectingManage;
|
||||||
if(myData.StartJunction is null || myData.CurrentJunction is null)
|
if(myData.StartJunction is null || myData.CurrentJunction is null)
|
||||||
@@ -157,7 +157,7 @@ public class NodeJunctionView : TemplatedControl
|
|||||||
#region 方法调用关系创建
|
#region 方法调用关系创建
|
||||||
if (myData.Type == JunctionOfConnectionType.Invoke)
|
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.StartJunction.JunctionType,
|
||||||
myData.CurrentJunction.JunctionType,
|
myData.CurrentJunction.JunctionType,
|
||||||
myData.ConnectionInvokeType);
|
myData.ConnectionInvokeType);
|
||||||
@@ -177,7 +177,7 @@ public class NodeJunctionView : TemplatedControl
|
|||||||
argIndex = myData.CurrentJunction.ArgIndex;
|
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.StartJunction.JunctionType,
|
||||||
myData.CurrentJunction.JunctionType,
|
myData.CurrentJunction.JunctionType,
|
||||||
myData.ConnectionArgSourceType,
|
myData.ConnectionArgSourceType,
|
||||||
|
|||||||
@@ -1944,7 +1944,7 @@ namespace Serein.Workbench
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender"></param>
|
/// <param name="sender"></param>
|
||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
private async void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (IsSelectControl)
|
if (IsSelectControl)
|
||||||
{
|
{
|
||||||
@@ -1975,7 +1975,7 @@ namespace Serein.Workbench
|
|||||||
#region 方法调用关系创建
|
#region 方法调用关系创建
|
||||||
if (myData.Type == JunctionOfConnectionType.Invoke)
|
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.StartJunction.JunctionType,
|
||||||
myData.CurrentJunction.JunctionType,
|
myData.CurrentJunction.JunctionType,
|
||||||
myData.ConnectionInvokeType);
|
myData.ConnectionInvokeType);
|
||||||
@@ -1995,7 +1995,7 @@ namespace Serein.Workbench
|
|||||||
argIndex = argJunction2.ArgIndex;
|
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.StartJunction.JunctionType,
|
||||||
myData.CurrentJunction.JunctionType,
|
myData.CurrentJunction.JunctionType,
|
||||||
myData.ConnectionArgSourceType,
|
myData.ConnectionArgSourceType,
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ namespace Serein.Workbench.Node.View
|
|||||||
EmbedContainer.Child = userControl;
|
EmbedContainer.Child = userControl;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NodeControlBase_Initialized(object sender, EventArgs e)
|
private void NodeControlBase_Initialized(object sender, EventArgs e)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Serein.Workbench.Node.ViewModel
|
|||||||
{
|
{
|
||||||
var context = new DynamicContext(NodeModel.Env);
|
var context = new DynamicContext(NodeModel.Env);
|
||||||
var cts = new CancellationTokenSource();
|
var cts = new CancellationTokenSource();
|
||||||
await NodeModel.ExecutingAsync(context, cts.Token);
|
var result = await NodeModel.ExecutingAsync(context, cts.Token);
|
||||||
cts?.Dispose();
|
cts?.Dispose();
|
||||||
if (context.NextOrientation == ConnectionInvokeType.IsSucceed
|
if (context.NextOrientation == ConnectionInvokeType.IsSucceed
|
||||||
&& NodeModel.Adapter.GetUserControl() is UserControl userControl)
|
&& NodeModel.Adapter.GetUserControl() is UserControl userControl)
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ namespace Serein.Workbench.Views
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender"></param>
|
/// <param name="sender"></param>
|
||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
private async void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (IsSelectControl)
|
if (IsSelectControl)
|
||||||
{
|
{
|
||||||
@@ -340,7 +340,7 @@ namespace Serein.Workbench.Views
|
|||||||
#region 方法调用关系创建
|
#region 方法调用关系创建
|
||||||
if (myData.Type == JunctionOfConnectionType.Invoke)
|
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.StartJunction.JunctionType,
|
||||||
myData.CurrentJunction.JunctionType,
|
myData.CurrentJunction.JunctionType,
|
||||||
myData.ConnectionInvokeType);
|
myData.ConnectionInvokeType);
|
||||||
@@ -360,7 +360,7 @@ namespace Serein.Workbench.Views
|
|||||||
argIndex = argJunction2.ArgIndex;
|
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.StartJunction.JunctionType,
|
||||||
myData.CurrentJunction.JunctionType,
|
myData.CurrentJunction.JunctionType,
|
||||||
myData.ConnectionArgSourceType,
|
myData.ConnectionArgSourceType,
|
||||||
|
|||||||
Reference in New Issue
Block a user