diff --git a/Library/Api/IFlowEdit.cs b/Library/Api/IFlowEdit.cs
index e848a65..b8aa2d1 100644
--- a/Library/Api/IFlowEdit.cs
+++ b/Library/Api/IFlowEdit.cs
@@ -69,12 +69,12 @@ namespace Serein.Library.Api
/// 决定了方法参数来源
/// 设置第几个参数
void ConnectArgSourceNode(string canvasGuid,
- string fromNodeGuid,
- string toNodeGuid,
- JunctionType fromNodeJunctionType,
- JunctionType toNodeJunctionType,
- ConnectionArgSourceType argSourceType,
- int argIndex);
+ string fromNodeGuid,
+ string toNodeGuid,
+ JunctionType fromNodeJunctionType,
+ JunctionType toNodeJunctionType,
+ ConnectionArgSourceType argSourceType,
+ int argIndex);
///
/// 移除两个节点之间的方法调用关系
diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs
index 23afb31..222e7c4 100644
--- a/Library/Api/IFlowEnvironment.cs
+++ b/Library/Api/IFlowEnvironment.cs
@@ -288,11 +288,11 @@ namespace Serein.Library.Api
///
/// 连接类型
///
- public ConnectionInvokeType ConnectionInvokeType { get;}
+ public ConnectionInvokeType ConnectionInvokeType { get; } = ConnectionInvokeType.None;
///
/// 表示此次需要在两个节点之间创建连接关系,或是移除连接关系
///
- public ConnectChangeType ChangeType { get;}
+ public ConnectChangeType ChangeType { get;}
///
/// 指示需要创建什么类型的连接线
///
@@ -300,7 +300,7 @@ namespace Serein.Library.Api
///
/// 节点对应的方法入参所需参数来源
///
- public ConnectionArgSourceType ConnectionArgSourceType { get;}
+ public ConnectionArgSourceType ConnectionArgSourceType { get;}
///
/// 第几个参数
///
diff --git a/Library/FlowNode/MethodDetails.cs b/Library/FlowNode/MethodDetails.cs
index 7269b59..d407fdd 100644
--- a/Library/FlowNode/MethodDetails.cs
+++ b/Library/FlowNode/MethodDetails.cs
@@ -105,6 +105,11 @@ namespace Serein.Library
///
public bool HasParamsArg => _paramsArgIndex >= 0;
+ ///
+ /// 是否为静态方法
+ ///
+ public bool IsStatic { get; set; }
+
///
/// 新增可变参数
///
@@ -119,10 +124,13 @@ namespace Serein.Library
return false;
}
+
var newPd = ParameterDetailss[index].CloneOfModel(this.NodeModel); // 复制出属于本身节点的参数描述
newPd.Index = ParameterDetailss.Length; // 更新索引
newPd.IsParams = true;
ParameterDetailss = ArrayHelper.AddToArray(ParameterDetailss, newPd); // 新增
+
+
return true;
}
@@ -140,6 +148,7 @@ namespace Serein.Library
parameterDetails.Index = ParameterDetailss.Length; // 更新索引
parameterDetails.IsParams = true;
ParameterDetailss = ArrayHelper.AddToArray(ParameterDetailss, parameterDetails); // 新增
+
return true;
}
@@ -299,6 +308,7 @@ namespace Serein.Library
ParamsArgIndex = this.ParamsArgIndex, // 拷贝
ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(), // 拷贝属于节点方法的新入参描述
IsAsync = this.IsAsync, // 拷贝
+ IsStatic = this.IsStatic, // 拷贝
};
return md;
diff --git a/Library/SereinBaseFunction.cs b/Library/SereinBaseFunction.cs
index 24e026a..c26945c 100644
--- a/Library/SereinBaseFunction.cs
+++ b/Library/SereinBaseFunction.cs
@@ -13,18 +13,17 @@ namespace Serein.Library
///
/// 基础功能
///
-
[DynamicFlow(Name ="[基础功能]")]
public static class SereinBaseFunction
{
-
+ [NodeAction(NodeType.Action, "对象透传")]
+ public static object SereinTransmissionObject(object value) => value;
[NodeAction(NodeType.Action, "键值对组装")]
- public static Dictionary SereinKvDataCollectionNode(string argName,
+ public static Dictionary SereinKvDataCollection(string argName,
params object[] value)
{
-
var names = argName.Split(';');
var count = Math.Min(value.Length, names.Length);
var dict = new Dictionary();
@@ -36,13 +35,13 @@ namespace Serein.Library
}
[NodeAction(NodeType.Action, "数组组装")]
- public static object[] SereinListDataCollectionNode(params object[] value)
+ public static object[] SereinListDataCollection(params object[] value)
{
return value;
}
[NodeAction(NodeType.Action, "输出")]
- public static object[] SereinConsoleNode(params object[] value)
+ public static object[] SereinConsole(params object[] value)
{
foreach (var item in value)
{
@@ -104,7 +103,7 @@ namespace Serein.Library
}
- [NodeAction(NodeType.Action, "设置/更新全局数据")]
+ [NodeAction(NodeType.Action, "设置或更新全局数据")]
public static object SereinAddOrUpdateFlowGlobalData(string name, object data)
{
SereinEnv.AddOrUpdateFlowGlobalData(name, data);
diff --git a/Library/Utils/DynamicObjectHelper.cs b/Library/Utils/DynamicObjectHelper.cs
index 2863710..b52dbdc 100644
--- a/Library/Utils/DynamicObjectHelper.cs
+++ b/Library/Utils/DynamicObjectHelper.cs
@@ -16,6 +16,20 @@ namespace Serein.Library.Utils
// 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式)
static Dictionary typeCache = new Dictionary();
+ ///
+ /// 获取运行时创建过的类型
+ ///
+ ///
+ ///
+ public static Type GetCacheType(string className)
+ {
+ if(typeCache.TryGetValue(className, out var type))
+ {
+ return type;
+ }
+ return null;
+ }
+
public static object Resolve(IDictionary properties, string typeName)
{
var obj = CreateObjectWithProperties(properties, typeName);
diff --git a/Library/Utils/SereinEnv.cs b/Library/Utils/SereinEnv.cs
index bcecb5d..7b7f2ee 100644
--- a/Library/Utils/SereinEnv.cs
+++ b/Library/Utils/SereinEnv.cs
@@ -101,8 +101,6 @@ namespace Serein.Library
#endregion
-
-
///
/// 设置运行流程
///
@@ -138,8 +136,35 @@ namespace Serein.Library
}
-
+ ///
+ /// 尝试在UI线程上触发事件
+ ///
+ ///
+ ///
+ public async static Task TriggerEvent(Action action)
+ {
+ if (environment is null)
+ {
+ action?.Invoke();
+ }
+ else
+ {
+ var uco = environment.UIContextOperation;
+ if (uco is null)
+ {
+ action?.Invoke();
+ }
+ else
+ {
+ await uco.InvokeAsync(() =>
+ {
+ action?.Invoke();
+ });
+ }
+ }
+
+ }
}
diff --git a/NodeFlow/Env/FlowEdit.cs b/NodeFlow/Env/FlowEdit.cs
index a090bfe..f245b14 100644
--- a/NodeFlow/Env/FlowEdit.cs
+++ b/NodeFlow/Env/FlowEdit.cs
@@ -272,6 +272,7 @@ namespace Serein.NodeFlow.Env
ToNodeGuid = toNodeGuid,
ConnectionInvokeType = connectionType,
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove,
+ JunctionOfConnectionType = JunctionOfConnectionType.Invoke,
};
flowOperationService.Execute(operation);
}
@@ -284,7 +285,8 @@ namespace Serein.NodeFlow.Env
FromNodeGuid = fromNodeGuid,
ToNodeGuid = toNodeGuid,
ArgIndex = argIndex,
- ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove
+ ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove,
+ JunctionOfConnectionType = JunctionOfConnectionType.Arg,
};
flowOperationService.Execute(operation);
}
@@ -354,7 +356,8 @@ namespace Serein.NodeFlow.Env
}*/
canvasModel.StartNode = newStartNodeModel;
//newStartNode.IsStart = true;
- _ = TriggerEvent(() =>
+
+ _ = SereinEnv.TriggerEvent(() =>
flowEnvironmentEvent.OnStartNodeChanged(
new StartNodeChangeEventArgs(canvasGuid, oldNodeGuid, newStartNodeModel.Guid)
));
@@ -651,20 +654,7 @@ namespace Serein.NodeFlow.Env
- private async Task TriggerEvent(Action action)
- {
- if(UIContextOperation is null)
- {
- action?.Invoke();
- }
- else
- {
- await UIContextOperation.InvokeAsync(() =>
- {
- action?.Invoke();
- });
- }
- }
+ private async Task TriggerEvent(Action action) => await SereinEnv.TriggerEvent(action);
}
diff --git a/NodeFlow/Model/Node/NodeModelBaseData.cs b/NodeFlow/Model/Node/NodeModelBaseData.cs
index e6122be..e4ab624 100644
--- a/NodeFlow/Model/Node/NodeModelBaseData.cs
+++ b/NodeFlow/Model/Node/NodeModelBaseData.cs
@@ -146,24 +146,28 @@ namespace Serein.NodeFlow.Model
partial void OnIsPublicChanged(bool oldValue, bool newValue)
{
var list = CanvasDetails.PublicNodes.ToList();
- if (newValue)
+ _ = SereinEnv.TriggerEvent(() =>
{
- // 公开节点
- if (!CanvasDetails.PublicNodes.Contains(this))
+ if (newValue)
{
- list.Add(this);
- CanvasDetails.PublicNodes= list;
+ // 公开节点
+ if (!CanvasDetails.PublicNodes.Contains(this))
+ {
+ list.Add(this);
+ CanvasDetails.PublicNodes = list;
+ }
}
- }
- else
- {
- // 取消公开
- if (CanvasDetails.PublicNodes.Contains(this))
+ else
{
- list.Remove(this);
- CanvasDetails.PublicNodes = list;
+ // 取消公开
+ if (CanvasDetails.PublicNodes.Contains(this))
+ {
+ list.Remove(this);
+ CanvasDetails.PublicNodes = list;
+ }
}
- }
+ });
+
}
diff --git a/NodeFlow/Model/Node/NodeModelBaseFunc.cs b/NodeFlow/Model/Node/NodeModelBaseFunc.cs
index b38d5be..7916b40 100644
--- a/NodeFlow/Model/Node/NodeModelBaseFunc.cs
+++ b/NodeFlow/Model/Node/NodeModelBaseFunc.cs
@@ -132,19 +132,31 @@ namespace Serein.NodeFlow.Model
throw new Exception($"节点{this.Guid}不存在对应委托");
}
- var instance = Env.IOC.Get(md.ActingInstanceType);
- if (instance is null)
+ if (md.IsStatic)
{
- Env.IOC.Register(md.ActingInstanceType).Build();
- instance = Env.IOC.Get(md.ActingInstanceType);
+ object[] args = await this.GetParametersAsync(context, token);
+ var result = await dd.InvokeAsync(null, args);
+ var flowReslt = new FlowResult(this.Guid, context, result);
+ return flowReslt;
}
- object[] args = await this.GetParametersAsync(context, token);
- var result = await dd.InvokeAsync(instance, args);
- var flowReslt = new FlowResult(this.Guid, context, result);
- return flowReslt;
+ else
+ {
+ var instance = Env.IOC.Get(md.ActingInstanceType);
+ if (instance is null)
+ {
+ Env.IOC.Register(md.ActingInstanceType).Build();
+ instance = Env.IOC.Get(md.ActingInstanceType);
+ }
+ object[] args = await this.GetParametersAsync(context, token);
+ var result = await dd.InvokeAsync(instance, args);
+ var flowReslt = new FlowResult(this.Guid, context, result);
+ return flowReslt;
+ }
+
}
+
}
diff --git a/NodeFlow/Model/Node/SingleFlowCallNode.cs b/NodeFlow/Model/Node/SingleFlowCallNode.cs
index 61036b7..9440e00 100644
--- a/NodeFlow/Model/Node/SingleFlowCallNode.cs
+++ b/NodeFlow/Model/Node/SingleFlowCallNode.cs
@@ -317,7 +317,8 @@ namespace Serein.NodeFlow.Model
_ => base.ExecutingAsync(context, token)
});
-
+ // 对于目标节点的后续节点,如果入参参数来源指定为它(目标节点)时,就需要从上下文中根据它的Guid获取流程数据
+ context.AddOrUpdateFlowData(TargetNode.Guid, flowData);
if (IsShareParam)
{
// 设置运行时上一节点
@@ -325,7 +326,6 @@ namespace Serein.NodeFlow.Model
// 此处代码与SereinFlow.Library.FlowNode.ParameterDetails
// ToMethodArgData()方法中判断流程接口节点分支逻辑耦合
// 不要轻易修改
- context.AddOrUpdateFlowData(TargetNode.Guid, flowData);
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
{
if (this.SuccessorNodes[ctType] == null) continue;
diff --git a/NodeFlow/Model/Node/SingleScriptNode.cs b/NodeFlow/Model/Node/SingleScriptNode.cs
index 1ce124c..e863c30 100644
--- a/NodeFlow/Model/Node/SingleScriptNode.cs
+++ b/NodeFlow/Model/Node/SingleScriptNode.cs
@@ -158,13 +158,14 @@ namespace Serein.NodeFlow.Model
varNames.Add(pd.Name);
}
- StringBuilder sb = new StringBuilder();
+ var sb = new StringBuilder();
foreach (var pd in MethodDetails.ParameterDetailss)
{
sb.AppendLine($"let {pd.Name};"); // 提前声明这些变量
}
sb.Append(Script);
- var p = new SereinScriptParser(sb.ToString());
+ var script = sb.ToString();
+ var p = new SereinScriptParser(script);
//var p = new SereinScriptParser(Script);
mainNode = p.Parse(); // 开始解析
diff --git a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs
index cf7a0e8..d4b2627 100644
--- a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs
+++ b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs
@@ -5,6 +5,8 @@ using Serein.NodeFlow.Model;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reactive;
+using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
using static Serein.Library.Api.NodeConnectChangeEventArgs;
@@ -163,11 +165,51 @@ namespace Serein.NodeFlow.Model.Operation
return false;
}
-
var isOverwriting = false;
ConnectionInvokeType overwritingCt = ConnectionInvokeType.None;
var isPass = false;
+ #region 类型检查
+ bool checkTypeState = true;
+ List toPds = new List();
+ if (ToNode.MethodDetails.ParameterDetailss.Length > 0)
+ {
+ var fromNoeReturnType = fromNode.MethodDetails.ReturnType;
+ if (fromNoeReturnType != null
+ && fromNoeReturnType != typeof(object)
+ && fromNoeReturnType != typeof(void)
+ && fromNoeReturnType != typeof(Unit))
+ {
+ var toNodePds = toNode.MethodDetails.ParameterDetailss;
+ 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; // 类型检查初步通过
+ }
+ }
+ }
+ if (!checkTypeState) // 类型检查不通过
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, "创建失败,目标节点没有合适的入参接收返回值");
+ return false;
+ }
+ #endregion
+
+
+
#region 检查是否存在对应的连接
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
{
@@ -237,7 +279,27 @@ namespace Serein.NodeFlow.Model.Operation
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
));
});
-
+
+
+ /* foreach (var toPd in toPds)
+ {
+ string canvasGuid = CanvasGuid;
+ string fromNodeGuid = fromNode.Guid;
+ string toNodeGuid = toNode.Guid;
+ JunctionType fromNodeJunctionType = JunctionType.ReturnData;
+ JunctionType toNodeJunctionType = JunctionType.ArgData;
+ ConnectionArgSourceType argSourceType = ConnectionArgSourceType.GetOtherNodeData;
+ int argIndex = toPd.Index;
+ // 调用创建连线接口
+ flowEnvironment.FlowEdit.ConnectArgSourceNode(canvasGuid,
+ fromNodeGuid,
+ toNodeGuid,
+ fromNodeJunctionType,
+ toNodeJunctionType,
+ argSourceType,
+ argIndex);
+ }*/
+
// Invoke
// GetResult
return true;
@@ -407,7 +469,9 @@ namespace Serein.NodeFlow.Model.Operation
///
private async Task RemoveArgConnection()
{
- ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = null;
+ var type = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType;
+ FromNode.NeedResultNodes[type].Remove(ToNode);
+ ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = string.Empty;
ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值
diff --git a/NodeFlow/Model/Operation/OperationBase.cs b/NodeFlow/Model/Operation/OperationBase.cs
index d1ea2fe..1cc5e09 100644
--- a/NodeFlow/Model/Operation/OperationBase.cs
+++ b/NodeFlow/Model/Operation/OperationBase.cs
@@ -102,23 +102,7 @@ namespace Serein.NodeFlow.Model.Operation
public abstract void ToInfo();
- protected async Task TriggerEvent(Action action)
- {
- /* if (OperatingSystem.IsWindows())
- {
- }*/
- if (uiContextOperation is null)
- {
- action?.Invoke();
- }
- else
- {
- await uiContextOperation.InvokeAsync(() =>
- {
- action?.Invoke();
- });
- }
- }
+ protected async Task TriggerEvent(Action action) => await SereinEnv.TriggerEvent(action);
}
diff --git a/NodeFlow/Model/Operation/RemoveNodeOperation.cs b/NodeFlow/Model/Operation/RemoveNodeOperation.cs
index de14c46..59572cc 100644
--- a/NodeFlow/Model/Operation/RemoveNodeOperation.cs
+++ b/NodeFlow/Model/Operation/RemoveNodeOperation.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
+using System.Xml.Linq;
namespace Serein.NodeFlow.Model.Operation
{
@@ -58,13 +59,17 @@ namespace Serein.NodeFlow.Model.Operation
// 还需要记录移除的事件参数,用以撤销恢复
#region 移除方法调用关系
- foreach (var item in flowNode.PreviousNodes)
+
+ // 检查该节点的前继节点,然后从这些前继节点中移除与该节点的连接关系
+ var previousNodes = flowNode.PreviousNodes.Where(kvp => kvp.Value.Count > 0).ToDictionary();
+ foreach (var item in previousNodes)
{
var connectionType = item.Key; // 连接类型
- var previousNodes = item.Value; // 对应类型的父节点集合
- foreach (IFlowNode previousNode in previousNodes)
+ var nodes = item.Value.ToArray(); // 对应类型的父节点集合
+ foreach (IFlowNode previousNode in nodes)
{
+ flowNode.PreviousNodes[connectionType].Remove(previousNode);
previousNode.SuccessorNodes[connectionType].Remove(flowNode);
var e = new NodeConnectChangeEventArgs(
CanvasGuid, // 画布
@@ -74,13 +79,11 @@ namespace Serein.NodeFlow.Model.Operation
connectionType, // 对应的连接关系
NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
EventArgs.Add(e); // 缓存事件参数
- await TriggerEvent(() =>
- {
- flowEnvironmentEvent.OnNodeConnectChanged(e);
- });
}
}
+ // 检查该节点的后续节点,然后从这些后续节点中移除与该节点的连接关系
+ var successorNodes = flowNode.SuccessorNodes.Where(kvp => kvp.Value.Count > 0).ToDictionary();
if (flowNode.ControlType == NodeControlType.FlowCall)
{
// 根据流程接口节点目前的设计,暂未支持能连接下一个节点
@@ -88,14 +91,15 @@ namespace Serein.NodeFlow.Model.Operation
else
{
// 遍历所有后继节点,从那些后继节点中的前置节点集合中移除该节点
- foreach (var item in flowNode.SuccessorNodes)
+ foreach (var item in successorNodes)
{
var connectionType = item.Key; // 方法调用连接类型
- var successorNodes = item.Value; // 对应类型的父节点集合
- foreach (IFlowNode successorNode in successorNodes)
+ var nodes = item.Value.ToArray(); // 对应类型的父节点集合
+ foreach (IFlowNode successorNode in nodes)
{
- successorNode.SuccessorNodes[connectionType].Remove(flowNode);
+ successorNode.PreviousNodes[connectionType].Remove(flowNode);
+ flowNode.SuccessorNodes[connectionType].Remove(successorNode);
var e = new NodeConnectChangeEventArgs(
CanvasGuid, // 画布
flowNode.Guid, // 被移除的节点Guid
@@ -104,10 +108,7 @@ namespace Serein.NodeFlow.Model.Operation
connectionType, // 对应的连接关系
NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
EventArgs.Add(e); // 缓存事件参数
- await TriggerEvent(() =>
- {
- flowEnvironmentEvent.OnNodeConnectChanged(e);
- });
+
}
}
}
@@ -120,7 +121,7 @@ namespace Serein.NodeFlow.Model.Operation
foreach (var item in flowNode.NeedResultNodes)
{
var connectionType = item.Key; // 参数来源连接类型
- var argNodes = item.Value; // 对应类型的入参需求节点集合
+ var argNodes = item.Value.ToArray(); // 对应类型的入参需求节点集合
foreach (var argNode in argNodes)
{
var md = argNode.MethodDetails;
@@ -140,10 +141,6 @@ namespace Serein.NodeFlow.Model.Operation
connectionType, // 对应的连接关系
NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
EventArgs.Add(e); // 缓存事件参数
- await TriggerEvent(() =>
- {
- flowEnvironmentEvent.OnNodeConnectChanged(e);
- });
}
}
}
@@ -152,7 +149,7 @@ namespace Serein.NodeFlow.Model.Operation
if (flowNode.MethodDetails?.ParameterDetailss != null)
{
- var pds = flowNode.MethodDetails.ParameterDetailss;
+ var pds = flowNode.MethodDetails.ParameterDetailss.ToArray();
foreach (var pd in pds)
{
if (string.IsNullOrWhiteSpace(pd.ArgDataSourceNodeGuid)) continue;
@@ -168,10 +165,6 @@ namespace Serein.NodeFlow.Model.Operation
pd.ArgDataSourceType, // 对应的连接关系
NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
EventArgs.Add(e); // 缓存事件参数
- await TriggerEvent(() =>
- {
- flowEnvironmentEvent.OnNodeConnectChanged(e);
- });
}
}
}
@@ -179,28 +172,55 @@ namespace Serein.NodeFlow.Model.Operation
#endregion
flowModelService.RemoveNodeModel(flowNode); // 从记录中移除
- //flowNode.Remove(); // 调用节点的移除方法
+ //flowNode.Remove(); // 调用节点的移除方法
- if(flowEnvironment.UIContextOperation is null)
+
+ // 存在UI上下文操作,当前运行环境极有可能运行在有UI线程的平台上
+ // 为了避免直接修改 ObservableCollection 集合导致异常产生,故而使用UI线程上下文操作运行
+ NodeConnectChangeEventArgs[] es = EventArgs.ToArray();
+ await TriggerEvent(() =>
+ {
+
+ /*flowCanvasDetails.Nodes.Remove(flowNode);
+ flowCanvasDetails.OnPropertyChanged(nameof(FlowCanvasDetails.Nodes));
+ if (flowNode.IsPublic)
+ {
+ flowCanvasDetails.PublicNodes.Remove(flowNode);
+ flowCanvasDetails.OnPropertyChanged(nameof(FlowCanvasDetails.PublicNodes));
+ }*/
+
+ // 手动赋值刷新UI显示
+ var lsit = flowCanvasDetails.Nodes.ToList();
+ lsit.Remove(flowNode);
+ flowCanvasDetails.Nodes = lsit;
+ if (flowNode.IsPublic)
+ {
+ var publicNodes = flowCanvasDetails.PublicNodes.ToList();
+ publicNodes.Remove(flowNode);
+ flowCanvasDetails.PublicNodes = publicNodes;
+ }
+
+ foreach (var e in es)
+ {
+ flowEnvironmentEvent.OnNodeConnectChanged(e); // 触发事件
+ }
+ flowEnvironmentEvent.OnNodeRemoved(new NodeRemoveEventArgs(CanvasGuid, NodeGuid));
+ });
+
+ /*if (flowEnvironment.UIContextOperation is null)
{
flowCanvasDetails?.Nodes.Remove(flowNode);
}
else
{
- // 存在UI上下文操作,当前运行环境极有可能运行在有UI线程的平台上
- // 为了避免直接修改 ObservableCollection 集合导致异常产生,故而使用UI线程上下文操作运行
- await TriggerEvent(() =>
- {
- var lsit = flowCanvasDetails.Nodes.ToList();
- lsit.Remove(flowNode);
- flowCanvasDetails.Nodes = lsit;
- });
- }
+
+
+ }*/
- await TriggerEvent(() =>
+ /* await TriggerEvent(() =>
{
- flowEnvironmentEvent.OnNodeRemoved(new NodeRemoveEventArgs(CanvasGuid, NodeGuid));
- });
+
+ });*/
return true;
}
diff --git a/NodeFlow/Services/FlowCoreGenerateService.cs b/NodeFlow/Services/FlowCoreGenerateService.cs
index 339af50..9c55642 100644
--- a/NodeFlow/Services/FlowCoreGenerateService.cs
+++ b/NodeFlow/Services/FlowCoreGenerateService.cs
@@ -255,7 +255,7 @@ namespace Serein.NodeFlow.Services
}
else if (pd.DataType.IsAssignableFrom(otherNodeReturnType))
{
- sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据");
+ sb_invoke_login.AppendCode(3, $"{valueType} value{index} = ({valueType}){flowContext}.{nameof(IDynamicContext.GetFlowData)}(\"{pd.ArgDataSourceNodeGuid}\").Value; // 获取指定节点的数据");
}
else
{
@@ -1058,7 +1058,7 @@ namespace Serein.NodeFlow.Services
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(3, $"return {flowResult};");
sb.AppendCode(2, $"}}");
return sb.ToString();
// throw new ArgumentNullException($"类型转换失败,{(flowResult.Value is null ? "返回数据为 null" : $"返回数据与需求类型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}。")}");
diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs
index 80e0cb3..a6e884b 100644
--- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs
+++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs
@@ -65,6 +65,7 @@ public static class NodeMethodDetailsHelper
Type? returnType;
bool isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult);
+ bool isStatic = methodInfo.IsStatic;
if (attribute.MethodDynamicType == Library.NodeType.UI)
@@ -162,6 +163,7 @@ public static class NodeMethodDetailsHelper
// 如果存在可变参数,取最后一个元素的下标,否则为-1;
ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length - 1 : -1,
IsAsync = isAsync,
+ IsStatic = isStatic,
};
//var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var methodDelegate);// 返回值
diff --git a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs
index 433c661..c28a902 100644
--- a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs
+++ b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs
@@ -251,7 +251,7 @@ namespace Serein.Library.NodeGenerator
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" } ");
- sb.AppendLine(" protected void OnPropertyChanged(string propertyName) => ");
+ sb.AppendLine(" public void OnPropertyChanged(string propertyName) => ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" ");
sb.AppendLine(" ");
diff --git a/Serein.Script/BinaryOperationEvaluator.cs b/Serein.Script/BinaryOperationEvaluator.cs
new file mode 100644
index 0000000..47ae019
--- /dev/null
+++ b/Serein.Script/BinaryOperationEvaluator.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Script
+{
+ public static class BinaryOperationEvaluator
+ {
+ public static object EvaluateValue(object left, string op, object right)
+ {
+ if (op == null) throw new ArgumentNullException(nameof(op));
+
+ // 特判字符串拼接
+ if (op == "+" && (left is string || right is string))
+ {
+ return (left?.ToString() ?? "") + (right?.ToString() ?? "");
+ }
+
+ // 支持 null 运算(可按需扩展)
+ if (left == null || right == null)
+ {
+ throw new InvalidOperationException($"无法对 null 执行操作:{op}");
+ }
+
+ // 尝试统一类型(浮点优先)
+ var leftType = left.GetType();
+ var rightType = right.GetType();
+
+ Type resultType = GetWiderType(leftType, rightType);
+ dynamic leftValue = Convert.ChangeType(left, resultType);
+ dynamic rightValue = Convert.ChangeType(right, resultType);
+
+ return op switch
+ {
+ "+" => leftValue + rightValue,
+ "-" => leftValue - rightValue,
+ "*" => leftValue * rightValue,
+ "/" => rightValue == 0 ? throw new DivideByZeroException() : leftValue / rightValue,
+
+ ">" => leftValue > rightValue,
+ "<" => leftValue < rightValue,
+ ">=" => leftValue >= rightValue,
+ "<=" => leftValue <= rightValue,
+ "==" => Equals(leftValue, rightValue),
+ "!=" => !Equals(leftValue, rightValue),
+
+ _ => throw new NotImplementedException($"未实现的操作符: {op}")
+ };
+ }
+
+
+
+
+ ///
+ /// 推导两个类型的“最通用类型”(类型提升)
+ ///
+ private static Type GetWiderType(Type t1, Type t2)
+ {
+ if (t1 == typeof(string) || t2 == typeof(string)) return typeof(string);
+ if (t1 == typeof(decimal) || t2 == typeof(decimal)) return typeof(decimal);
+ if (t1 == typeof(double) || t2 == typeof(double)) return typeof(double);
+ if (t1 == typeof(float) || t2 == typeof(float)) return typeof(float);
+ if (t1 == typeof(ulong) || t2 == typeof(ulong)) return typeof(ulong);
+ if (t1 == typeof(long) || t2 == typeof(long)) return typeof(long);
+ if (t1 == typeof(uint) || t2 == typeof(uint)) return typeof(uint);
+ if (t1 == typeof(int) || t2 == typeof(int)) return typeof(int);
+
+ // fallback
+ return typeof(object);
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ public static Type EvaluateType(Type leftType, string op, Type rightType)
+ {
+ if (leftType == null || rightType == null)
+ throw new ArgumentNullException("操作数类型不能为 null");
+
+ // 字符串拼接
+ if (op == "+" && (leftType == typeof(string) || rightType == typeof(string)))
+ return typeof(string);
+
+ // 比较操作总是返回 bool
+ if (op is ">" or "<" or ">=" or "<=" or "==" or "!=")
+ return typeof(bool);
+
+ // 数值类型推导
+ if (IsNumeric(leftType) && IsNumeric(rightType))
+ {
+ return PromoteNumericType(leftType, rightType);
+ }
+
+ // 逻辑操作
+ if (op is "&&" or "||")
+ {
+ if (leftType == typeof(bool) && rightType == typeof(bool))
+ return typeof(bool);
+ throw new InvalidOperationException($"逻辑操作 '{op}' 仅适用于 bool 类型");
+ }
+
+ throw new NotImplementedException($"不支持操作符 '{op}' 对 {leftType.Name} 和 {rightType.Name} 进行类型推导");
+ }
+
+ private static bool IsNumeric(Type type)
+ {
+ return type == typeof(byte) || type == typeof(sbyte) ||
+ type == typeof(short) || type == typeof(ushort) ||
+ type == typeof(int) || type == typeof(uint) ||
+ type == typeof(long) || type == typeof(ulong) ||
+ type == typeof(float) || type == typeof(double) ||
+ type == typeof(decimal);
+ }
+
+ // 提升到更高精度类型
+ private static Type[] types = new[]
+ {
+ typeof(decimal),
+ typeof(double),
+ typeof(float),
+ typeof(ulong),
+ typeof(long),
+ typeof(uint),
+ typeof(int),
+ typeof(ushort),
+ typeof(short),
+ typeof(byte),
+ typeof(sbyte)
+ };
+
+ private static Type PromoteNumericType(Type left, Type right)
+ {
+ var ranks = types;
+ foreach (var type in ranks)
+ {
+ if (type == left || type == right)
+ return type;
+ }
+
+ return typeof(object); // fallback
+ }
+ }
+
+
+
+
+
+}
+
+
diff --git a/Serein.Script/Node/MemberAccessNode.cs b/Serein.Script/Node/MemberAccessNode.cs
index d889a3d..04cc5a3 100644
--- a/Serein.Script/Node/MemberAccessNode.cs
+++ b/Serein.Script/Node/MemberAccessNode.cs
@@ -11,7 +11,14 @@ namespace Serein.Script.Node
///
public class MemberAccessNode : ASTNode
{
+ ///
+ /// 对象token
+ ///
public ASTNode Object { get; }
+
+ ///
+ /// 成员名称
+ ///
public string MemberName { get; }
public MemberAccessNode(ASTNode obj, string memberName)
diff --git a/Serein.Script/Node/MemberAssignmentNode.cs b/Serein.Script/Node/MemberAssignmentNode.cs
index ffa643a..7e429a0 100644
--- a/Serein.Script/Node/MemberAssignmentNode.cs
+++ b/Serein.Script/Node/MemberAssignmentNode.cs
@@ -11,8 +11,17 @@ namespace Serein.Script.Node
///
public class MemberAssignmentNode : ASTNode
{
+ ///
+ /// 作用的对象
+ ///
public ASTNode Object { get; }
+ ///
+ /// 被赋值的成员(属性/字段)名称
+ ///
public string MemberName { get; }
+ ///
+ /// 值来源
+ ///
public ASTNode Value { get; }
public MemberAssignmentNode(ASTNode obj, string memberName, ASTNode value)
diff --git a/Serein.Script/Node/MemberFunctionCallNode.cs b/Serein.Script/Node/MemberFunctionCallNode.cs
index 4d030b1..0312226 100644
--- a/Serein.Script/Node/MemberFunctionCallNode.cs
+++ b/Serein.Script/Node/MemberFunctionCallNode.cs
@@ -11,8 +11,19 @@ namespace Serein.Script.Node
///
public class MemberFunctionCallNode : ASTNode
{
+ ///
+ /// 需要被调用的对象
+ ///
public ASTNode Object { get; }
+
+ ///
+ /// 被调用的方法名称
+ ///
public string FunctionName { get; }
+
+ ///
+ /// 方法参数
+ ///
public List Arguments { get; }
public MemberFunctionCallNode(ASTNode @object, string functionName, List arguments)
diff --git a/Serein.Script/Node/NumberIntNode.cs b/Serein.Script/Node/NumberIntNode.cs
new file mode 100644
index 0000000..bdd8942
--- /dev/null
+++ b/Serein.Script/Node/NumberIntNode.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Script.Node
+{
+ ///
+ /// 数值型节点
+ ///
+ public abstract class NumberNode : ASTNode where T : struct, IComparable
+ {
+ public T Value { get; }
+ public NumberNode(T value) => Value = value;
+ }
+
+
+ ///
+ /// int 整数型字面量
+ ///
+ public class NumberIntNode(int vlaue) : NumberNode(vlaue)
+ {
+ }
+
+ ///
+ /// int 整数型字面量
+ ///
+ public class NumberLongNode(long vlaue) : NumberNode(vlaue)
+ {
+ }
+
+ ///
+ /// int 整数型字面量
+ ///
+ public class NumberFloatNode(float vlaue) : NumberNode(vlaue)
+ {
+ }
+
+ ///
+ /// int 整数型字面量
+ ///
+ public class NumberDoubleNode(double vlaue) : NumberNode(vlaue)
+ {
+ }
+
+
+
+
+ /*///
+ /// int 整数型字面量
+ ///
+ public class NumberIntNode : ASTNode
+ {
+ public int Value { get; }
+ public NumberIntNode(int value) => Value = value;
+ }
+
+
+ ///
+ /// int 整数型字面量
+ ///
+ public class NumberLongNode : ASTNode
+ {
+ public long Value { get; }
+ public NumberLongNode(long value) => Value = value;
+ }
+
+
+ ///
+ /// int 整数型字面量
+ ///
+ public class NumberFloatNode : ASTNode
+ {
+ public float Value { get; }
+ public NumberFloatNode(float value) => Value = value;
+ }
+
+
+ ///
+ /// int 整数型字面量
+ ///
+ public class NumberDoubleNode : ASTNode
+ {
+ public double Value { get; }
+ public NumberDoubleNode(double value) => Value = value;
+ }*/
+
+
+}
diff --git a/Serein.Script/Node/NumberNode.cs b/Serein.Script/Node/NumberNode.cs
deleted file mode 100644
index 0f1ad07..0000000
--- a/Serein.Script/Node/NumberNode.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Serein.Script.Node
-{
-
-
- ///
- /// 整数型字面量
- ///
- public class NumberNode : ASTNode
- {
- public int Value { get; }
- public NumberNode(int value) => Value = value;
- }
-
-
-}
diff --git a/Serein.Script/SereinScriptInterpreter.cs b/Serein.Script/SereinScriptInterpreter.cs
index e3f5d57..1e46d84 100644
--- a/Serein.Script/SereinScriptInterpreter.cs
+++ b/Serein.Script/SereinScriptInterpreter.cs
@@ -30,6 +30,9 @@ namespace Serein.Script
///
public interface IScriptInvokeContext
{
+ ///
+ /// 脚本运行的流程上下文,包含了流程上下文和变量等信息
+ ///
IDynamicContext FlowContext { get; }
///
@@ -64,7 +67,6 @@ namespace Serein.Script
void OnExit();
}
-
public class ScriptInvokeContext : IScriptInvokeContext
{
public ScriptInvokeContext(IDynamicContext dynamicContext)
@@ -78,7 +80,10 @@ namespace Serein.Script
/// 定义的变量
///
private Dictionary _variables = new Dictionary();
-
+
+ ///
+ /// 取消令牌源,用于控制脚本的执行
+ ///
private CancellationTokenSource _tokenSource = new CancellationTokenSource();
///
@@ -109,8 +114,6 @@ namespace Serein.Script
}
-
-
void IScriptInvokeContext.OnExit()
{
// 清理脚本中加载的非托管资源
@@ -134,7 +137,9 @@ namespace Serein.Script
}
-
+ ///
+ /// 脚本解释器,负责解析和执行 Serein 脚本
+ ///
public class SereinScriptInterpreter
{
@@ -220,23 +225,34 @@ namespace Serein.Script
private async Task