修改了流程运行中的bug

This commit is contained in:
fengjiayi
2024-10-28 21:52:45 +08:00
parent 561b6d764f
commit 66141533b1
27 changed files with 518 additions and 373 deletions

View File

@@ -36,25 +36,55 @@ namespace Serein.Library.Core.NodeFlow
public ConnectionInvokeType NextOrientation { get; set; }
/// <summary>
/// 每个上下文分别存放节点的当前数据
/// 每个流程上下文分别存放节点的当前数据
/// </summary>
private readonly ConcurrentDictionary<string,object?> dictNodeFlowData = new ConcurrentDictionary<string, object?>();
private readonly ConcurrentDictionary<string, object?> dictNodeFlowData = new ConcurrentDictionary<string, object?>();
/// <summary>
/// 每个流程上下文存储运行时节点的调用关系
/// </summary>
private readonly ConcurrentDictionary<NodeModelBase, NodeModelBase> dictPreviousNodes = new ConcurrentDictionary<NodeModelBase, NodeModelBase>();
/// <summary>
/// 设置运行时上一节点
/// </summary>
/// <param name="currentNodeModel">当前节点</param>
/// <param name="PreviousNode">上一节点</param>
public void SetPreviousNode(NodeModelBase currentNodeModel, NodeModelBase PreviousNode)
{
dictPreviousNodes.AddOrUpdate(currentNodeModel, (_)=> PreviousNode, (_,_) => PreviousNode);
}
/// <summary>
/// 获取当前节点的运行时上一节点
/// </summary>
/// <param name="currentNodeModel"></param>
/// <returns></returns>
public NodeModelBase GetPreviousNode(NodeModelBase currentNodeModel)
{
if (dictPreviousNodes.TryGetValue(currentNodeModel, out var node))
{
return node;
}
else
{
return null;
}
}
/// <summary>
/// 获取节点当前数据
/// </summary>
/// <param name="nodeGuid"></param>
/// <param name="nodeGuid">节点</param>
/// <returns></returns>
public object? GetFlowData(string nodeGuid)
{
if (string.IsNullOrEmpty(nodeGuid))
{
return null;
}
if(dictNodeFlowData.TryGetValue(nodeGuid,out var data))
if(dictNodeFlowData.TryGetValue(nodeGuid, out var data))
{
return data;
}
else
{
return null;
}
@@ -63,14 +93,32 @@ namespace Serein.Library.Core.NodeFlow
/// <summary>
/// 添加或更新当前节点数据
/// </summary>
/// <param name="nodeGuid">节点Guid</param>
/// <param name="nodeGuid">节点</param>
/// <param name="flowData">新的数据</param>
public void AddOrUpdate(string nodeGuid,object? flowData)
public void AddOrUpdate(string nodeGuid, object? flowData)
{
// this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData);
this.dictNodeFlowData.AddOrUpdate(nodeGuid, _ => flowData, (_, _) => flowData);
}
/// <summary>
/// 上一节点数据透传到下一节点
/// </summary>
/// <param name="nodeModel"></param>
public object? TransmissionData(NodeModelBase nodeModel)
{
if (dictPreviousNodes.TryGetValue(nodeModel, out var previousNode)) // 首先获取当前节点的上一节点
{
if (dictNodeFlowData.TryGetValue(previousNode.Guid, out var data)) // 其次获取上一节点的数据
{
return data;
//AddOrUpdate(nodeModel.Guid, data); // 然后作为当前节点的数据记录在上下文中
}
}
return null;
}
/// <summary>
/// 结束流程
/// </summary>

View File

@@ -18,7 +18,6 @@ namespace Serein.Library.Framework.NodeFlow
RunState = RunState.Running;
}
/// <summary>
/// 运行环境
@@ -40,10 +39,38 @@ namespace Serein.Library.Framework.NodeFlow
/// </summary>
private readonly ConcurrentDictionary<string, object> dictNodeFlowData = new ConcurrentDictionary<string, object>();
private readonly ConcurrentDictionary<NodeModelBase, NodeModelBase> dictPreviousNodes = new ConcurrentDictionary<NodeModelBase, NodeModelBase>();
/// <summary>
/// 设置运行时上一节点
/// </summary>
/// <param name="currentNodeModel">当前节点</param>
/// <param name="PreviousNode">上一节点</param>
public void SetPreviousNode(NodeModelBase currentNodeModel, NodeModelBase PreviousNode)
{
dictPreviousNodes.AddOrUpdate(currentNodeModel, (n1) => PreviousNode, (n1, n2) => PreviousNode);
}
/// <summary>
/// 获取当前节点的运行时上一节点
/// </summary>
/// <param name="currentNodeModel"></param>
/// <returns></returns>
public NodeModelBase GetPreviousNode(NodeModelBase currentNodeModel)
{
if (dictPreviousNodes.TryGetValue(currentNodeModel, out var node))
{
return node;
}
else
{
return null;
}
}
/// <summary>
/// 获取节点当前数据
/// </summary>
/// <param name="nodeGuid"></param>
/// <returns></returns>
public object GetFlowData(string nodeGuid)
{
@@ -51,6 +78,7 @@ namespace Serein.Library.Framework.NodeFlow
{
return data;
}
else
{
return null;
}
@@ -59,14 +87,32 @@ namespace Serein.Library.Framework.NodeFlow
/// <summary>
/// 添加或更新当前节点数据
/// </summary>
/// <param name="nodeGuid">节点Guid</param>
/// <param name="nodeGuid">节点</param>
/// <param name="flowData">新的数据</param>
public void AddOrUpdate(string nodeGuid, object flowData)
{
// this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData);
this.dictNodeFlowData[nodeGuid] = flowData;
this.dictNodeFlowData.AddOrUpdate(nodeGuid, n1 => flowData, (n1, n2)=> flowData);
}
/// <summary>
/// 上一节点数据透传到下一节点
/// </summary>
/// <param name="nodeModel"></param>
public object TransmissionData(NodeModelBase nodeModel)
{
if (dictPreviousNodes.TryGetValue(nodeModel, out var previousNode)) // 首先获取当前节点的上一节点
{
if (dictNodeFlowData.TryGetValue(previousNode.Guid, out var data)) // 其次获取上一节点的数据
{
return data;
//AddOrUpdate(nodeModel.Guid, data); // 然后作为当前节点的数据记录在上下文中
}
}
return null;
}
/// <summary>
/// 结束流程
/// </summary>
@@ -74,7 +120,11 @@ namespace Serein.Library.Framework.NodeFlow
{
foreach (var nodeObj in dictNodeFlowData.Values)
{
if (nodeObj != null)
if (nodeObj is null)
{
continue;
}
else
{
if (typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) && nodeObj is IDisposable disposable)
{
@@ -85,7 +135,6 @@ namespace Serein.Library.Framework.NodeFlow
this.dictNodeFlowData?.Clear();
RunState = RunState.Completion;
}
// public NodeRunCts NodeRunCts { get; set; }
// public ISereinIOC SereinIoc { get; }
//public Task CreateTimingTask(Action action, int time = 100, int count = -1)

View File

@@ -21,10 +21,24 @@ namespace Serein.Library.Api
RunState RunState { get; }
/// <summary>
/// 下一个要执行的节点
/// 下一个要执行的节点类别
/// </summary>
ConnectionInvokeType NextOrientation { get; set; }
/// <summary>
/// 设置节点的运行时上一节点,用以多线程中隔开不同流程的数据
/// </summary>
/// <param name="currentNodeModel">当前节点</param>
/// <param name="PreviousNode">运行时上一节点</param>
void SetPreviousNode(NodeModelBase currentNodeModel, NodeModelBase PreviousNode);
/// <summary>
/// 获取当前节点的运行时上一节点,用以流程中获取数据
/// </summary>
/// <param name="currentNodeModel"></param>
/// <returns></returns>
NodeModelBase GetPreviousNode(NodeModelBase currentNodeModel);
/// <summary>
/// 获取节点的数据(当前节点需要获取上一节点数据时,需要从 运行时上一节点 的Guid 通过这个方法进行获取
/// </summary>
@@ -32,6 +46,13 @@ namespace Serein.Library.Api
/// <returns></returns>
object GetFlowData(string nodeGuid);
/// <summary>
/// 上一节点数据透传到下一节点
/// </summary>
/// <param name="nodeModel"></param>
object TransmissionData(NodeModelBase nodeModel);
/// <summary>
/// 添加或更新当前节点的数据
/// </summary>

View File

@@ -13,11 +13,11 @@ namespace Serein.Library
/// <summary>
/// 取消触发器当前所在分支的继续执行
/// </summary>
Branch,
CancelBranch,
/// <summary>
/// 取消整个触发器流程的再次执行(用于停止全局触发器)
/// </summary>
Flow,
CancelFlow,
}
/// <summary>
/// 是否已取消
@@ -27,7 +27,7 @@ namespace Serein.Library
/// 取消类型
/// </summary>
public CancelClass Type { get; }
public FlipflopException(string message, bool isCancel = true,CancelClass clsss = CancelClass.Branch) :base(message)
public FlipflopException(string message, bool isCancel = true,CancelClass clsss = CancelClass.CancelBranch) :base(message)
{
IsCancel = isCancel;
Type = clsss;

View File

@@ -160,7 +160,7 @@ namespace Serein.Library
StringBuilder sb = new StringBuilder();
sb.AppendLine($"方法别名:{this.MethodAnotherName}");
sb.AppendLine($"方法名称:{this.MethodName}");
sb.AppendLine($"需要实例:{this.ActingInstanceType.FullName}");
sb.AppendLine($"需要实例:{this.ActingInstanceType?.FullName}");
sb.AppendLine($"");
sb.AppendLine($"入参参数信息:");
foreach (var arg in this.ParameterDetailss)

View File

@@ -42,7 +42,7 @@ namespace Serein.Library
/// <summary>
/// 中断级别,暂时停止继续执行后继分支。
/// </summary>
[PropertyInfo(IsNotification = true)] // CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);"
[PropertyInfo(IsNotification = true, CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);")] // CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);"
private bool _isInterrupt = false;
/// <summary>

View File

@@ -66,8 +66,8 @@ namespace Serein.Library
/// <summary>
/// 运行时的上一节点
/// </summary>
[PropertyInfo]
private NodeModelBase _previousNode ;
//[PropertyInfo]
//private NodeModelBase _previousNode ;
/// <summary>
/// 当前节点执行完毕后需要执行的下一个分支的类别
@@ -86,6 +86,11 @@ namespace Serein.Library
public abstract partial class NodeModelBase : IDynamicFlowNode
{
/// <summary>
/// 加载完成后调用的方法
/// </summary>
public abstract void OnLoading();
public NodeModelBase(IFlowEnvironment environment)
{
PreviousNodes = new Dictionary<ConnectionInvokeType, List<NodeModelBase>>();
@@ -100,7 +105,6 @@ namespace Serein.Library
}
/// <summary>
/// 不同分支的父节点
/// </summary>
@@ -111,210 +115,7 @@ namespace Serein.Library
/// </summary>
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> SuccessorNodes { get; }
/// <summary>
/// 控制FlowData在同一时间只会被同一个线程更改。
/// </summary>
//private readonly ReaderWriterLockSlim _flowDataLock = new ReaderWriterLockSlim();
//private object _flowData;
///// <summary>
///// 当前传递数据(执行了节点对应的方法,才会存在值)。
///// </summary>
//protected object FlowData
//{
// get
// {
// _flowDataLock.EnterReadLock();
// try
// {
// return _flowData;
// }
// finally
// {
// _flowDataLock.ExitReadLock();
// }
// }
// set
// {
// _flowDataLock.EnterWriteLock();
// try
// {
// _flowData = value;
// }
// finally
// {
// _flowDataLock.ExitWriteLock();
// }
// }
//}
}
}
/*
/// <summary>
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
/// </summary>
public abstract partial class NodeModelBase : IDynamicFlowNode
{
/// <summary>
/// 节点保留对环境的引用,因为需要在属性更改时通知
/// </summary>
public IFlowEnvironment Env { get; }
/// <summary>
/// 在画布中的位置
/// </summary>
public PositionOfUI Position { get; set; }
/// <summary>
/// 附加的调试功能
/// </summary>
public NodeDebugSetting DebugSetting { get; set; }
/// <summary>
/// 描述节点对应的控件类型
/// </summary>
public NodeControlType ControlType { get; set; }
/// <summary>
/// 方法描述。不包含Method与委托需要通过MethodName从环境中获取委托进行调用。
/// </summary>
public MethodDetails MethodDetails { get; set; }
/// <summary>
/// 标识节点对象全局唯一
/// </summary>
public string Guid { get; set; }
/// <summary>
/// 显示名称
/// </summary>
public string DisplayName { get; set; } = string.Empty;
/// <summary>
/// 是否为起点控件
/// </summary>
public bool IsStart { get; set; }
/// <summary>
/// 运行时的上一节点
/// </summary>
public NodeModelBase PreviousNode { get; set; }
/// <summary>
/// 当前节点执行完毕后需要执行的下一个分支的类别
/// </summary>
public ConnectionType NextOrientation { get; set; } = ConnectionType.None;
/// <summary>
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
/// </summary>
public Exception RuningException { get; set; } = null;
}*/
/// <summary>
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
/// </summary>
//public class NodeModelBaseBuilder
//{
// public NodeModelBaseBuilder(NodeModelBase builder)
// {
// this.ControlType = builder.ControlType;
// this.MethodDetails = builder.MethodDetails;
// this.Guid = builder.Guid;
// this.DisplayName = builder.DisplayName;
// this.IsStart = builder.IsStart;
// this.PreviousNode = builder.PreviousNode;
// this.PreviousNodes = builder.PreviousNodes;
// this.SucceedBranch = builder.SucceedBranch;
// this.FailBranch = builder.FailBranch;
// this.ErrorBranch = builder.ErrorBranch;
// this.UpstreamBranch = builder.UpstreamBranch;
// this.FlowState = builder.FlowState;
// this.RuningException = builder.RuningException;
// this.FlowData = builder.FlowData;
// }
// /// <summary>
// /// 节点对应的控件类型
// /// </summary>
// public NodeControlType ControlType { get; }
// /// <summary>
// /// 方法描述对应DLL的方法
// /// </summary>
// public MethodDetails MethodDetails { get; }
// /// <summary>
// /// 节点guid
// /// </summary>
// public string Guid { get; }
// /// <summary>
// /// 显示名称
// /// </summary>
// public string DisplayName { get;}
// /// <summary>
// /// 是否为起点控件
// /// </summary>
// public bool IsStart { get; }
// /// <summary>
// /// 运行时的上一节点
// /// </summary>
// public NodeModelBase? PreviousNode { get; }
// /// <summary>
// /// 上一节点集合
// /// </summary>
// public List<NodeModelBase> PreviousNodes { get; } = [];
// /// <summary>
// /// 下一节点集合(真分支)
// /// </summary>
// public List<NodeModelBase> SucceedBranch { get; } = [];
// /// <summary>
// /// 下一节点集合(假分支)
// /// </summary>
// public List<NodeModelBase> FailBranch { get; } = [];
// /// <summary>
// /// 异常分支
// /// </summary>
// public List<NodeModelBase> ErrorBranch { get; } = [];
// /// <summary>
// /// 上游分支
// /// </summary>
// public List<NodeModelBase> UpstreamBranch { get; } = [];
// /// <summary>
// /// 当前执行状态(进入真分支还是假分支,异常分支在异常中确定)
// /// </summary>
// public FlowStateType FlowState { get; set; } = FlowStateType.None;
// /// <summary>
// /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
// /// </summary>
// public Exception RuningException { get; set; } = null;
// /// <summary>
// /// 当前传递数据(执行了节点对应的方法,才会存在值)
// /// </summary>
// public object? FlowData { get; set; } = null;
//}

View File

@@ -173,7 +173,7 @@ namespace Serein.Library
var cancelType = await upstreamNode.DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{upstreamNode.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
}
upstreamNode.PreviousNode = currentNode;
context.SetPreviousNode(upstreamNode, currentNode);
await upstreamNode.StartFlowAsync(context); // 执行流程节点的上游分支
if (context.NextOrientation == ConnectionInvokeType.IsError)
{
@@ -186,6 +186,7 @@ namespace Serein.Library
}
// 上游分支执行完成,才执行当前节点
if (IsBradk(context, flowCts)) break; // 退出执行
context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态
object newFlowData = await currentNode.ExecutingAsync(context);
if (IsBradk(context, flowCts)) break; // 退出执行
@@ -204,7 +205,7 @@ namespace Serein.Library
// 筛选出启用的节点的节点
if (nextNodes[i].DebugSetting.IsEnable)
{
nextNodes[i].PreviousNode = currentNode;
context.SetPreviousNode(nextNodes[i], currentNode);
stack.Push(nextNodes[i]);
}
}
@@ -248,7 +249,10 @@ namespace Serein.Library
{
object[] args = await GetParametersAsync(context, this, md);
var result = await dd.InvokeAsync(md.ActingInstance, args);
context.NextOrientation = ConnectionInvokeType.IsSucceed;
if(context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置
{
context.NextOrientation = ConnectionInvokeType.IsSucceed;
}
return result;
}
catch (Exception ex)
@@ -342,7 +346,10 @@ namespace Serein.Library
{
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase))
{
var previousFlowData = context.GetFlowData(nodeModel?.PreviousNode?.Guid); // 当前传递的数据
var previousNode = context.GetPreviousNode(nodeModel);
var previousFlowData = context.GetFlowData(previousNode.Guid); // 当前传递的数据
// 执行表达式从上一节点获取对象
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, previousFlowData, out _);
}
@@ -356,7 +363,8 @@ namespace Serein.Library
{
if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
{
inputParameter = context.GetFlowData(nodeModel?.PreviousNode?.Guid); // 当前传递的数据
var previousNode = context.GetPreviousNode(nodeModel);
inputParameter = context.GetFlowData(previousNode.Guid); // 当前传递的数据
}
else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
{
@@ -509,6 +517,7 @@ namespace Serein.Library
public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object newData = null)
{
string guid = nodeModel.Guid;
context.AddOrUpdate(guid, newData); // 上下文中更新数据
if (newData is null)
{
}
@@ -517,7 +526,6 @@ namespace Serein.Library
await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象
await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点
//nodeModel.FlowData = newData; // 替换数据
context.AddOrUpdate(guid, newData); // 上下文中更新数据
}
}

View File

@@ -174,7 +174,7 @@ namespace Serein.Library
{
if(_convertor is null)
{
return $"[{this.Index}] {this.Name} : {this.DataType.FullName}";
return $"[{this.Index}] {this.Name} : {this.DataType?.FullName}";
}
else
{

View File

@@ -2,7 +2,10 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Serein.Library.Utils.SereinExpression
{
@@ -152,53 +155,88 @@ namespace Serein.Library.Utils.SereinExpression
throw new ArgumentException($"Invalid array syntax for member {member}");
}
// 提取数组索引
var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
if (!int.TryParse(indexStr, out int index))
var targetType = target?.GetType(); // 目标对象的类型
if(targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
throw new ArgumentException($"Invalid array index '{indexStr}' for member {member}");
}
// 获取数组或集合对象
var arrayProperty = target?.GetType().GetProperty(arrayName);
if (arrayProperty is null)
{
var arrayField = target?.GetType().GetField(arrayName);
if (arrayField is null)
var typetmp = target.GetType().FullName;
// 目标是键值对
var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
var method = targetType.GetMethod("get_Item", BindingFlags.Public | BindingFlags.Instance);
if(method != null)
{
throw new ArgumentException($"Member {arrayName} not found on target.");
var result = method.Invoke(target, new object[] { indexStr });
if(result != null)
{
return result;
}
}
//var dict = target as Dictionary<string, string>;
////var dict = (Dictionary<dynamic, dynamic>)target;
//var temp = dict[indexStr];
////if (target is Dictionary<object, object> dict)
////{
//// var temp = dict[indexStr];
////}
//var TMP2= target.GetType().GetEnumValues();
}
else
{
#region
// 获取数组或集合对象
var arrayProperty = target?.GetType().GetProperty(arrayName);
if (arrayProperty is null)
{
var arrayField = target?.GetType().GetField(arrayName);
if (arrayField is null)
{
throw new ArgumentException($"Member {arrayName} not found on target.");
}
else
{
target = arrayField.GetValue(target);
}
}
else
{
target = arrayField.GetValue(target);
target = arrayProperty.GetValue(target);
}
}
else
{
target = arrayProperty.GetValue(target);
// 提取数组索引
var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
if (!int.TryParse(indexStr, out int index))
{
throw new ArgumentException($"Invalid array index '{indexStr}' for member {member}");
}
// 访问数组或集合中的指定索引
if (target is Array array)
{
if (index < 0 || index >= array.Length)
{
throw new ArgumentException($"Index {index} out of bounds for array {arrayName}");
}
target = array.GetValue(index);
}
else if (target is IList<object> list)
{
if (index < 0 || index >= list.Count)
{
throw new ArgumentException($"Index {index} out of bounds for list {arrayName}");
}
target = list[index];
}
else
{
throw new ArgumentException($"Member {arrayName} is not an array or list.");
}
#endregion
}
// 访问数组或集合中的指定索引
if (target is Array array)
{
if (index < 0 || index >= array.Length)
{
throw new ArgumentException($"Index {index} out of bounds for array {arrayName}");
}
target = array.GetValue(index);
}
else if (target is IList<object> list)
{
if (index < 0 || index >= list.Count)
{
throw new ArgumentException($"Index {index} out of bounds for list {arrayName}");
}
target = list[index];
}
else
{
throw new ArgumentException($"Member {arrayName} is not an array or list.");
}
}
else
{

View File

@@ -1438,8 +1438,7 @@ namespace Serein.NodeFlow.Env
try
{
Assembly assembly = Assembly.LoadFrom(dllPath); // 加载DLL文件
Type[] types = assembly.GetTypes(); // 获取程序集中的所有类型
List<Type> types = assembly.GetTypes().ToList(); // 获取程序集中的所有类型
Dictionary<RegisterSequence, List<Type>> autoRegisterTypes = new Dictionary<RegisterSequence, List<Type>>();
foreach (Type type in types)
{
@@ -1720,10 +1719,6 @@ namespace Serein.NodeFlow.Env
if (!string.IsNullOrEmpty(toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid))
{
//if(toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType == connectionArgSourceType)
//{
// return ;
//}
await RemoteConnectAsync(fromNode,toNode,argIndex); // 已经存在连接,将其移除
}
toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = fromNode.Guid;

View File

@@ -55,8 +55,7 @@ namespace Serein.NodeFlow.Env
var md = methodDetails.CloneOfNode(nodeModel.Env, nodeModel);
nodeModel.DisplayName = md.MethodAnotherName;
nodeModel.MethodDetails = md;
nodeModel.OnLoading();
return nodeModel;
}

View File

@@ -169,21 +169,6 @@ namespace Serein.NodeFlow
#endregion
#region 退
//foreach (var md in initMethods) // 初始化
//{
// md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
//}
//foreach (var md in loadingMethods) // 加载
//{
// md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
//}
//foreach (var md in exitMethods) // 初始化
//{
// md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
//}
#endregion
#region IOC容器
if (autoRegisterTypes.TryGetValue(RegisterSequence.FlowInit, out var flowInitTypes))
@@ -356,7 +341,9 @@ namespace Serein.NodeFlow
/// <param name="env">流程运行全局环境</param>
/// <param name="singleFlipFlopNode">需要全局监听信号的触发器</param>
/// <returns></returns>
private async Task FlipflopExecuteAsync(IFlowEnvironment env, SingleFlipflopNode singleFlipFlopNode, CancellationTokenSource cts)
private async Task FlipflopExecuteAsync(IFlowEnvironment env,
SingleFlipflopNode singleFlipFlopNode,
CancellationTokenSource cts)
{
if(_flipFlopCts is null)
{
@@ -369,34 +356,38 @@ namespace Serein.NodeFlow
try
{
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task
context.AddOrUpdate(singleFlipFlopNode.Guid, newFlowData);
await NodeModelBase.RefreshFlowDataAndExpInterrupt(context, singleFlipFlopNode, newFlowData); // 全局触发器触发后刷新该触发器的节点数据
if (context.NextOrientation == ConnectionInvokeType.None)
{
continue;
}
var nextNodes = singleFlipFlopNode.SuccessorNodes[context.NextOrientation];
for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--)
{
// 筛选出启用的节点
if (!nextNodes[i].DebugSetting.IsEnable)
_ = Task.Run(async () => {
var nextNodes = singleFlipFlopNode.SuccessorNodes[context.NextOrientation];
for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--)
{
continue;
}
// 筛选出启用的节点
if (!nextNodes[i].DebugSetting.IsEnable)
{
continue ;
}
nextNodes[i].PreviousNode = singleFlipFlopNode;
if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前
{
var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");
context.SetPreviousNode(nextNodes[i], singleFlipFlopNode);
if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前
{
var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");
}
await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点
context.Exit();
}
await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点
}
});
}
catch (FlipflopException ex)
{
await Console.Out.WriteLineAsync($"触发器[{singleFlipFlopNode.MethodDetails.MethodName}]因非预期异常终止。"+ex.Message);
if (ex.Type == FlipflopException.CancelClass.Flow)
if (ex.Type == FlipflopException.CancelClass.CancelFlow)
{
break;
}
@@ -407,7 +398,7 @@ namespace Serein.NodeFlow
}
finally
{
context.Exit();
}
}

View File

@@ -29,6 +29,13 @@ namespace Serein.NodeFlow.Model
}
/// <summary>
/// 加载完成后调用的方法
/// </summary>
public override void OnLoading()
{
Console.WriteLine("CompositeConditionNode 暂未实现 OnLoading");
}
public void AddNode(SingleConditionNode node)
{
@@ -59,7 +66,8 @@ namespace Serein.NodeFlow.Model
}
}
return Task.FromResult(context.GetFlowData(PreviousNode.Guid)); // 条件区域透传上一节点的数据
//var previousNode = context.GetPreviousNode()
return Task.FromResult(context.TransmissionData(this)); // 条件区域透传上一节点的数据
}

View File

@@ -13,6 +13,15 @@ namespace Serein.NodeFlow.Model
{
}
/// <summary>
/// 加载完成后调用的方法
/// </summary>
public override void OnLoading()
{
Console.WriteLine("SingleActionNode 暂未实现 OnLoading");
}
public override ParameterData[] GetParameterdatas()
{
if (base.MethodDetails.ParameterDetailss.Length > 0)

View File

@@ -39,8 +39,34 @@ namespace Serein.NodeFlow.Model
this.IsCustomData = false;
this.CustomData = null;
this.Expression = "PASS";
}
/// <summary>
/// 加载完成后调用的方法
/// </summary>
public override void OnLoading()
{
var pd = new ParameterDetails
{
Index = 0,
Name = "Exp",
DataType = typeof(object),
ExplicitType = typeof(object),
IsExplicitData = false,
DataValue = string.Empty,
ArgDataSourceNodeGuid = string.Empty,
ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData,
NodeModel = this,
Convertor = null,
ExplicitTypeName = "Value",
Items = Array.Empty<string>(),
};
this.MethodDetails.ParameterDetailss = new ParameterDetails[] { pd };
}
/// <summary>
/// 重写节点的方法执行
/// </summary>
@@ -50,7 +76,7 @@ namespace Serein.NodeFlow.Model
{
// 接收上一节点参数or自定义参数内容
object? parameter;
object? result = context.GetFlowData(PreviousNode.Guid); // 条件节点透传上一节点的数据
object? result = context.TransmissionData(this); // 条件节点透传上一节点的数据
if (IsCustomData) // 是否使用自定义参数
{
// 表达式获取上一节点数据

View File

@@ -28,10 +28,18 @@ namespace Serein.NodeFlow.Model
}
//public override async Task<object?> Executing(IDynamicContext context)
/// <summary>
/// 加载完成后调用的方法
/// </summary>
public override void OnLoading()
{
Console.WriteLine("SingleExpOpNode 暂未实现 OnLoading");
}
public override Task<object?> ExecutingAsync(IDynamicContext context)
{
var data = context.GetFlowData(PreviousNode.Guid); // 表达式节点使用上一节点数据
var data = context.TransmissionData(this); // 表达式节点使用上一节点数据
try
{

View File

@@ -16,6 +16,14 @@ namespace Serein.NodeFlow.Model
}
/// <summary>
/// 加载完成后调用的方法
/// </summary>
public override void OnLoading()
{
Console.WriteLine("SingleFlipflopNode 暂未实现 OnLoading");
}
/// <summary>
/// 执行触发器进行等待触发
@@ -56,7 +64,7 @@ namespace Serein.NodeFlow.Model
}
catch (FlipflopException ex)
{
if(ex.Type == FlipflopException.CancelClass.Flow)
if(ex.Type == FlipflopException.CancelClass.CancelFlow)
{
throw;
}

View File

@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>1.0.0</Version>
<!--<TargetFrameworks>net8.0</TargetFrameworks>-->
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>SereinFow</Title>
<Description>基础节点</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/fhhyyp/serein-flow</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Library\Serein.Library.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,52 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils.SereinExpression;
namespace Serein.BaseNode
{
public enum ExpType
{
Get,
Set
}
[DynamicFlow(Name ="基础节点")]
internal class SereinBaseNodes
{
[NodeAction(NodeType.Action,"条件节点")]
private bool SereinConditionNode(IDynamicContext context,
object targetObject,
string exp = "ISPASS")
{
var isPass = SereinConditionParser.To(targetObject, exp);
context.NextOrientation = isPass ? ConnectionInvokeType.IsSucceed : ConnectionInvokeType.IsFail;
return isPass;
}
[NodeAction(NodeType.Action, "表达式节点")]
private object SereinExpNode(IDynamicContext context,
object targetObject,
string exp)
{
exp = "@" + exp;
var newData = SerinExpressionEvaluator.Evaluate(exp, targetObject, out bool isChange);
object result;
if (isChange || exp.StartsWith("@GET",System.StringComparison.OrdinalIgnoreCase))
{
result = newData;
}
else
{
result = targetObject;
}
context.NextOrientation = ConnectionInvokeType.IsSucceed;
return result;
}
}
}

View File

@@ -20,11 +20,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\S
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net462DllTest", "Net462DllTest\Net462DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Extend.RemoteControl", "Extend.FlowRemoteManagement\Serein.Extend.RemoteControl.csproj", "{3E568C47-74C6-4C28-9D43-C9BA29008DB7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.FlowStartTool", "FlowStartTool\Serein.FlowStartTool.csproj", "{38D0FA92-5139-4616-A41E-8186AA4C1532}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.NodeGenerator", "Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj", "{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library.NodeGenerator", "Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj", "{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.BaseNode", "Serein.BaseNode\Serein.BaseNode.csproj", "{9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -56,10 +56,6 @@ Global
{E40EE629-1A38-4011-88E3-9AD036869987}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E40EE629-1A38-4011-88E3-9AD036869987}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E40EE629-1A38-4011-88E3-9AD036869987}.Release|Any CPU.Build.0 = Release|Any CPU
{3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Release|Any CPU.Build.0 = Release|Any CPU
{38D0FA92-5139-4616-A41E-8186AA4C1532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{38D0FA92-5139-4616-A41E-8186AA4C1532}.Debug|Any CPU.Build.0 = Debug|Any CPU
{38D0FA92-5139-4616-A41E-8186AA4C1532}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -68,6 +64,10 @@ Global
{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}.Release|Any CPU.Build.0 = Release|Any CPU
{9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -13,10 +13,10 @@ namespace Serein.Workbench
void LoadLocalProject()
{
#if DEBUG
if (1 == 1)
if (1 == 11)
{
string filePath;
filePath = @"F:\临时\project\linux\project.dnf";
filePath = @"F:\临时\project\linux\http\project.dnf";
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//

View File

@@ -89,15 +89,16 @@
<!--<Button Grid.Row="0" Content="卸载清空" Click="UnloadAllButton_Click" HorizontalAlignment="Right" Margin="5,5,5,5"/>--><!--
</Grid>-->
<!--暂时隐藏基础面板-->
<ScrollViewer Grid.Row="0" Visibility="Collapsed" HorizontalScrollBarVisibility="Auto">
<!--暂时隐藏基础面板 Visibility="Collapsed" -->
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto">
<StackPanel Orientation="Horizontal">
<nodeView:ExpOpNodeControl x:Name="ExpOpNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
<nodeView:ConditionNodeControl x:Name="ConditionNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
<nodeView:ConditionRegionControl x:Name="ConditionRegionControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
</StackPanel>
</ScrollViewer>
<ScrollViewer VerticalAlignment="Top" Grid.Row="1" VerticalScrollBarVisibility="Auto" MaxHeight="400" Grid.RowSpan="2">
<ScrollViewer VerticalAlignment="Top" Grid.Row="1" VerticalScrollBarVisibility="Auto" Grid.RowSpan="2">
<StackPanel x:Name="DllStackPanel" Margin="5"/>
</ScrollViewer>
<!--<GridSplitter Grid.Row="3" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Center" ResizeBehavior="PreviousAndNext" Background="Gray"/>-->

View File

@@ -511,10 +511,6 @@ namespace Serein.Workbench
#endregion
#endregion
}
}
/// <summary>

View File

@@ -17,7 +17,7 @@
<Grid>
<Grid.ToolTip>
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding NodeModel.MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" />
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding NodeModel.MethodDetails.MethodAnotherName, UpdateSourceTrigger=PropertyChanged}" />
</Grid.ToolTip>
<Grid.RowDefinitions>
@@ -25,16 +25,39 @@
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="#A8D8EA" BorderBrush="#A8D8EA" BorderThickness="1" HorizontalAlignment="Stretch">
<TextBlock Text="条件节点" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Grid Grid.Row="1" Background="#F1FFDF" HorizontalAlignment="Stretch">
<Grid Grid.Row="0" Background="#A8D8EA" >
<!--<Grid Grid.Row="0" >-->
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<local:ExecuteJunctionControl Grid.Column="0" MyNode="{Binding NodeModel}" x:Name="ExecuteJunctionControl" HorizontalAlignment="Left" Grid.RowSpan="2"/>
<Border Grid.Column="1" BorderThickness="1" HorizontalAlignment="Stretch">
<TextBlock Text="条件节点" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<local:NextStepJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="NextStepJunctionControl" HorizontalAlignment="Right" Grid.RowSpan="2"/>
</Grid>
<Grid Grid.Row="1" Background="#F1FFDF" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" IsChecked="{Binding NodeModel.IsCustomData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> <!--Converter={StaticResource BoolToVis}-->
<TextBox Grid.Column="1" MinWidth="50" Text="{Binding NodeModel.CustomData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
<local:ArgJunctionControl Grid.Column="0" x:Name="ArgJunctionControl" ArgIndex="0" MyNode="{Binding NodeModel}" />
<CheckBox Grid.Column="1" IsChecked="{Binding NodeModel.IsCustomData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center"/> <!--Converter={StaticResource BoolToVis}-->
<TextBox Grid.Column="2" MinWidth="50" Text="{Binding NodeModel.CustomData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch" VerticalAlignment="Center">
<TextBox.Style>
<Style TargetType="TextBox">
@@ -48,7 +71,7 @@
</TextBox.Style>
</TextBox>
<TextBlock Grid.Column="1" MinWidth="50" Text="上一节点数据" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<TextBlock Grid.Column="2" MinWidth="50" Text="上一节点数据" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed" />
@@ -60,9 +83,12 @@
</Style>
</TextBlock.Style>
</TextBlock>
<TextBox Grid.Row="1" Grid.ColumnSpan="3" Background="#F1FFDF" MinWidth="100" Text="{Binding NodeModel.Expression, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
</Grid>
<TextBox Grid.Row="2" Background="#f1F66F" MinWidth="100" Text="{Binding NodeModel.Expression, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
<StackPanel Grid.Row="2" Background="#A8D8EA">
<local:ResultJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="ResultJunctionControl" HorizontalAlignment="Right"/>
</StackPanel>
<!--<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
<Border Grid.Row="2" Background="#EAFFD0" BorderBrush="#EAFFD0" BorderThickness="1">

View File

@@ -6,7 +6,7 @@ namespace Serein.Workbench.Node.View
/// <summary>
/// ConditionNode.xaml 的交互逻辑
/// </summary>
public partial class ConditionNodeControl : NodeControlBase
public partial class ConditionNodeControl : NodeControlBase, INodeJunction
{
public ConditionNodeControl() : base()
{
@@ -21,6 +21,37 @@ namespace Serein.Workbench.Node.View
DataContext = viewModel;
InitializeComponent();
}
/// <summary>
/// 入参控制点(可能有,可能没)
/// </summary>
JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl;
/// <summary>
/// 下一个调用方法控制点(可能有,可能没)
/// </summary>
JunctionControlBase INodeJunction.NextStepJunction => this.NextStepJunctionControl;
/// <summary>
/// 返回值控制点(可能有,可能没)
/// </summary>
JunctionControlBase INodeJunction.ReturnDataJunction => this.ResultJunctionControl;
/// <summary>
/// 方法入参控制点(可能有,可能没)
/// </summary>
private JunctionControlBase[] argDataJunction;
/// <summary>
/// 方法入参控制点(可能有,可能没)
/// </summary>
JunctionControlBase[] INodeJunction.ArgDataJunction
{
get
{
argDataJunction = new JunctionControlBase[1];
argDataJunction[0] = this.ArgJunctionControl;
return argDataJunction;
}
}
}
}

View File

@@ -10,30 +10,30 @@ namespace Serein.Workbench.Node.ViewModel
{
public new SingleConditionNode NodeModel { get; }
///// <summary>
///// 是否为自定义参数
///// </summary>
//public bool IsCustomData
//{
// get => Node.IsCustomData;
// set { Node.IsCustomData= value; OnPropertyChanged(); }
//}
///// <summary>
// /// 自定义参数值
// /// </summary>
//public object? CustomData
//{
// get => Node.CustomData;
// set { Node.CustomData = value ; OnPropertyChanged(); }
//}
///// <summary>
///// 表达式
///// </summary>
//public string Expression
//{
// get => Node.Expression;
// set { Node.Expression = value; OnPropertyChanged(); }
//}
/// <summary>
/// 是否为自定义参数
/// </summary>
public bool IsCustomData
{
get => NodeModel.IsCustomData;
set { NodeModel.IsCustomData= value; OnPropertyChanged(); }
}
/// <summary>
/// 自定义参数值
/// </summary>
public object? CustomData
{
get => NodeModel.CustomData;
set { NodeModel.CustomData = value ; OnPropertyChanged(); }
}
/// <summary>
/// 表达式
/// </summary>
public string Expression
{
get => NodeModel.Expression;
set { NodeModel.Expression = value; OnPropertyChanged(); }
}
/// <summary>
/// 条件节点
@@ -42,9 +42,9 @@ namespace Serein.Workbench.Node.ViewModel
public ConditionNodeControlViewModel(SingleConditionNode node) : base(node)
{
this.NodeModel = node;
//IsCustomData = false;
//CustomData = "";
//Expression = "PASS";
IsCustomData = false;
CustomData = "";
Expression = "PASS";
}
}