mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-02 15:50:47 +08:00
修复了全局节点连接异常异常。
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,3 +58,5 @@ namespace Serein.Library
|
||||
public double ScaleY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
/// 实例化返回值
|
||||
|
||||
@@ -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; // 流程执行完成,返回结果
|
||||
}
|
||||
|
||||
|
||||
@@ -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, // 拷贝
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -42,7 +42,8 @@ namespace Serein.Library.Utils
|
||||
|
||||
public ObjectPool(Factory factory)
|
||||
: this(factory, Environment.ProcessorCount * 2)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
public ObjectPool(Factory factory, int size)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
353
NodeFlow/Model/Infos/FlowApiMethodInfo.cs
Normal file
353
NodeFlow/Model/Infos/FlowApiMethodInfo.cs
Normal 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();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using System.ComponentModel;
|
||||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using Serein.Library;
|
||||
using System.Security.AccessControl;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
/// <summary>
|
||||
/// 单动作节点(用于动作控件)
|
||||
|
||||
@@ -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>
|
||||
{
|
||||
|
||||
@@ -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 - 表达式操作
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
211
NodeFlow/Services/CoreGenerateExtension.cs
Normal file
211
NodeFlow/Services/CoreGenerateExtension.cs
Normal 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("<", "<")
|
||||
.Replace(">", ">");
|
||||
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
@@ -1,6 +1,7 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
|
||||
namespace Serein.NodeFlow.Services
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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}";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,5 +33,10 @@ namespace Serein.Script.Node
|
||||
Operator = op;
|
||||
Right = right;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"({Left} {Operator} {Right})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@ namespace Serein.Script.Node
|
||||
Value = value;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"ctor {Class}.{MemberName} = {Value}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,5 +26,10 @@ namespace Serein.Script.Node
|
||||
Object = obj;
|
||||
MemberName = memberName;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Object}.{MemberName}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,5 +31,10 @@ namespace Serein.Script.Node
|
||||
MemberName = memberName;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Object}.{MemberName} = {Value}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,5 +18,9 @@ namespace Serein.Script.Node
|
||||
TypeName = typeName;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[type]{TypeName}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,10 @@ namespace Serein.Script.Node
|
||||
{
|
||||
public bool Value { get; }
|
||||
public BooleanNode(bool value) => Value = value;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Value}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,9 @@ namespace Serein.Script.Node
|
||||
{
|
||||
Value = char.Parse(value);
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return $"'{Value}'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,5 +11,9 @@ namespace Serein.Script.Node
|
||||
/// </summary>
|
||||
public class NullNode : ASTNode
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Null";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ namespace Serein.Script.Node
|
||||
{
|
||||
public T Value { get; }
|
||||
public NumberNode(T value) => Value = value;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Value}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ namespace Serein.Script.Node
|
||||
}
|
||||
Value = output.ToString();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"\"{Value}\"";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,9 +8,6 @@ namespace Serein.Script
|
||||
|
||||
public class SereinScript
|
||||
{
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 类型分析
|
||||
/// </summary>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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); // 赋值语句不产生类型
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.Workbench.Node.View;
|
||||
|
||||
namespace Serein.Workbench.Node.ViewModel
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.Workbench.Node.View;
|
||||
|
||||
namespace Serein.Workbench.Node.ViewModel
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.Workbench.Node.View;
|
||||
|
||||
namespace Serein.Workbench.Node.ViewModel
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.Workbench.Node.View;
|
||||
|
||||
namespace Serein.Workbench.Node.ViewModel
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user