diff --git a/Library/FlowNode/FlowContext.cs b/Library/FlowNode/FlowContext.cs
index e11ebc4..9e48a7e 100644
--- a/Library/FlowNode/FlowContext.cs
+++ b/Library/FlowNode/FlowContext.cs
@@ -27,13 +27,15 @@ namespace Serein.Library
RunState = RunState.Running;
}
- private string _guid = global::System.Guid.NewGuid().ToString();
-
///
/// 是否记录流程调用信息
///
public bool IsRecordInvokeInfo { get; set; } = true;
- string IFlowContext.Guid => _guid;
+
+ ///
+ /// 标识流程的Guid
+ ///
+ public string Guid { get; private set; } = global::System.Guid.NewGuid().ToString();
///
/// 运行环境
@@ -97,7 +99,7 @@ namespace Serein.Library
FlowInvokeInfo flowInvokeInfo = new FlowInvokeInfo
{
- ContextGuid = this._guid,
+ ContextGuid = this.Guid,
Id = id,
PreviousNodeGuid = previousNode?.Guid,
Method = theNode.MethodDetails?.MethodName,
@@ -258,7 +260,7 @@ namespace Serein.Library
NextOrientation = ConnectionInvokeType.None;
RunState = RunState.Running;
flowInvokeInfos.Clear();
- _guid = global::System.Guid.NewGuid().ToString();
+ Guid = global::System.Guid.NewGuid().ToString();
}
///
diff --git a/NodeFlow/Model/Nodes/SingleExpOpNode.cs b/NodeFlow/Model/Nodes/SingleExpOpNode.cs
index de1cd92..2e7397c 100644
--- a/NodeFlow/Model/Nodes/SingleExpOpNode.cs
+++ b/NodeFlow/Model/Nodes/SingleExpOpNode.cs
@@ -69,6 +69,7 @@ namespace Serein.NodeFlow.Model.Nodes
};
this.MethodDetails.ParameterDetailss = [.. pd];
+ this.MethodDetails.ReturnType = typeof(object);
}
///
@@ -103,14 +104,12 @@ namespace Serein.NodeFlow.Model.Nodes
{
if(token.IsCancellationRequested) return FlowResult.Fail(this.Guid, context, "流程已通过token取消");
- object? parameter = null;// context.TransmissionData(this); // 表达式节点使用上一节点数据
+ object? parameter = null;// 表达式节点使用上一节点数据
var pd = MethodDetails.ParameterDetailss[0];
var hasNode = context.Env.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var argSourceNode);
if (!hasNode)
{
- /*context.NextOrientation = ConnectionInvokeType.IsError;
- return new FlowResult(this.Guid, context);*/
parameter = null;
}
else
@@ -130,14 +129,6 @@ namespace Serein.NodeFlow.Model.Nodes
parameter = result.Value;
}
}
-
- /*
- else
- {
- // 条件节点透传上一节点的数据
- parameter = context.TransmissionData(this.Guid);
- }
-*/
try
{
var result = await GetValueExpressionAsync(context, parameter, Expression);
@@ -179,17 +170,18 @@ namespace Serein.NodeFlow.Model.Nodes
// 表达式默认包含 “.”
expression = $"return {expression};";
}
- var resultType = getValueScript .ParserScript(expression, new Dictionary
+ var resultType = getValueScript.ParserScript(expression, new Dictionary
{
{ dataName, dataType},
});
+
}
IScriptInvokeContext scriptContext = new ScriptInvokeContext(flowContext);
scriptContext.SetVarValue(dataName, data);
- var result = await getValueScript .InterpreterAsync(scriptContext);
+ var result = await getValueScript.InterpreterAsync(scriptContext);
return result;
}
diff --git a/NodeFlow/Model/Nodes/SingleScriptNode.cs b/NodeFlow/Model/Nodes/SingleScriptNode.cs
index e7700ca..e4f4227 100644
--- a/NodeFlow/Model/Nodes/SingleScriptNode.cs
+++ b/NodeFlow/Model/Nodes/SingleScriptNode.cs
@@ -49,6 +49,7 @@ namespace Serein.NodeFlow.Model.Nodes
public SingleScriptNode(IFlowEnvironment environment) : base(environment)
{
sereinScript = new SereinScript();
+
}
static SingleScriptNode()
@@ -78,9 +79,9 @@ namespace Serein.NodeFlow.Model.Nodes
IsScriptChanged = true;
}
- ///
- /// 节点创建时
- ///
+ ///
+ /// 节点创建时
+ ///
public override void OnCreating()
{
var md = MethodDetails;
@@ -106,6 +107,38 @@ namespace Serein.NodeFlow.Model.Nodes
}
+ ///
+ /// 更新节点返回类型
+ ///
+ ///
+ private void UploadNodeReturnType(Type newType)
+ {
+ MethodDetails.ReturnType = newType;
+
+
+ foreach (var ct in NodeStaticConfig.ConnectionArgSourceTypes)
+ {
+ var newResultNodes = NeedResultNodes[ct].ToArray();
+ foreach (var node in newResultNodes)
+ {
+ if(node is SingleScriptNode scriptNode)
+ {
+ var pds = scriptNode.MethodDetails.ParameterDetailss;
+ foreach (var pd in pds)
+ {
+ if (pd.ArgDataSourceType == ct &&
+ pd.ArgDataSourceNodeGuid == this.Guid)
+ {
+ pd.DataType = newType; // 更新参数类型
+ }
+ }
+ //scriptNode.ReloadScript(); // 重新加载目标脚本节点
+ }
+
+ }
+ }
+ }
+
///
/// 保存项目时保存脚本代码、方法入参类型、返回值类型
///
@@ -181,58 +214,77 @@ namespace Serein.NodeFlow.Model.Nodes
}
-
-
-
+ private object reloadLockObj = new object();
///
/// 重新加载脚本代码
///
public bool ReloadScript()
{
- try
+ lock (reloadLockObj)
{
- HashSet varNames = new HashSet();
- foreach (var pd in MethodDetails.ParameterDetailss)
+ if (!CheckRepeatParamter())
+ return false;
+ var argTypes = GetParamterTypeInfo();
+ try
{
- if (varNames.Contains(pd.Name))
- {
- throw new Exception($"脚本节点重复的变量名称:{pd.Name} - {Guid}");
- }
- varNames.Add(pd.Name);
+ var returnType = sereinScript.ParserScript(Script, argTypes); // 开始解析获取程序主节点
+ UploadNodeReturnType(returnType);
+ }
+ catch (Exception ex)
+ {
+ SereinEnv.WriteLine(InfoType.WARN, ex.Message);
+ return false; // 解析失败
}
-
- var argTypes = MethodDetails.ParameterDetailss
- .Select(pd =>
- {
- if (pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
- {
- var Type = pd.DataType;
- return (pd.Name, Type);
- }
- if (Env.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var node) &&
- node.MethodDetails?.ReturnType is not null)
- {
- pd.DataType = node.MethodDetails.ReturnType;
- var Type = node.MethodDetails.ReturnType;
- return (pd.Name, Type);
- }
- return default;
- })
- .Where(x => x != default)
- .ToDictionary(x => x.Name, x => x.Type); // 准备预定义类型
-
-
- var returnType = sereinScript.ParserScript(Script, argTypes); // 开始解析获取程序主节点
- MethodDetails.ReturnType = returnType;
return true;
}
- catch (Exception ex)
- {
- SereinEnv.WriteLine(InfoType.WARN, ex.Message);
- return false; // 解析失败
- }
}
-
+
+ ///
+ /// 检查脚本参数是否有重复的名称
+ ///
+ ///
+ private bool CheckRepeatParamter()
+ {
+ bool isSueccess = true;
+ HashSet varNames = new HashSet();
+ foreach (var pd in MethodDetails.ParameterDetailss)
+ {
+ if (varNames.Contains(pd.Name))
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, $"脚本节点重复的变量名称:{pd.Name} - {Guid}");
+
+ isSueccess = false;
+ }
+ varNames.Add(pd.Name);
+ }
+ return isSueccess;
+ }
+
+ ///
+ /// 获取参数类型信息
+ ///
+ ///
+ private Dictionary GetParamterTypeInfo()
+ {
+ Dictionary argTypes = [];
+ var pds = MethodDetails.ParameterDetailss.ToArray();
+ foreach (var pd in pds)
+ {
+ if (pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
+ {
+ argTypes[pd.Name] = pd.DataType;
+ }
+ if (Env.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var node) &&
+ node.MethodDetails?.ReturnType is not null)
+ {
+ pd.DataType = node.MethodDetails.ReturnType;
+ var Type = node.MethodDetails.ReturnType;
+ argTypes[pd.Name] = Type;
+ }
+ }
+ return argTypes;
+ }
+
///
/// 转换为 C# 代码,并且附带方法信息
///
@@ -246,39 +298,11 @@ namespace Serein.NodeFlow.Model.Nodes
methodName = $"FlowMethod_{tmp}";
}
- HashSet varNames = new HashSet();
- foreach (var pd in MethodDetails.ParameterDetailss)
- {
- if (varNames.Contains(pd.Name))
- {
- throw new Exception($"脚本节点重复的变量名称:{pd.Name} - {Guid}");
- }
- varNames.Add(pd.Name);
- }
-
- var argTypes = MethodDetails.ParameterDetailss
- .Select(pd =>
- {
- if(pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
- {
- var Type = pd.DataType;
- return (pd.Name, Type);
- }
- if (Env.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var node) &&
- node.MethodDetails?.ReturnType is not null)
- {
- pd.DataType = node.MethodDetails.ReturnType;
- var Type = node.MethodDetails.ReturnType;
- return (pd.Name, Type);
- }
- return default;
- })
- .Where(x => x != default)
- .ToDictionary(x => x.Name, x => x.Type); // 准备预定义类型
-
-
+ if (!CheckRepeatParamter())
+ throw new Exception($"脚本节点入参重复");
+ var argTypes = GetParamterTypeInfo();
var returnType = sereinScript.ParserScript(Script, argTypes); // 开始解析获取程序主节点
- MethodDetails.ReturnType = returnType;
+ UploadNodeReturnType(returnType);
var scriptMethodInfo = sereinScript.ConvertCSharpCode(methodName, argTypes);
return scriptMethodInfo;
}
@@ -297,6 +321,13 @@ namespace Serein.NodeFlow.Model.Nodes
///
public override async Task ExecutingAsync(IFlowContext context, CancellationToken token)
{
+ if (IsScriptChanged)
+ {
+ if (!ReloadScript())
+ {
+ return FlowResult.Fail(this.Guid, context, "脚本解析失败,请检查脚本代码");
+ }
+ }
var result = await ExecutingAsync(this, context, token);
return result;
}
@@ -311,6 +342,14 @@ namespace Serein.NodeFlow.Model.Nodes
public async Task ExecutingAsync(NodeModelBase flowCallNode, IFlowContext context, CancellationToken token)
{
if (token.IsCancellationRequested) return FlowResult.Fail(this.Guid, context, "流程已通过token取消");
+ if (IsScriptChanged)
+ {
+ if (!ReloadScript())
+ {
+ return FlowResult.Fail(this.Guid, context, "脚本解析失败,请检查脚本代码");
+ }
+ }
+
var @params = await flowCallNode.GetParametersAsync(context, token);
IScriptInvokeContext scriptContext = new ScriptInvokeContext(context);
diff --git a/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs b/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs
index 31e4f55..4f3e63b 100644
--- a/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs
+++ b/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs
@@ -189,6 +189,7 @@ namespace Serein.NodeFlow.Model.Operations
return false;
}
+
if (ToNode.ControlType is not (NodeControlType.GlobalData or NodeControlType.ExpCondition or NodeControlType.ExpOp))
{
@@ -199,6 +200,7 @@ namespace Serein.NodeFlow.Model.Operations
}
if (ToNode.MethodDetails.ParameterDetailss.Length > 0)
{
+
var fromNoeReturnType = fromNode.MethodDetails.ReturnType;
if (fromNoeReturnType != null
&& fromNoeReturnType != typeof(object)
@@ -206,24 +208,33 @@ namespace Serein.NodeFlow.Model.Operations
&& fromNoeReturnType != typeof(Unit))
{
var toNodePds = toNode.MethodDetails.ParameterDetailss;
- foreach (ParameterDetails toNodePd in toNodePds)
+ if (toNodePds.Any(pd=>pd.IsExplicitData))
{
- 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;
+ checkTypeState = true; // 目标节点使用了显式的入参,无需关心参数是否匹配
}
else
{
- checkTypeState = true; // 类型检查初步通过
+ foreach (ParameterDetails toNodePd in toNodePds)
+ {
+ 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; // 类型检查初步通过
+ }
}
+
}
}
}
diff --git a/Serein.Script/Node/ValueNode/NumberNode.cs b/Serein.Script/Node/ValueNode/NumberNode.cs
index 7ad7162..13fc133 100644
--- a/Serein.Script/Node/ValueNode/NumberNode.cs
+++ b/Serein.Script/Node/ValueNode/NumberNode.cs
@@ -27,23 +27,23 @@ namespace Serein.Script.Node
public class NumberIntNode(int vlaue) : NumberNode(vlaue)
{
}
-
+
///
- /// int 整数型字面量
+ /// long 整数型字面量
///
public class NumberLongNode(long vlaue) : NumberNode(vlaue)
{
}
-
+
///
- /// int 整数型字面量
+ /// float 字面量
///
public class NumberFloatNode(float vlaue) : NumberNode(vlaue)
{
}
-
+
///
- /// int 整数型字面量
+ /// double 字面量
///
public class NumberDoubleNode(double vlaue) : NumberNode(vlaue)
{
diff --git a/Serein.Script/SereinScriptInterpreter.cs b/Serein.Script/SereinScriptInterpreter.cs
index dabdccf..171aba2 100644
--- a/Serein.Script/SereinScriptInterpreter.cs
+++ b/Serein.Script/SereinScriptInterpreter.cs
@@ -321,11 +321,11 @@ namespace Serein.Script
async Task