修复了全局节点连接异常异常。

This commit is contained in:
fengjiayi
2025-07-29 14:25:31 +08:00
parent acb15c323e
commit 77160feaeb
66 changed files with 1719 additions and 1342 deletions

View File

@@ -2,6 +2,7 @@
using Serein.Library.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -196,6 +197,10 @@ namespace Serein.Library
}
}
private static ObjectPool<Stack<IFlowNode>> flowStackPool = new ObjectPool<Stack<IFlowNode>>(()=> new Stack<IFlowNode>());
//private static ObjectPool<HashSet<IFlowNode>> processedNodesPool = new ObjectPool<HashSet<IFlowNode>>(()=> new HashSet<IFlowNode>());
private static ObjectPool<HashSet<IFlowNode>> checkpoints = new ObjectPool<HashSet<IFlowNode>>(()=> new HashSet<IFlowNode>());
/// <summary>
/// 开始执行
/// </summary>
@@ -203,133 +208,186 @@ namespace Serein.Library
/// <param name="context"></param>
/// <param name="token">流程运行</param>
/// <returns></returns>
#nullable enable
public static async Task<FlowResult> StartFlowAsync(this IFlowNode nodeModel, IFlowContext context, CancellationToken token)
{
Stack<IFlowNode> stack = new Stack<IFlowNode>();
HashSet<IFlowNode> processedNodes = new HashSet<IFlowNode>(); // 用于记录已处理上游节点的节点
stack.Push(nodeModel);
#nullable enable
Stack<IFlowNode> flowStack = flowStackPool.Allocate() ;
//HashSet<IFlowNode> processedNodes = processedNodesPool.Allocate() ; // 用于记录已处理上游节点的节点
flowStack.Push(nodeModel);
IFlowNode? previousNode = null;
IFlowNode? currentNode = null;
while (true)
try
{
if (token.IsCancellationRequested)
{
throw new Exception($"流程执行被取消,未能获取到流程结果。");
}
#region
// 从栈中弹出一个节点作为当前节点进行处理
previousNode = currentNode;
currentNode = stack.Pop();
#region
FlowInvokeInfo? invokeInfo = null;
var isRecordInvokeInfo = context.IsRecordInvokeInfo;
if (!isRecordInvokeInfo) goto Label_NotRecordInvoke;
FlowInvokeInfo.InvokeType invokeType = context.NextOrientation switch
{
ConnectionInvokeType.IsSucceed => FlowInvokeInfo.InvokeType.IsSucceed,
ConnectionInvokeType.IsFail => FlowInvokeInfo.InvokeType.IsFail,
ConnectionInvokeType.IsError => FlowInvokeInfo.InvokeType.IsError,
ConnectionInvokeType.Upstream => FlowInvokeInfo.InvokeType.Upstream,
_ => FlowInvokeInfo.InvokeType.None
};
invokeInfo = context.NewInvokeInfo(previousNode, currentNode, invokeType);
#endregion
Label_NotRecordInvoke:
context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态
FlowResult flowResult = null;
try
{
flowResult = await currentNode.ExecutingAsync(context, token);
if (context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置
{
context.NextOrientation = ConnectionInvokeType.IsSucceed;
}
}
catch (Exception ex)
{
flowResult = new FlowResult(currentNode.Guid, context);
context.Env.WriteLine(InfoType.ERROR, $"节点[{currentNode.Guid}]异常:" + ex);
context.NextOrientation = ConnectionInvokeType.IsError;
context.ExceptionOfRuning = ex;
}
#endregion
#region
var state = context.NextOrientation switch
{
ConnectionInvokeType.IsFail => FlowInvokeInfo.RunState.Failed,
ConnectionInvokeType.IsError => FlowInvokeInfo.RunState.Error,
_ => FlowInvokeInfo.RunState.Succeed
};
if (isRecordInvokeInfo)
{
invokeInfo.UploadState(state);
invokeInfo.UploadResultValue(flowResult.Value);
}
#endregion
#region
context.AddOrUpdateFlowData(currentNode.Guid, flowResult); // 上下文中更新数据
// 首先将指定类别后继分支的所有节点逆序推入栈中
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
for (int index = nextNodes.Count - 1; index >= 0; index--)
{
// 筛选出启用的节点的节点
if (nextNodes[index].DebugSetting.IsEnable)
{
//if (!ignodeState)
context.SetPreviousNode(nextNodes[index].Guid, currentNode.Guid);
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].Guid, currentNode.Guid);
stack.Push(upstreamNodes[index]);
}
}
#endregion
#region
if (stack.Count == 0)
{
return flowResult; // 说明流程到了终点
}
if (context.RunState == RunState.Completion)
{
currentNode.Env.WriteLine(InfoType.INFO, $"流程执行到节点[{currentNode.Guid}]时提前结束,将返回当前执行结果。");
return flowResult; // 流程执行完成,返回结果
}
if (token.IsCancellationRequested)
{
throw new Exception($"流程执行到节点[{currentNode.Guid}]时被取消,未能获取到流程结果。");
}
#endregion
#if DEBUG
//await Task.Delay(1);
/*
var sw = Stopwatch.StartNew();
var checkpoints = new Dictionary<string, TimeSpan>();
checkpoints["创建调用信息"] = sw.Elapsed;
var last = TimeSpan.Zero;
foreach (var kv in checkpoints)
{
SereinEnv.WriteLine(InfoType.INFO, $"{kv.Key} 耗时: {(kv.Value - last).TotalMilliseconds} ms");
last = kv.Value;
}
*/
var sw = Stopwatch.StartNew();
var checkpoints = new Dictionary<string, TimeSpan>();
#endif
while (true)
{
#if DEBUG
sw.Restart();
var last = TimeSpan.Zero;
checkpoints.Clear();
#endif
#region
// 从栈中弹出一个节点作为当前节点进行处理
previousNode = currentNode;
currentNode = flowStack.Pop();
#region
FlowInvokeInfo? invokeInfo = null;
var isRecordInvokeInfo = context.IsRecordInvokeInfo;
if (!isRecordInvokeInfo) goto Label_NotRecordInvoke;
FlowInvokeInfo.InvokeType invokeType = context.NextOrientation switch
{
ConnectionInvokeType.IsSucceed => FlowInvokeInfo.InvokeType.IsSucceed,
ConnectionInvokeType.IsFail => FlowInvokeInfo.InvokeType.IsFail,
ConnectionInvokeType.IsError => FlowInvokeInfo.InvokeType.IsError,
ConnectionInvokeType.Upstream => FlowInvokeInfo.InvokeType.Upstream,
_ => FlowInvokeInfo.InvokeType.None
};
invokeInfo = context.NewInvokeInfo(previousNode, currentNode, invokeType);
#endregion
#if DEBUG
checkpoints[$"[{currentNode.Guid}]\t创建调用信息"] = sw.Elapsed;
#endif
Label_NotRecordInvoke:
context.NextOrientation = ConnectionInvokeType.IsSucceed; // 默认执行成功
FlowResult? flowResult = null;
try
{
flowResult = await currentNode.ExecutingAsync(context, token);
}
catch (Exception ex)
{
flowResult = new FlowResult(currentNode.Guid, context);
context.Env.WriteLine(InfoType.ERROR, $"节点[{currentNode.Guid}]异常:" + ex);
context.NextOrientation = ConnectionInvokeType.IsError;
context.ExceptionOfRuning = ex;
}
#if DEBUG
finally
{
checkpoints[$"[{currentNode.Guid}]\t方法调用"] = sw.Elapsed;
}
#endif
#endregion
#region
var state = context.NextOrientation switch
{
ConnectionInvokeType.IsFail => FlowInvokeInfo.RunState.Failed,
ConnectionInvokeType.IsError => FlowInvokeInfo.RunState.Error,
_ => FlowInvokeInfo.RunState.Succeed
};
if (isRecordInvokeInfo)
{
invokeInfo.UploadState(state);
invokeInfo.UploadResultValue(flowResult.Value);
}
#endregion
#if DEBUG
checkpoints[$"[{currentNode.Guid}]\t更新调用信息"] = sw.Elapsed;
#endif
#region
context.AddOrUpdateFlowData(currentNode.Guid, flowResult); // 上下文中更新数据
#if DEBUG
checkpoints["[{currentNode.Guid}]\t执行完成时更新栈"] = sw.Elapsed;
#endif
// 首先将指定类别后继分支的所有节点逆序推入栈中
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
for (int index = nextNodes.Count - 1; index >= 0; index--)
{
// 筛选出启用的节点的节点
if (nextNodes[index].DebugSetting.IsEnable)
{
var node = nextNodes[index];
context.SetPreviousNode(node.Guid, currentNode.Guid);
flowStack.Push(node);
}
}
#if DEBUG
checkpoints[$"[{currentNode.Guid}]\t后继分支推入栈中"] = sw.Elapsed;
#endif
// 然后将指上游分支的所有节点逆序推入栈中
var upstreamNodes = currentNode.SuccessorNodes[ConnectionInvokeType.Upstream];
for (int index = upstreamNodes.Count - 1; index >= 0; index--)
{
// 筛选出启用的节点的节点
if (upstreamNodes[index].DebugSetting.IsEnable)
{
var node = upstreamNodes[index];
context.SetPreviousNode(node.Guid, currentNode.Guid);
flowStack.Push(node);
}
}
#if DEBUG
checkpoints[$"[{currentNode.Guid}]\t上游分支推入栈中"] = sw.Elapsed;
#endif
#endregion
#if DEBUG
foreach (var kv in checkpoints)
{
SereinEnv.WriteLine(InfoType.INFO, $"{kv.Key} 耗时: {(kv.Value - last).TotalMilliseconds} ms");
last = kv.Value;
}
SereinEnv.WriteLine(InfoType.INFO, $"------");
#endif
#region
if (flowStack.Count == 0)
{
return flowResult; // 说明流程到了终点
}
if (context.RunState == RunState.Completion)
{
currentNode.Env.WriteLine(InfoType.INFO, $"流程执行到节点[{currentNode.Guid}]时提前结束,将返回当前执行结果。");
return flowResult; // 流程执行完成,返回结果
}
if (token.IsCancellationRequested)
{
throw new Exception($"流程执行到节点[{currentNode.Guid}]时被取消,未能获取到流程结果。");
}
#endregion
#if DEBUG
//await Task.Delay(1);
#endif
}
}
finally
{
flowStackPool.Free(flowStack);
//processedNodesPool.Free(processedNodes);
}
}

View File

@@ -58,3 +58,5 @@ namespace Serein.Library
public double ScaleY;
}
}

View File

@@ -173,34 +173,6 @@ namespace Serein.Library
dictPreviousNodes.AddOrUpdate(currentNodeModel, (_) => PreviousNode, (o, n) => PreviousNode);
}
/// <summary>
/// 忽略处理该节点流程
/// </summary>
/// <param name="node"></param>
public void IgnoreFlowHandle(string node)
{
dictIgnoreNodeFlow.AddOrUpdate(node, (o) => true, (o, n) => true);
}
/// <summary>
/// 获取此次流程处理状态
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public bool GetIgnodeFlowStateUpload(string node)
{
return dictIgnoreNodeFlow.TryGetValue(node, out var state) ? state : false;
}
/// <summary>
/// 恢复流程处理状态
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public void RecoverIgnodeFlowStateUpload(string node)
{
dictIgnoreNodeFlow.AddOrUpdate(node, (o) => false, (o, n) => false);
}
/// <summary>
/// 获取当前节点的运行时上一节点
/// </summary>
@@ -242,7 +214,6 @@ namespace Serein.Library
/// <param name="flowData">新的数据</param>
public void AddOrUpdateFlowData(string nodeModel, FlowResult flowData)
{
// this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData);
dictNodeFlowData.AddOrUpdate(nodeModel, _ => flowData, (o,n ) => flowData);
}
@@ -274,10 +245,6 @@ namespace Serein.Library
throw new InvalidOperationException($"透传{nodeModel}节点数据时发生异常:上一节点不存在数据");
}
/// <summary>
/// 开始
/// </summary>
/// <summary>
/// 重置
/// </summary>

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
@@ -23,7 +24,7 @@ namespace Serein.Library
/// <summary>
/// 流程返回值的包装
/// </summary>
public class FlowResult
public sealed class FlowResult
{
/// <summary>
/// 实例化返回值

View File

@@ -151,14 +151,21 @@ namespace Serein.Library
}
}
private enum ActionType
{
Action,
Task,
}
private ActionType actionType = ActionType.Action;
public void SetAction(Action<IFlowContext> action)
{
this.action = action;
actionType = ActionType.Action;
}
public void SetAction(Func<IFlowContext, Task> taskFunc)
{
this.taskFunc = taskFunc;
actionType = ActionType.Task;
}
@@ -177,12 +184,14 @@ namespace Serein.Library
public Dictionary<ConnectionInvokeType, List<CallNode>> SuccessorNodes { get; private set; }
public CallNode[][] ChildNodes { get; private set; } = new CallNode[][]
{
new CallNode[32],
new CallNode[32],
new CallNode[32],
new CallNode[32]
new CallNode[MaxChildNodeCount],
new CallNode[MaxChildNodeCount],
new CallNode[MaxChildNodeCount],
new CallNode[MaxChildNodeCount]
};
private const int MaxChildNodeCount = 16; // 每个分支最多支持16个子节点
public int GetCount(ConnectionInvokeType type)
{
if (type == ConnectionInvokeType.Upstream) return UpstreamNodeCount;
@@ -245,13 +254,13 @@ namespace Serein.Library
{
return;
}
if (action is not null)
if (actionType == ActionType.Action)
{
action(context);
action.Invoke(context);
}
else if (taskFunc is not null)
else if (actionType == ActionType.Task)
{
await taskFunc(context);
await taskFunc.Invoke(context);
}
else
{
@@ -286,11 +295,8 @@ namespace Serein.Library
FlowResult flowResult = null;
try
{
context.NextOrientation = ConnectionInvokeType.IsSucceed; // 默认执行成功
await currentNode.InvokeAsync(context, token);
if (context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置
{
context.NextOrientation = ConnectionInvokeType.IsSucceed;
}
}
catch (Exception ex)
{
@@ -306,17 +312,18 @@ namespace Serein.Library
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
for (int index = nextNodes.Count - 1; index >= 0; index--)
{
//if (!ignodeState)
context.SetPreviousNode(nextNodes[index].Guid, currentNode.Guid);
stack.Push(nextNodes[index]);
var node = nextNodes[index];
context.SetPreviousNode(node.Guid, currentNode.Guid);
stack.Push(node);
}
// 然后将指上游分支的所有节点逆序推入栈中
var upstreamNodes = currentNode.SuccessorNodes[ConnectionInvokeType.Upstream];
for (int index = upstreamNodes.Count - 1; index >= 0; index--)
{
context.SetPreviousNode(upstreamNodes[index].Guid, currentNode.Guid);
stack.Push(upstreamNodes[index]);
var node = upstreamNodes[index];
context.SetPreviousNode(node.Guid, currentNode.Guid);
stack.Push(node);
}
#endregion
@@ -334,7 +341,7 @@ namespace Serein.Library
_stackPool.Return(stack);
context.Env.WriteLine(InfoType.INFO, $"流程执行到节点[{currentNode.Guid}]时提前结束,将返回当前执行结果。");
flowResult = context.GetFlowData(currentNode.Guid); _stackPool.Return(stack);
flowResult = context.GetFlowData(currentNode.Guid);
return flowResult; // 流程执行完成,返回结果
}

View File

@@ -306,7 +306,7 @@ namespace Serein.Library
MethodName = this.MethodName, // 拷贝
MethodLockName = this.MethodLockName, // 拷贝
ParamsArgIndex = this.ParamsArgIndex, // 拷贝
ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(), // 拷贝属于节点方法的新入参描述
ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray() , // 拷贝属于节点方法的新入参描述
IsAsync = this.IsAsync, // 拷贝
IsStatic = this.IsStatic, // 拷贝
};

View File

@@ -10,8 +10,28 @@ namespace Serein.Library.Utils
/// <summary>
/// 对于 linq 的异步扩展方法
/// </summary>
public static class LinqAsyncHelper
public static class LinqHelper
{
/// <summary>
/// 根据条件筛选,只保留第一个满足条件的元素,其余的不包含。
/// </summary>
public static IEnumerable<T> DistinctByCondition<T>(
this IEnumerable<T> source,
Func<T, bool> predicate)
{
var seenKeys = new HashSet<T>();
foreach (var item in source)
{
if (!predicate(item))
continue;
/*var key = keySelector(item);
if (seenKeys.Add(key)) // 如果是新键*/
yield return item;
}
}
public static async Task<IEnumerable<TResult>> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, Task<TResult>> method)
{

View File

@@ -42,7 +42,8 @@ namespace Serein.Library.Utils
public ObjectPool(Factory factory)
: this(factory, Environment.ProcessorCount * 2)
{ }
{
}
public ObjectPool(Factory factory, int size)
{

View File

@@ -2,6 +2,7 @@
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.NodeFlow.Services;
using System;
using System.Collections;

View File

@@ -3,6 +3,7 @@ using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.NodeFlow.Model.Operation;
using Serein.NodeFlow.Services;
using System.Diagnostics;

View File

@@ -3,6 +3,7 @@ using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Env;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Reflection;

View File

@@ -0,0 +1,353 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.NodeFlow.Services;
using System.Text;
namespace Serein.NodeFlow.Model.Infos
{
/// <summary>
/// 指示流程接口方法需要生成什么代码
/// </summary>
internal class FlowApiMethodInfo
{
public FlowApiMethodInfo(SingleFlowCallNode singleFlowCallNode)
{
NodeModel = singleFlowCallNode;
}
/// <summary>
/// 对应的流程节点
/// </summary>
public SingleFlowCallNode NodeModel { get; }
/// <summary>
/// 生成的接口名称
/// </summary>
public string ApiMethodName { get; set; }
/// <summary>
/// 返回类型
/// </summary>
public Type ReturnType { get; set; }
/// <summary>
/// 参数实体信息
/// </summary>
public List<ParamInfo> ParamInfos { get; set; } = [];
/// <summary>
/// 参数实体类型名称
/// </summary>
public string ParamTypeName => $"FlowApiInvoke_{ApiMethodName}";
public class ParamInfo
{
public ParamInfo(Type type, string paramName)
{
Type = type;
ParamName = paramName;
}
/// <summary>
///
/// </summary>
public Type Type { get; set; }
/// <summary>
/// 参数名称
/// </summary>
public string ParamName { get; set; }
/// <summary>
/// 注释备注
/// </summary>
public string Comments { get; set; }
}
public enum ParamType
{
Defute, // 仅使用方法参数
HasToken, // 包含取消令牌和方法参数
HasContextAndToken, // 包含上下文、取消令牌和方法参数
}
public bool IsVoid => ReturnType == typeof(void);
public string ObjPoolName => $"_objPool{ParamTypeName.ToPascalCase()}";
/// <summary>
/// 生成所需的参数类的签名代码
/// </summary>
/// <returns></returns>
public string ToParamterClassSignature()
{
StringBuilder sb = new StringBuilder();
//sb.AppendCode(2, $"private static readonly global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}> {ObjPoolName} = ");
//sb.AppendCode(2, $" new global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}>(");
//sb.AppendCode(2, $" new global::Microsoft.Extensions.ObjectPool.DefaultPooledObjectPolicy<{ParamTypeName}>()); ");
sb.AppendLine();
var classXmlComments = $"流程接口[{ApiMethodName}]需要的参数类".ToXmlComments(2);
sb.AppendCode(2, classXmlComments);
sb.AppendCode(2, $"public class {ParamTypeName}");
sb.AppendCode(2, $"{{");
for (int index = 0; index < ParamInfos.Count; index++)
{
ParamInfo? info = ParamInfos[index];
var argXmlComments = $"[{index}]流程接口参数{(string.IsNullOrWhiteSpace(info.Comments) ? string.Empty : $"{info.Comments}")}".ToXmlComments(2);
sb.AppendCode(3, argXmlComments);
sb.AppendCode(3, $"public global::{info.Type.FullName} {info.ParamName.ToPascalCase()} {{ get; set; }}");
}
sb.AppendCode(2, $"}}");
return sb.ToString();
}
public string ToObjPoolSignature()
{
StringBuilder sb = new StringBuilder();
sb.AppendCode(2, $"private static readonly global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}> {ObjPoolName} = ");
sb.AppendCode(4, $"new global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}>(");
sb.AppendCode(5, $"new global::Microsoft.Extensions.ObjectPool.DefaultPooledObjectPolicy<{ParamTypeName}>()); ");
return sb.ToString();
}
/// <summary>
/// 生成接口的签名方法
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public string ToInterfaceMethodSignature(ParamType type)
{
var taskTypeFullName = $"global::System.Threading.Tasks.Task";
var contextFullName = $"global::{typeof(IFlowContext).FullName}";
var tokenFullName = $"global::{typeof(CancellationToken).FullName}";
var returnContext = IsVoid ? taskTypeFullName : $"{taskTypeFullName}<{ReturnType.FullName}>";
if (type == ParamType.Defute)
{
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
return $"{returnContext} {ApiMethodName}({paramSignature});";
}
else if (type == ParamType.HasToken)
{
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
return $"{returnContext} {ApiMethodName}({tokenFullName} token, {paramSignature});";
}
else if (type == ParamType.HasContextAndToken)
{
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
return $"{returnContext} {ApiMethodName}({contextFullName} flowContext, {tokenFullName} token, {paramSignature});";
}
throw new Exception();
}
/// <summary>
/// 生成实现方法的签名代码
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public string ToImpleMethodSignature(ParamType type)
{
var taskTypeFullName = $"global::System.Threading.Tasks.Task";
var contextApiFullName = $"global::{typeof(IFlowContext).FullName}";
var contextImpleFullName = $"global::{typeof(FlowContext).FullName}";
var tokenSourceFullName = $"global::{typeof(CancellationTokenSource).FullName}";
var tokenFullName = $"global::{typeof(CancellationToken).FullName}";
var flowContextPoolName = $"global::{typeof(LightweightFlowControl).FullName}";
string flowEnvironment = nameof(flowEnvironment);
string flowContext = nameof(flowContext);
string token = nameof(token);
var returnTypeContext = IsVoid ? taskTypeFullName : $"{taskTypeFullName}<{ReturnType.FullName}>";
StringBuilder sb = new StringBuilder();
if (IsVoid)
{
if (type == ParamType.Defute)
{
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{{{p.Type.FullName} {p.ParamName}"));
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({paramSignature})");
sb.AppendCode(2, $"{{");
sb.AppendCode(3, $"{contextApiFullName} {flowContext} = {flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Allocate)}(); // 从对象池获取一个上下文");
sb.AppendCode(3, $"{tokenSourceFullName} cts = new {tokenSourceFullName}(); // 创建取消令牌");
sb.AppendCode(3, $"try");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"await {ApiMethodName}({flowContext}, cts.Token, {invokeParamSignature}); // 调用目标方法");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"catch (Exception)");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"throw;");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"finally");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"{flowContext}.{nameof(IFlowContext.Reset)}(); ");
sb.AppendCode(4, $"cts.{nameof(CancellationTokenSource.Dispose)}(); ");
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
sb.AppendCode(3, $"}}");
sb.AppendCode(2, $"}}");
return sb.ToString();
}
else if (type == ParamType.HasToken)
{
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{{{p.Type.FullName} {p.ParamName}"));
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({tokenFullName} {token}, {paramSignature})");
sb.AppendCode(2, $"{{");
sb.AppendCode(3, $"{contextApiFullName} {flowContext} = {flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Allocate)}(); // 从对象池获取一个上下文");
sb.AppendCode(3, $"try");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"await {ApiMethodName}({flowContext}, {token}, {invokeParamSignature}); // 调用目标方法");
sb.AppendCode(4, $"{flowContext}.{nameof(IFlowContext.Reset)}(); ");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"catch (Exception)");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"throw;");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"finally");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
sb.AppendCode(3, $"}}");
sb.AppendCode(2, $"}}");
return sb.ToString();
}
else if (type == ParamType.HasContextAndToken)
{
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{{{p.Type.FullName} {p.ParamName}"));
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({contextApiFullName} {flowContext}, {tokenFullName} token, {paramSignature})");
sb.AppendCode(2, $"{{");
sb.AppendCode(3, $"token.ThrowIfCancellationRequested(); // 检查任务是否取消");
sb.AppendCode(3, $"try");
sb.AppendCode(3, $"{{");
sb.AppendCode(3, $"global::{ParamTypeName} data = {ObjPoolName}.Get(); // 从对象池获取一个对象");
for (int index = 0; index < ParamInfos.Count; index++)
{
ParamInfo? info = ParamInfos[index];
sb.AppendCode(4, $"data.{info.ParamName.ToPascalCase()} = {info.ParamName}; // [{index}] {info.Comments}");
}
sb.AppendCode(3, $"{flowContext}.{nameof(IFlowContext.AddOrUpdate)}(\"{ApiMethodName}\", data);");
sb.AppendCode(3, $"{flowContext}.{nameof(IFlowContext.SetPreviousNode)}(\"{NodeModel.TargetNode.Guid}\", \"{ApiMethodName}\");");
sb.AppendCode(3, $"global::{typeof(CallNode).FullName} node = Get(\"{NodeModel.Guid}\");");
sb.AppendCode(3, $"await node.{nameof(CallNode.StartFlowAsync)}({flowContext}, {token}); // 调用目标方法");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"catch (Exception)");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"throw;");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"finally");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
sb.AppendCode(3, $"}}");
sb.AppendCode(2, $"}}");
return sb.ToString();
}
}
else
{
string flowResult = nameof(flowResult);
if (type == ParamType.Defute)
{
//sb.AppendCode(3, $"{contextApiFullName} {flowContext} = new {contextImpleFullName}({flowEnvironment}); // 创建上下文");
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({paramSignature})");
sb.AppendCode(2, $"{{");
sb.AppendCode(3, $"{contextApiFullName} {flowContext} = {flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Allocate)}(); // 从对象池获取一个上下文");
sb.AppendCode(3, $"{tokenSourceFullName} cts = new {tokenSourceFullName}(); // 创建取消令牌");
sb.AppendCode(3, $"try");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"{ReturnType.FullName} {flowResult} = await {ApiMethodName}({flowContext}, cts.{nameof(CancellationTokenSource.Token)}, {invokeParamSignature}); // 调用目标方法");
sb.AppendCode(4, $"return {flowResult};");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"catch (Exception)");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"throw;");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"finally");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"{flowContext}.{nameof(IFlowContext.Reset)}(); ");
sb.AppendCode(4, $"cts.{nameof(CancellationTokenSource.Dispose)}(); ");
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
sb.AppendCode(3, $"}}");
sb.AppendCode(2, $"}}");
return sb.ToString();
}
else if (type == ParamType.HasToken)
{
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({tokenFullName} {token}, {paramSignature})");
sb.AppendCode(2, $"{{");
sb.AppendCode(3, $"{contextApiFullName} {flowContext} = {flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Allocate)}(); // 从对象池获取一个上下文");
sb.AppendCode(3, $"try");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"{ReturnType.FullName} {flowResult} = await {ApiMethodName}({flowContext}, {token}, {invokeParamSignature}); // 调用目标方法");
sb.AppendCode(4, $"return {flowResult};");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"catch (Exception)");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"throw;");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"finally");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"{flowContext}.{nameof(IFlowContext.Reset)}(); ");
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
sb.AppendCode(3, $"}}");
sb.AppendCode(2, $"}}");
return sb.ToString();
}
else if (type == ParamType.HasContextAndToken)
{
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({contextApiFullName} {flowContext}, {tokenFullName} token, {paramSignature})");
sb.AppendCode(2, $"{{");
sb.AppendCode(3, $"token.ThrowIfCancellationRequested(); // 检查任务是否取消");
sb.AppendCode(3, $"global::{ParamTypeName} data = {ObjPoolName}.Get(); // 从对象池获取一个对象");
for (int index = 0; index < ParamInfos.Count; index++)
{
ParamInfo? info = ParamInfos[index];
sb.AppendCode(4, $"data.{info.ParamName.ToPascalCase()} = {info.ParamName}; // [{index}] {info.Comments}"); // 进行赋值
}
sb.AppendCode(3, $"{flowContext}.{nameof(IFlowContext.AddOrUpdate)}(\"{ApiMethodName}\", data);");
sb.AppendCode(3, $"{flowContext}.{nameof(IFlowContext.SetPreviousNode)}(\"{NodeModel.Guid}\", \"{ApiMethodName}\");");
sb.AppendCode(3, $"global::{typeof(CallNode).FullName} node = Get(\"{NodeModel.Guid}\");");
sb.AppendCode(3, $"global::{typeof(FlowResult).FullName} {flowResult} = await node.{nameof(CallNode.StartFlowAsync)}({flowContext}, {token}); // 调用目标方法");
if(ReturnType == typeof(object))
{
sb.AppendCode(3, $"if ({flowResult}.{nameof(FlowResult.Value)} is null)");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"return null;");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"else", isWrapping :false);
}
sb.AppendCode(3, $"if ({flowResult}.{nameof(FlowResult.Value)} is global::{ReturnType.FullName} result)");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"return result;");
sb.AppendCode(3, $"}}");
sb.AppendCode(3, $"else");
sb.AppendCode(3, $"{{");
sb.AppendCode(4, $"throw new ArgumentNullException($\"{{(flowResult.Value is null ? \"返回数据为 null\" : $\"返回数据与需求类型不匹配,当前返回类型为[{{flowResult.Value.GetType().FullName}}。\")}}\");");
sb.AppendCode(3, $"}}");
//sb.AppendCode(3, $"return {flowResult};");
sb.AppendCode(2, $"}}");
return sb.ToString();
// throw new ArgumentNullException($"类型转换失败,{(flowResult.Value is null ? "返回数据为 null" : $"返回数据与需求类型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}。")}");
}
}
throw new Exception();
}
}
}

View File

@@ -7,7 +7,7 @@ using System.ComponentModel;
using System.Net.Mime;
using System.Threading;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{

View File

@@ -1,7 +1,7 @@
using Serein.Library;
using Serein.Library.Api;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{

View File

@@ -2,7 +2,7 @@
using Serein.Library;
using System.Security.AccessControl;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{
/// <summary>
/// 单动作节点(用于动作控件)

View File

@@ -4,7 +4,7 @@ using Serein.Script;
using System.Dynamic;
using System.Linq.Expressions;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{
/// <summary>
/// 条件节点(用于条件控件)
@@ -177,7 +177,7 @@ namespace Serein.NodeFlow.Model
context.ExceptionOfRuning = ex;
}
SereinEnv.WriteLine(InfoType.INFO, $"{result} {Expression} -> " + context.NextOrientation);
SereinEnv.WriteLine(InfoType.INFO, $"{Expression} -> " + context.NextOrientation);
//return result;
return new FlowResult(this.Guid, context, judgmentResult);
}
@@ -236,19 +236,18 @@ namespace Serein.NodeFlow.Model
var dataName = nameof(data);
if (!expression.Equals(conditionExpression))
{
conditionExpression = expression;
conditionExpression = expression.Trim();
conditionScript = new SereinScript();
var dataType = data is null ? typeof(object) : data.GetType();
conditionExpression = expression.Trim();
if (expression[0] == '.')
{
// 对象取值
conditionExpression = $"return {dataName}{expression};";
conditionExpression = $"return {dataName}{conditionExpression};";
}
else
{
// 直接表达式
conditionExpression = $"return {dataName}.{expression};";
conditionExpression = $"return {dataName}.{conditionExpression};";
}
var resultType = conditionScript.ParserScript(conditionExpression, new Dictionary<string, Type>
{

View File

@@ -3,7 +3,7 @@ using Serein.Library.Api;
using Serein.Script;
using System.Dynamic;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{
/// <summary>
/// Expression Operation - 表达式操作

View File

@@ -3,13 +3,14 @@ using Serein.Library;
using Serein.Library.Utils;
using System;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{
/// <summary>
/// 触发器节点
/// </summary>
public class SingleFlipflopNode : NodeModelBase
{
public SingleFlipflopNode(IFlowEnvironment environment) : base(environment)
{

View File

@@ -4,7 +4,7 @@ using Serein.NodeFlow.Services;
using System.Dynamic;
using System.Reflection;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{
[NodeProperty(ValuePath = NodeValuePath.Node)]

View File

@@ -2,7 +2,7 @@
using Serein.Library.Api;
using System.Dynamic;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{
@@ -13,7 +13,7 @@ namespace Serein.NodeFlow.Model
public partial class SingleGlobalDataNode : NodeModelBase
{
/// <summary>
/// 表达式
/// 全局数据的Key名称
/// </summary>
[PropertyInfo(IsNotification = true)]
private string _keyName;
@@ -43,7 +43,7 @@ namespace Serein.NodeFlow.Model
/// <summary>
/// 数据来源的节点
/// </summary>
private IFlowNode? DataNode;
public IFlowNode? DataNode { get; private set; } = null;
/// <summary>
/// 有节点被放置
@@ -52,12 +52,27 @@ namespace Serein.NodeFlow.Model
/// <returns></returns>
public bool PlaceNode(IFlowNode nodeModel)
{
if(nodeModel.ControlType is not (NodeControlType.Action or NodeControlType.Script))
{
SereinEnv.WriteLine(InfoType.INFO, "放置在全局数据必须是有返回值的[Action]节点,[Script]节点。");
return false;
}
if (nodeModel.MethodDetails?.ReturnType is null
|| nodeModel.MethodDetails.ReturnType == typeof(void))
{
SereinEnv.WriteLine(InfoType.INFO, "放置在全局数据必须是有返回值的[Action]节点,[Script]节点。");
return false;
}
if(DataNode is null)
{
// 放置节点
nodeModel.ContainerNode = this;
ChildrenNode.Add(nodeModel);
DataNode = nodeModel;
MethodDetails.IsAsync = nodeModel.MethodDetails.IsAsync;
MethodDetails.ReturnType = nodeModel.MethodDetails.ReturnType;
return true;
}
else if (DataNode.Guid != nodeModel.Guid)

View File

@@ -8,7 +8,7 @@ using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{
[NodeProperty(ValuePath = NodeValuePath.Node)]

View File

@@ -13,7 +13,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{
[NodeProperty(ValuePath = NodeValuePath.Node)]
@@ -263,20 +263,6 @@ namespace Serein.NodeFlow.Model
{
if (token.IsCancellationRequested) return new FlowResult(this.Guid, context);
var @params = await flowCallNode.GetParametersAsync(context, token);
//if (token.IsCancellationRequested) return new FlowResult(this.Guid, context);
//context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改
/* if (IsScriptChanged)
{
lock (@params) {
if (IsScriptChanged)
{
ReloadScript();// 执行时检查是否需要重新解析
IsScriptChanged = false;
context.Env.WriteLine(InfoType.INFO, $"[{Guid}]脚本解析完成");
}
}
}*/
IScriptInvokeContext scriptContext = new ScriptInvokeContext(context);

View File

@@ -6,7 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model
namespace Serein.NodeFlow.Model.Nodes
{
public class SingleUINode : NodeModelBase
{

View File

@@ -172,40 +172,58 @@ namespace Serein.NodeFlow.Model.Operation
#region
bool checkTypeState = true;
List<ParameterDetails> toPds = new List<ParameterDetails>();
if(ToNode.MethodDetails.ParameterDetailss is null)
if (FromNode.ContainerNode is not null)
{
SereinEnv.WriteLine(InfoType.WARN, "目标节点没有入参参数,无法进行连接");
SereinEnv.WriteLine(InfoType.WARN, "连接失败,起始节点处于容器中");
return false;
}
if (ToNode.MethodDetails.ParameterDetailss.Length > 0)
if (ToNode.ContainerNode is not null)
{
var fromNoeReturnType = fromNode.MethodDetails.ReturnType;
if (fromNoeReturnType != null
&& fromNoeReturnType != typeof(object)
&& fromNoeReturnType != typeof(void)
&& fromNoeReturnType != typeof(Unit))
SereinEnv.WriteLine(InfoType.WARN, "连接失败,目标节点处于容器中");
return false;
}
if (ToNode.ControlType != NodeControlType.GlobalData)
{
if (ToNode.MethodDetails.ParameterDetailss is null)
{
var toNodePds = toNode.MethodDetails.ParameterDetailss;
foreach (ParameterDetails toNodePd in toNodePds)
SereinEnv.WriteLine(InfoType.WARN, "连接失败,目标节点没有入参参数。");
return false;
}
if (ToNode.MethodDetails.ParameterDetailss.Length > 0)
{
var fromNoeReturnType = fromNode.MethodDetails.ReturnType;
if (fromNoeReturnType != null
&& fromNoeReturnType != typeof(object)
&& fromNoeReturnType != typeof(void)
&& fromNoeReturnType != typeof(Unit))
{
if (string.IsNullOrWhiteSpace(toNodePd.ArgDataSourceNodeGuid) // 入参没有设置数据来源节点
&& toNodePd.DataType.IsAssignableFrom(fromNoeReturnType)) // 返回值与目标入参相同(或可转换为目标入参)
var toNodePds = toNode.MethodDetails.ParameterDetailss;
foreach (ParameterDetails toNodePd in toNodePds)
{
toPds.Add(toNodePd);
if (string.IsNullOrWhiteSpace(toNodePd.ArgDataSourceNodeGuid) // 入参没有设置数据来源节点
&& toNodePd.DataType.IsAssignableFrom(fromNoeReturnType)) // 返回值与目标入参相同(或可转换为目标入参)
{
toPds.Add(toNodePd);
}
}
if (toPds.Count == 0)
{
var any = toNodePds.Any(pd => pd.ArgDataSourceNodeGuid == fromNode.Guid); // 判断目标节点是否已有该节点的连接
checkTypeState = any;
}
else
{
checkTypeState = true; // 类型检查初步通过
}
}
if (toPds.Count == 0)
{
var any = toNodePds.Any(pd => pd.ArgDataSourceNodeGuid == fromNode.Guid); // 判断目标节点是否已有该节点的连接
checkTypeState = any;
}
else
{
checkTypeState = true; // 类型检查初步通过
}
}
}
if (!checkTypeState) // 类型检查不通过
{
SereinEnv.WriteLine(InfoType.ERROR, "创建失败,目标节点没有合适的入参接收返回值");
@@ -396,7 +414,11 @@ namespace Serein.NodeFlow.Model.Operation
SereinEnv.WriteLine(InfoType.WARN, $"连接失败,节点参数入参不允许接收多个节点返回值。起始节点[{FromNode.Guid}],目标节点[{FromNode.Guid}]。");
return false;
}
if (FromNode.ContainerNode is not null)
{
SereinEnv.WriteLine(InfoType.WARN, "连接失败,参数来源节点处于容器中");
return false;
}
// 判断是否建立过连接关系
if (FromNode.Guid == toNodeArgSourceGuid && toNodeArgSourceType == ConnectionArgSourceType)

View File

@@ -0,0 +1,211 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Infos;
using Serein.NodeFlow.Model.Nodes;
using System.Runtime.CompilerServices;
using System.Text;
namespace Serein.NodeFlow.Services
{
internal static class CoreGenerateExtension
{
/// <summary>
/// 生成流程接口信息描述
/// </summary>
/// <param name="flowCallNode"></param>
/// <returns></returns>
public static FlowApiMethodInfo? ToFlowApiMethodInfo(this SingleFlowCallNode flowCallNode)
{
var targetNode = flowCallNode.TargetNode;
if (targetNode.ControlType is not (NodeControlType.Action or NodeControlType.Script)) return null;
if (flowCallNode.MethodDetails is null) return null;
if (string.IsNullOrWhiteSpace(flowCallNode.ApiGlobalName)) return null;
FlowApiMethodInfo flowApiMethodInfo = new FlowApiMethodInfo(flowCallNode);
flowApiMethodInfo.ReturnType = targetNode.ControlType == NodeControlType.Script ? typeof(object)
: flowCallNode.MethodDetails.ReturnType;
flowApiMethodInfo.ApiMethodName = flowCallNode.ApiGlobalName;
List<FlowApiMethodInfo.ParamInfo> list = [];
int index = 0;
foreach (var pd in flowCallNode.MethodDetails.ParameterDetailss)
{
if (pd.DataType is null || string.IsNullOrWhiteSpace(pd.Name))
{
return null;
}
if (pd.IsParams)
{
list.Add(new FlowApiMethodInfo.ParamInfo(pd.DataType, $"{pd.Name}{index++}"));
}
else
{
list.Add(new FlowApiMethodInfo.ParamInfo(pd.DataType, pd.Name));
}
}
flowApiMethodInfo.ParamInfos = list;
return flowApiMethodInfo;
}
/// <summary>
/// 生成方法名称
/// </summary>
/// <param name="flowNode"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ToNodeMethodName(this IFlowNode flowNode)
{
/*if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
flowNode.MethodDetails.MethodName,
out var methodInfo))
{
throw new Exception();
}*/
var guid = flowNode.Guid;
var tmp = guid.Replace("-", "");
var methodName = $"FlowMethod_{tmp}";
return methodName;
}
/// <summary>
/// 生成完全的xml注释
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static string ToXmlComments(this string context, int retractCount = 0)
{
StringBuilder sb = new StringBuilder();
var startLine = "/// <summary>";
var endLine = "/// </summary>";
sb.AppendLine(startLine);
var rows = context.Split(Environment.NewLine);
string retract = new string(' ', retractCount * 4);
foreach (var row in rows)
{
// 处理转义
var value = row.Replace("<", "&lt")
.Replace(">", "&gt");
sb.AppendLine($"{retract}/// <para>{value}</para>");
}
sb.AppendLine(endLine);
return sb.ToString();
}
/// <summary>
/// 生成类型的驼峰命名法名称(首字母小写)
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ToCamelCase(this Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
// 获取类型名称(不包括命名空间)
string typeName = type.Name;
if (string.IsNullOrEmpty(typeName))
{
return string.Empty;
}
// 处理泛型类型(去掉后面的`N
int indexOfBacktick = typeName.IndexOf('`');
if (indexOfBacktick > 0)
{
typeName = typeName.Substring(0, indexOfBacktick);
}
// 如果是接口且以"I"开头,去掉第一个字母
if (type.IsInterface && typeName.Length > 1 && typeName[0] == 'I' && char.IsUpper(typeName[1]))
{
typeName = typeName.Substring(1);
}
// 转换为驼峰命名法:首字母小写,其余不变
if (typeName.Length > 0)
{
return char.ToLowerInvariant(typeName[0]) + typeName.Substring(1);
}
return typeName;
}
/// <summary>
/// 生成类型的大驼峰命名法名称PascalCase
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ToPascalCase(this Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
string typeName = type.Name;
if (string.IsNullOrEmpty(typeName))
{
return string.Empty;
}
// 去掉泛型标记(如 `1
int indexOfBacktick = typeName.IndexOf('`');
if (indexOfBacktick > 0)
{
typeName = typeName.Substring(0, indexOfBacktick);
}
// 如果是接口以 I 开头,且后面是大写,去掉前缀 I
if (type.IsInterface && typeName.Length > 1 && typeName[0] == 'I' && char.IsUpper(typeName[1]))
{
typeName = typeName.Substring(1);
}
// 首字母转为大写(如果有需要)
if (typeName.Length > 0)
{
return char.ToUpperInvariant(typeName[0]) + typeName.Substring(1);
}
return typeName;
}
/// <summary>
/// 将字符串首字母大写PascalCase
/// </summary>
/// <param name="text">原始文本</param>
/// <returns>首字母大写的文本</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ToPascalCase(this string text)
{
if (string.IsNullOrEmpty(text))
{
return string.Empty;
}
if (char.IsUpper(text[0]))
{
return text; // 已是大写
}
return char.ToUpperInvariant(text[0]) + text.Substring(1);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
namespace Serein.NodeFlow.Services
{

View File

@@ -3,6 +3,7 @@ using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.NodeFlow.Tool;
using System;
using System.Collections.Concurrent;
@@ -285,7 +286,7 @@ namespace Serein.NodeFlow.Services
checkpoints["执行流程"] = sw.Elapsed;
if (context.IsRecordInvokeInfo)
if (context.IsRecordInvokeInfo && false)
{
var invokeInfos = context.GetAllInvokeInfos();
_ = Task.Delay(100).ContinueWith(async (task) =>
@@ -309,9 +310,7 @@ namespace Serein.NodeFlow.Services
SereinEnv.WriteLine(InfoType.INFO, $"平均耗时:{total / invokeInfos.Count}");
SereinEnv.WriteLine(InfoType.INFO, $"总耗时:{total}");
}
});
});
}
@@ -324,6 +323,7 @@ namespace Serein.NodeFlow.Services
_ = Task.Run(() =>
{
//var checkpoints = new Dictionary<string, TimeSpan>();
var last = TimeSpan.Zero;
foreach (var kv in checkpoints)
{

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Serein.Script.Node
{
@@ -22,6 +23,13 @@ namespace Serein.Script.Node
public ASTNode Value { get; }
public AssignmentNode(ASTNode target, ASTNode value) => (Target, Value) = (target, value);
public override string ToString()
{
return $"{Target} = {Value}";
}
}

View File

@@ -33,5 +33,10 @@ namespace Serein.Script.Node
Operator = op;
Right = right;
}
public override string ToString()
{
return $"({Left} {Operator} {Right})";
}
}
}

View File

@@ -30,7 +30,11 @@ namespace Serein.Script.Node
this.ClassType = className;
}
public override string ToString()
{
var p = string.Join(",", Propertys.Select(p => $"{p.Value}"));
return $"{ClassType}({p})";
}
/* /// <summary>

View File

@@ -26,6 +26,12 @@ namespace Serein.Script.Node
this.Collection = Collection;
this.Index = indexValue;
}
public override string ToString()
{
return $"{Collection}[{Index}]";
}
}
/// <summary>
@@ -48,5 +54,10 @@ namespace Serein.Script.Node
this.Collection = collection;
this.Value = value;
}
public override string ToString()
{
return $"{Collection} = {Value}";
}
}
}

View File

@@ -41,6 +41,9 @@ namespace Serein.Script.Node
Value = value;
}
public override string ToString()
{
return $"ctor {Class}.{MemberName} = {Value}";
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
@@ -26,6 +27,12 @@ namespace Serein.Script.Node
FunctionName = functionName;
Arguments = arguments;
}
public override string ToString()
{
var p = string.Join(",", Arguments.Select(p => $"{p}"));
return $"{FunctionName}({p})";
}
}
}

View File

@@ -16,5 +16,11 @@ namespace Serein.Script.Node
/// </summary>
public string Name { get; }
public IdentifierNode(string name) => Name = name;
public override string ToString()
{
return $"let {Name}";
}
}
}

View File

@@ -26,5 +26,10 @@ namespace Serein.Script.Node
Object = obj;
MemberName = memberName;
}
public override string ToString()
{
return $"{Object}.{MemberName}";
}
}
}

View File

@@ -31,5 +31,10 @@ namespace Serein.Script.Node
MemberName = memberName;
Value = value;
}
public override string ToString()
{
return $"{Object}.{MemberName} = {Value}";
}
}
}

View File

@@ -32,5 +32,12 @@ namespace Serein.Script.Node
FunctionName = functionName;
Arguments = arguments;
}
public override string ToString()
{
var p = string.Join(",", Arguments.Select(p => $"{p}"));
return $"{Object}.{FunctionName}({p})";
}
}
}

View File

@@ -37,6 +37,13 @@ namespace Serein.Script.Node
CtorAssignments = ctorAssignments;
return this;
}
public override string ToString()
{
var arg = string.Join(",", Arguments.Select(p => $"{p}"));
var ctor_arg = string.Join(",", CtorAssignments.Select(p => $"{p}"));
return $"new {Type}({arg}){ctor_arg}";
}
}
}

View File

@@ -18,5 +18,9 @@ namespace Serein.Script.Node
TypeName = typeName;
}
public override string ToString()
{
return $"[type]{TypeName}";
}
}
}

View File

@@ -13,5 +13,10 @@ namespace Serein.Script.Node
{
public bool Value { get; }
public BooleanNode(bool value) => Value = value;
public override string ToString()
{
return $"{Value}";
}
}
}

View File

@@ -13,5 +13,9 @@ namespace Serein.Script.Node
{
Value = char.Parse(value);
}
public override string ToString()
{
return $"'{Value}'";
}
}
}

View File

@@ -11,5 +11,9 @@ namespace Serein.Script.Node
/// </summary>
public class NullNode : ASTNode
{
public override string ToString()
{
return $"Null";
}
}
}

View File

@@ -13,6 +13,11 @@ namespace Serein.Script.Node
{
public T Value { get; }
public NumberNode(T value) => Value = value;
public override string ToString()
{
return $"{Value}";
}
}

View File

@@ -55,6 +55,11 @@ namespace Serein.Script.Node
}
Value = output.ToString();
}
public override string ToString()
{
return $"\"{Value}\"";
}
}

View File

@@ -8,9 +8,6 @@ namespace Serein.Script
public class SereinScript
{
/// <summary>
/// 类型分析
/// </summary>

View File

@@ -1,21 +1,53 @@
namespace Serein.Script
{
/// <summary>
/// 脚本方法信息
/// </summary>
public class SereinScriptMethodInfo
{
/// <summary>
/// 类名
/// </summary>
public string ClassName { get; set; }
/// <summary>
/// 方法名
/// </summary>
public string MethodName { get; set; }
/// <summary>
/// 返回类型
/// </summary>
public Type? ReturnType { get; set; }
/// <summary>
/// 是否异步
/// </summary>
public bool IsAsync { get; set; }
/// <summary>
/// 入参参数信息
/// </summary>
public List<SereinScriptParamInfo> ParamInfos { get; set; }
/// <summary>
/// 对应的C#代码
/// </summary>
public string CsharpCode { get; set; }
/// <summary>
/// 入参信息
/// </summary>
public class SereinScriptParamInfo
{
/// <summary>
/// 入参参数名称
/// </summary>
public string ParamName { get; set; }
/// <summary>
/// 入参类型
/// </summary>
public Type ParameterType { get; set; }
}
}

View File

@@ -1,4 +1,5 @@
using Serein.Script.Node;
using Serein.Library.Utils;
using Serein.Script.Node;
using Serein.Script.Node.FlowControl;
using System.Text;
@@ -88,20 +89,38 @@ namespace Serein.Script
Indent();
if(param is null || param.Count == 0)
{
// 生成方法签名
AppendLine($"public static {returnContent} {mehtodName}()");
}
else
{
// 生成方法签名
AppendLine($"public static {returnContent} {mehtodName}({GetMethodParamster(param)})");
}
AppendLine( "{");
Indent();
foreach (var stmt in programNode.Statements)
// 生成变量节点
var idfNodesTemp = _symbolInfos.Keys.Where(key => key is IdentifierNode)
.OfType<IdentifierNode>().ToList() ;
var idfNodes = (param is null) switch
{
ConvertCode(stmt); // 递归遍历
true => idfNodesTemp.DistinctBy(n => n.Name).ToList(),
false => idfNodesTemp.DistinctBy(n => n.Name).DistinctByCondition(n => !param.ContainsKey(n.Name) ).ToList(),
};
foreach (var idf in idfNodes)
{
var varName = idf.Name;
var varType = _symbolInfos[idf];
AppendLine($"global::{varType.FullName} {varName} = default; // 变量");
}
AppendLine("");
// 递归遍历节点生成代码
foreach (var stmt in programNode.Statements)
{
ConvertCode(stmt);
Append(";");
}
if (_symbolInfos[programNode] == typeof(void))
{
AppendLine("");
@@ -192,8 +211,8 @@ namespace Serein.Script
void ConvertCodeOfIdentifierNode(IdentifierNode identifierNode)
{
var varName = identifierNode.Name;
if(_local.TryGetValue(varName, out var type))
Append(varName);
/*if (_local.TryGetValue(varName, out var type))
{
// 定义过,需要使用变量
Append(varName);
@@ -211,7 +230,7 @@ namespace Serein.Script
{
throw new Exception($"加载符号表时,无法匹配 IdentifierNode 节点的类型。 name {varName}");
}
}
}*/
}
ConvertCodeOfIdentifierNode(identifierNode);
break;
@@ -227,7 +246,6 @@ namespace Serein.Script
foreach(var item in ifNOde.TrueBranch)
{
ConvertCode(item);
//Append(";");
AppendLine(string.Empty);
}
Unindent();
@@ -235,10 +253,9 @@ namespace Serein.Script
AppendLine("else");
AppendLine("{");
Indent();
foreach (var item in ifNOde.TrueBranch)
foreach (var item in ifNOde.FalseBranch)
{
ConvertCode(item);
//Append(";");
AppendLine(string.Empty);
}
Unindent();

View File

@@ -198,7 +198,7 @@ namespace Serein.Script
var targetType = Analysis(assignmentNode.Target);
var valueType = Analysis (assignmentNode.Value);
if (!targetType.IsAssignableFrom(valueType))
throw new Exception($"索引类型不匹配:需要 {targetType},实际为 {valueType}");
throw new Exception($"赋值类型不匹配:需要 {targetType},实际为 {valueType}");
NodeSymbolInfos[assignmentNode.Value] = valueType;
NodeSymbolInfos[assignmentNode.Target] = valueType;
NodeSymbolInfos[assignmentNode] = typeof(void); // 赋值语句不产生类型

View File

@@ -1,5 +1,6 @@
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Node.ViewModel;
namespace Serein.Workbench.Node.View

View File

@@ -1,5 +1,6 @@
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Node.ViewModel;
namespace Serein.Workbench.Node.View

View File

@@ -1,6 +1,7 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Node.ViewModel;
using System.Windows;
using System.Windows.Controls;

View File

@@ -1,5 +1,6 @@
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Api;
using Serein.Workbench.Node.ViewModel;

View File

@@ -1,5 +1,6 @@
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Node.ViewModel;
using System;
using System.Collections.Generic;
@@ -27,8 +28,10 @@ namespace Serein.Workbench.Node.View
{
var env = App.GetService<IFlowEnvironment>();
base.ViewModel = new NetScriptNodeControlViewModel(new SingleNetScriptNode(env));
base.ViewModel.IsEnabledOnView = false;
base.ViewModel = new NetScriptNodeControlViewModel(new SingleNetScriptNode(env))
{
IsEnabledOnView = false
};
base.DataContext = ViewModel;
InitializeComponent();
}

View File

@@ -1,5 +1,6 @@
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Node.ViewModel;
using System;
using System.Collections.Generic;

View File

@@ -1,4 +1,5 @@
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Node.View;
namespace Serein.Workbench.Node.ViewModel

View File

@@ -1,4 +1,5 @@
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Node.View;
namespace Serein.Workbench.Node.ViewModel

View File

@@ -1,4 +1,5 @@
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Node.View;
namespace Serein.Workbench.Node.ViewModel

View File

@@ -1,4 +1,5 @@
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Node.View;
namespace Serein.Workbench.Node.ViewModel

View File

@@ -2,6 +2,7 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Api;
using Serein.Workbench.Services;
using Serein.Workbench.ViewModels;

View File

@@ -1,4 +1,5 @@
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using System.Windows;
using System.Windows.Input;

View File

@@ -1,5 +1,6 @@
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Library;
using Serein.NodeFlow.Model.Nodes;
using Serein.Workbench.Themes;
using System.Windows.Input;
@@ -61,6 +62,7 @@ public class FlowLibrary
// nodeModel.Env.WriteLine(InfoType.ERROR, ex.ToString());
//}
});
NodeModel1 = nodeModel;
}
private static void OnCompileComplete(FlowLibraryCache flowLibrary)
@@ -84,7 +86,6 @@ public class FlowLibrary
/// 打开编辑窗口
/// </summary>
public ICommand CommandOpenScriptEdit { get; }
public NodeModelBase NodeModel1 { get; }
}
}

View File

@@ -1,6 +1,7 @@
using Serein.Library;
using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using System;
using System.Collections.Generic;
using System.Diagnostics;

View File

@@ -2,6 +2,7 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using System;
using System.Collections.Generic;
using System.Linq;