优化了流程的进行

This commit is contained in:
fengjiayi
2024-09-15 22:07:10 +08:00
parent fe2ccaf74c
commit 61d40977ff
21 changed files with 153 additions and 117 deletions

View File

@@ -65,15 +65,15 @@ namespace Serein.Library.Core.NodeFlow
/// </summary> /// </summary>
public class FlipflopContext : IFlipflopContext public class FlipflopContext : IFlipflopContext
{ {
public FlowStateType State { get; set; } public FlipflopStateType State { get; set; }
public object Data { get; set; } public object Data { get; set; }
public FlipflopContext(FlowStateType ffState) public FlipflopContext(FlipflopStateType ffState)
{ {
State = ffState; State = ffState;
} }
public FlipflopContext(FlowStateType ffState, object data) public FlipflopContext(FlipflopStateType ffState, object data)
{ {
State = ffState; State = ffState;
Data = data; Data = data;

View File

@@ -17,7 +17,6 @@ namespace Serein.Library.Framework.NodeFlow
SereinIoc = sereinIoc; SereinIoc = sereinIoc;
FlowEnvironment = flowEnvironment; FlowEnvironment = flowEnvironment;
} }
public NodeRunCts NodeRunCts { get; set; } public NodeRunCts NodeRunCts { get; set; }
public ISereinIoc SereinIoc { get; } public ISereinIoc SereinIoc { get; }
public IFlowEnvironment FlowEnvironment { get; } public IFlowEnvironment FlowEnvironment { get; }

View File

@@ -5,24 +5,6 @@ using System.Threading.Tasks;
namespace Serein.Library.Framework.NodeFlow namespace Serein.Library.Framework.NodeFlow
{ {
//public enum FfState
//{
// Succeed,
// Cancel,
// Error,
//}
//public class FlipflopContext
//{
// public FlowStateType State { get; set; }
// public object? Data { get; set; }
// public FlipflopContext(FlowStateType ffState, object? data = null)
// {
// State = ffState;
// Data = data;
// }
//}
public static class FlipflopFunc public static class FlipflopFunc
{ {
/// <summary> /// <summary>
@@ -75,20 +57,19 @@ namespace Serein.Library.Framework.NodeFlow
/// </summary> /// </summary>
public class FlipflopContext : IFlipflopContext public class FlipflopContext : IFlipflopContext
{ {
public FlowStateType State { get; set; } public FlipflopStateType State { get; set; }
//public TResult? Data { get; set; } //public TResult? Data { get; set; }
public object Data { get; set; } public object Data { get; set; }
public FlipflopContext(FlowStateType ffState) public FlipflopContext(FlipflopStateType ffState)
{ {
State = ffState; State = ffState;
} }
public FlipflopContext(FlowStateType ffState, object data) public FlipflopContext(FlipflopStateType ffState, object data)
{ {
State = ffState; State = ffState;
Data = data; Data = data;
} }
} }
} }

View File

@@ -55,7 +55,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Library\Serein.Library.csproj"> <ProjectReference Include="..\Library\Serein.Library.csproj">
<Project>{5e19d0f2-913a-4d1c-a6f8-1e1227baa0e3}</Project> <Project>{55C77D23-2FD3-43D1-918C-DC3DE9614F0F}</Project>
<Name>Serein.Library</Name> <Name>Serein.Library</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>

View File

@@ -4,7 +4,7 @@ namespace Serein.Library.Api
{ {
public interface IFlipflopContext public interface IFlipflopContext
{ {
FlowStateType State { get; set; } FlipflopStateType State { get; set; }
object Data { get; set; } object Data { get; set; }
} }
} }

View File

@@ -6,6 +6,10 @@ namespace Serein.Library.Enums
{ {
public enum ConnectionType public enum ConnectionType
{ {
/// <summary>
/// 不执行分支
/// </summary>
None,
/// <summary> /// <summary>
/// 真分支 /// 真分支
/// </summary> /// </summary>

View File

@@ -7,12 +7,8 @@ using System.Threading.Tasks;
namespace Serein.Library.Enums namespace Serein.Library.Enums
{ {
public enum FlowStateType public enum FlipflopStateType
{ {
/// <summary>
/// 待执行
/// </summary>
None,
/// <summary> /// <summary>
/// 成功(方法成功执行) /// 成功(方法成功执行)
/// </summary> /// </summary>
@@ -25,5 +21,11 @@ namespace Serein.Library.Enums
/// 异常(节点没有成功执行,执行时发生非预期的错误) /// 异常(节点没有成功执行,执行时发生非预期的错误)
/// </summary> /// </summary>
Error, Error,
/// <summary>
/// 取消
/// </summary>
Cancel,
} }
} }

View File

@@ -0,0 +1,16 @@
using System;
namespace Serein.Library.Ex
{
/// <summary>
/// 触发器
/// </summary>
public class FlipflopException: Exception
{
public bool IsCancel { get; }
public FlipflopException(string message, bool isCancel = true) :base(message)
{
IsCancel = isCancel;
}
}
}

View File

@@ -27,7 +27,7 @@ namespace Serein.Library.Attributes
/// <summary> /// <summary>
/// 标记一个方法是什么类型加载dll后用来拖拽到画布中 /// 建议触发器手动设置返回类型
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class NodeActionAttribute : Attribute public class NodeActionAttribute : Attribute
@@ -45,9 +45,6 @@ namespace Serein.Library.Attributes
public bool Scan; public bool Scan;
public string MethodTips; public string MethodTips;
public NodeType MethodDynamicType; public NodeType MethodDynamicType;
/// <summary>
/// 推荐触发器手动设置返回类型
/// </summary>
public Type ReturnType; public Type ReturnType;
public string LockName; public string LockName;
} }

View File

@@ -1,6 +1,7 @@
using System; using Serein.Library.Ex;
using System.Threading.Tasks; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace Serein.Library.Core.NodeFlow.Tool namespace Serein.Library.Core.NodeFlow.Tool
{ {
@@ -13,9 +14,8 @@ namespace Serein.Library.Core.NodeFlow.Tool
// } // }
//} //}
public class TcsSignal<TSignal> where TSignal : struct, Enum public class TcsSignalFlipflop<TSignal> where TSignal : struct, Enum
{ {
//public ConcurrentDictionary<TSignal, Queue<TaskCompletionSource<object>>> TcsEvent { get; } = new();
public ConcurrentDictionary<TSignal, TaskCompletionSource<object>> TcsEvent { get; } = new ConcurrentDictionary<TSignal, TaskCompletionSource<object>>(); public ConcurrentDictionary<TSignal, TaskCompletionSource<object>> TcsEvent { get; } = new ConcurrentDictionary<TSignal, TaskCompletionSource<object>>();
public ConcurrentDictionary<TSignal, object> TcsLock { get; } = new ConcurrentDictionary<TSignal, object>(); public ConcurrentDictionary<TSignal, object> TcsLock { get; } = new ConcurrentDictionary<TSignal, object>();
@@ -56,7 +56,7 @@ namespace Serein.Library.Core.NodeFlow.Tool
{ {
foreach (var tcs in TcsEvent.Values) foreach (var tcs in TcsEvent.Values)
{ {
tcs.SetException(new Exception("任务取消")); tcs.SetException(new FlipflopException("任务取消"));
} }
TcsEvent.Clear(); TcsEvent.Clear();
} }

View File

@@ -1,10 +1,6 @@
using Newtonsoft.Json; using Serein.Library.Api;
using Serein.Library.Api;
using Serein.Library.Entity; using Serein.Library.Entity;
using Serein.Library.Enums; using Serein.Library.Enums;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Tool.SerinExpression;
using System.Xml.Linq;
namespace Serein.NodeFlow.Base namespace Serein.NodeFlow.Base
{ {
@@ -65,12 +61,14 @@ namespace Serein.NodeFlow.Base
/// <summary> /// <summary>
/// 不同分支的子节点 /// 不同分支的子节点
/// </summary> /// </summary>
public Dictionary<ConnectionType,List<NodeModelBase>> SuccessorNodes { get; } public Dictionary<ConnectionType,List<NodeModelBase>> SuccessorNodes { get; }
public ConnectionType NextOrientation { get; set; } = ConnectionType.None;
/// <summary> /// <summary>
/// 当前执行状态(进入真分支还是假分支,异常分支在异常中确定) /// 当前执行状态(进入真分支还是假分支,异常分支在异常中确定)
/// </summary> /// </summary>
public FlowStateType FlowState { get; set; } = FlowStateType.None; // public FlowStateType FlowState { get; set; } = FlowStateType.Cancel;
/// <summary> /// <summary>
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值) /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)

View File

@@ -2,6 +2,7 @@
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Entity; using Serein.Library.Entity;
using Serein.Library.Enums; using Serein.Library.Enums;
using Serein.Library.Ex;
using Serein.NodeFlow.Tool.SerinExpression; using Serein.NodeFlow.Tool.SerinExpression;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -92,14 +93,13 @@ namespace Serein.NodeFlow.Base
currentNode.FlowData = currentNode.Execute(context); currentNode.FlowData = currentNode.Execute(context);
} }
ConnectionType connection = currentNode.FlowState switch if(currentNode.NextOrientation == ConnectionType.None)
{ {
FlowStateType.Succeed => ConnectionType.IsSucceed, // 不再执行
FlowStateType.Fail => ConnectionType.IsFail, break;
FlowStateType.Error => ConnectionType.IsError, }
_ => throw new Exception("非预期的枚举值")
}; var nextNodes = currentNode.SuccessorNodes[currentNode.NextOrientation];
var nextNodes = currentNode.SuccessorNodes[connection];
// 将下一个节点集合中的所有节点逆序推入栈中 // 将下一个节点集合中的所有节点逆序推入栈中
for (int i = nextNodes.Count - 1; i >= 0; i--) for (int i = nextNodes.Count - 1; i >= 0; i--)
@@ -148,12 +148,12 @@ namespace Serein.NodeFlow.Base
result = func?.Invoke(md.ActingInstance, parameters); result = func?.Invoke(md.ActingInstance, parameters);
} }
} }
FlowState = FlowStateType.Succeed; NextOrientation = ConnectionType.IsSucceed;
return result; return result;
} }
catch (Exception ex) catch (Exception ex)
{ {
FlowState = FlowStateType.Error; NextOrientation = ConnectionType.IsError;
RuningException = ex; RuningException = ex;
} }
@@ -184,23 +184,16 @@ namespace Serein.NodeFlow.Base
object?[]? parameters = GetParameters(context, MethodDetails); object?[]? parameters = GetParameters(context, MethodDetails);
flipflopContext = await ((Func<object, object[], Task<IFlipflopContext>>)md.MethodDelegate).Invoke(MethodDetails.ActingInstance, parameters); flipflopContext = await ((Func<object, object[], Task<IFlipflopContext>>)md.MethodDelegate).Invoke(MethodDetails.ActingInstance, parameters);
} }
if (flipflopContext == null)
if (flipflopContext != null)
{ {
FlowState = flipflopContext.State; throw new FlipflopException("没有返回上下文");
if (flipflopContext.State == FlowStateType.Succeed)
{
result = flipflopContext.Data;
}
else
{
result = null;
}
} }
NextOrientation = flipflopContext.State.ToContentType();
result = flipflopContext.Data;
} }
catch (Exception ex) catch (Exception ex)
{ {
FlowState = FlowStateType.Error; NextOrientation = ConnectionType.IsError;
RuningException = ex; RuningException = ex;
} }

View File

@@ -7,6 +7,7 @@ using Serein.NodeFlow.Base;
using Serein.NodeFlow.Model; using Serein.NodeFlow.Model;
using Serein.NodeFlow.Tool; using Serein.NodeFlow.Tool;
using System.Diagnostics; using System.Diagnostics;
using System.Net.Mime;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Xml.Linq; using System.Xml.Linq;
@@ -618,6 +619,18 @@ namespace Serein.NodeFlow
Path = assembly.Location, Path = assembly.Location,
}; };
} }
public static ConnectionType ToContentType(this FlipflopStateType flowStateType)
{
return flowStateType switch
{
FlipflopStateType.Succeed => ConnectionType.IsSucceed,
FlipflopStateType.Fail => ConnectionType.IsFail,
FlipflopStateType.Error => ConnectionType.IsError,
FlipflopStateType.Cancel => ConnectionType.None,
_ => throw new NotImplementedException("未定义的流程状态")
};
}
} }

View File

@@ -159,11 +159,14 @@ namespace Serein.NodeFlow
IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters); IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters);
if (flipflopContext.State == FlowStateType.Succeed) ConnectionType connection = flipflopContext.State.ToContentType();
if (connection != ConnectionType.None)
{ {
singleFlipFlopNode.FlowState = FlowStateType.Succeed; singleFlipFlopNode.NextOrientation = connection;
singleFlipFlopNode.FlowData = flipflopContext.Data; singleFlipFlopNode.FlowData = flipflopContext.Data;
var tasks = singleFlipFlopNode.SuccessorNodes[ConnectionType.IsSucceed].Select(nextNode =>
var tasks = singleFlipFlopNode.SuccessorNodes[connection].Select(nextNode =>
{ {
var context = new DynamicContext(SereinIoc,flowEnvironment); var context = new DynamicContext(SereinIoc,flowEnvironment);
nextNode.PreviousNode = singleFlipFlopNode; nextNode.PreviousNode = singleFlipFlopNode;

View File

@@ -26,50 +26,38 @@ namespace Serein.NodeFlow.Model
/// <returns></returns> /// <returns></returns>
public override object? Execute(IDynamicContext context) public override object? Execute(IDynamicContext context)
{ {
// bool allTrue = ConditionNodes.All(condition => Judge(context,condition.MethodDetails)); // NextOrientation = ConnectionType.IsSucceed;
// bool IsAllTrue = true; // 初始化为 true
FlowState = FlowStateType.Succeed; // 条件区域中遍历每个条件节点
foreach (SingleConditionNode? node in ConditionNodes) foreach (SingleConditionNode? node in ConditionNodes)
{ {
var state = Judge(context, node); var state = Judge(context, node);
if (state == FlowStateType.Fail || FlowStateType.Fail == FlowStateType.Error) NextOrientation = state; // 每次判读完成后,设置区域后继方向为判断结果
if (state != ConnectionType.IsSucceed)
{ {
FlowState = state; // 如果条件不通过,立刻推出循环
break;// 一旦发现条件为假,立即退出循环 break;
} }
} }
return PreviousNode?.FlowData; return PreviousNode?.FlowData;
//if (IsAllTrue)
//{
// foreach (var nextNode in TrueBranchNextNodes)
// {
// nextNode.ExecuteStack(context);
// }
//}
//else
//{
// foreach (var nextNode in FalseBranchNextNodes)
// {
// nextNode.ExecuteStack(context);
// }
//}
} }
private ConnectionType Judge(IDynamicContext context, SingleConditionNode node)
private FlowStateType Judge(IDynamicContext context, SingleConditionNode node)
{ {
try try
{ {
node.Execute(context); node.Execute(context);
return node.FlowState; return node.NextOrientation;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.Message); Console.WriteLine(ex.Message);
return FlowStateType.Error; NextOrientation = ConnectionType.IsError;
RuningException = ex;
return ConnectionType.IsError;
} }
} }

View File

@@ -43,15 +43,15 @@ namespace Serein.NodeFlow.Model
try try
{ {
var isPass = SerinConditionParser.To(result, Expression); var isPass = SerinConditionParser.To(result, Expression);
FlowState = isPass ? FlowStateType.Succeed : FlowStateType.Fail; NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail;
} }
catch (Exception ex) catch (Exception ex)
{ {
FlowState = FlowStateType.Error; NextOrientation = ConnectionType.IsError;
RuningException = ex; RuningException = ex;
} }
Console.WriteLine($"{result} {Expression} -> " + FlowState); Console.WriteLine($"{result} {Expression} -> " + NextOrientation);
return result; return result;
} }

View File

@@ -3,6 +3,7 @@ using Serein.Library.Entity;
using Serein.Library.Enums; using Serein.Library.Enums;
using Serein.NodeFlow.Base; using Serein.NodeFlow.Base;
using Serein.NodeFlow.Tool.SerinExpression; using Serein.NodeFlow.Tool.SerinExpression;
using System.Text;
namespace Serein.NodeFlow.Model namespace Serein.NodeFlow.Model
{ {
@@ -21,16 +22,27 @@ namespace Serein.NodeFlow.Model
{ {
var data = PreviousNode?.FlowData; var data = PreviousNode?.FlowData;
var newData = SerinExpressionEvaluator.Evaluate(Expression, data, out bool isChange); try
{
var newData = SerinExpressionEvaluator.Evaluate(Expression, data, out bool isChange);
Console.WriteLine(newData);
object? result = null;
if (isChange)
{
result = newData;
}
else
{
result = PreviousNode?.FlowData;
}
FlowState = FlowStateType.Succeed; NextOrientation = ConnectionType.IsSucceed;
Console.WriteLine(newData); return result;
if (isChange)
{
return newData;
} }
else catch (Exception ex)
{ {
NextOrientation = ConnectionType.IsError;
RuningException = ex;
return PreviousNode?.FlowData; return PreviousNode?.FlowData;
} }

View File

@@ -1,5 +1,6 @@
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Entity; using Serein.Library.Entity;
using Serein.Library.Ex;
using Serein.NodeFlow.Base; using Serein.NodeFlow.Base;
namespace Serein.NodeFlow.Model namespace Serein.NodeFlow.Model
@@ -7,9 +8,11 @@ namespace Serein.NodeFlow.Model
public class SingleFlipflopNode : NodeModelBase public class SingleFlipflopNode : NodeModelBase
{ {
public override object Execute(IDynamicContext context) public override object? Execute(IDynamicContext context)
{ {
throw new NotImplementedException("无法以非await/async的形式调用触发器"); NextOrientation = Library.Enums.ConnectionType.IsError;
RuningException = new FlipflopException ("无法以非await/async的形式调用触发器");
return null;
} }
public override Parameterdata[] GetParameterdatas() public override Parameterdata[] GetParameterdatas()

View File

@@ -67,6 +67,10 @@ public static class MethodDetailsHelperTmp
var methodName = method.Name; var methodName = method.Name;
var attribute = method.GetCustomAttribute<NodeActionAttribute>(); var attribute = method.GetCustomAttribute<NodeActionAttribute>();
if(attribute is null)
{
return null;
}
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters()); var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
// 生成委托 // 生成委托
var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型 var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
@@ -74,6 +78,16 @@ public static class MethodDetailsHelperTmp
method.GetParameters(),// 方法参数 method.GetParameters(),// 方法参数
method.ReturnType);// 返回值 method.ReturnType);// 返回值
Type returnType;
if (attribute?.MethodDynamicType == Library.Enums.NodeType.Flipflop)
{
// 触发器节点
returnType = attribute.ReturnType;
}
else
{
returnType = method.ReturnType;
}
var dllTypeName = $"{assemblyName}.{type.Name}"; var dllTypeName = $"{assemblyName}.{type.Name}";
// object instance = Activator.CreateInstance(type); // object instance = Activator.CreateInstance(type);
@@ -89,7 +103,7 @@ public static class MethodDetailsHelperTmp
MethodLockName = attribute.LockName, MethodLockName = attribute.LockName,
MethodTips = attribute.MethodTips, MethodTips = attribute.MethodTips,
ExplicitDatas = explicitDataOfParameters, ExplicitDatas = explicitDataOfParameters,
ReturnType = method.ReturnType, ReturnType = returnType,
}; };
} }

View File

@@ -3,10 +3,11 @@
不定期在Bilibili个人空间上更新相关的视频。 不定期在Bilibili个人空间上更新相关的视频。
https://space.bilibili.com/33526379 https://space.bilibili.com/33526379
# 当前任务 2024年9月12日22:32:10 # 当前任务 2024年9月15日22:04:40
1. 将运行环境从UIWPF中独立出来方便控制台程序直接运行项目文件。 * 计划新增基础节点“属性包装器”,用来收集各个节点的数据,包装成匿名对象/Json类型
2. 包装数据类型, 优化传递效率(尽可能避免拆箱、装箱) * 计划编写网络方面的通讯,方便传出、传入数据
3. 优化触发器节点(显示传出类型 * 包装数据类型, 优化传递效率(尽可能避免拆箱、装箱
# 如何加载我的DLL # 如何加载我的DLL
使用 **DynamicFlow** 特性标记你的类,可以参照 **MyDll****SereinWAT** 的实现。编译为 Dll文件 后,拖入到软件中即可。 使用 **DynamicFlow** 特性标记你的类,可以参照 **MyDll****SereinWAT** 的实现。编译为 Dll文件 后,拖入到软件中即可。

View File

@@ -28,6 +28,18 @@
</Border> </Border>
<!--<themes:ExplicitDataControl Grid.Row="1" ExplicitDatas="{Binding ExplicitDatas}" />--> <!--<themes:ExplicitDataControl Grid.Row="1" ExplicitDatas="{Binding ExplicitDatas}" />-->
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" /> <themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
<Grid Grid.Row="2" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="#EAFFD0" BorderBrush="#EAFFD0" BorderThickness="1">
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Grid.Column="1" Background="#EAFFD0" BorderBrush="#EAFFD0" BorderThickness="1">
<TextBlock Text="{Binding MethodDetails.ReturnType}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</Grid>
<!--<themes:ConditionControl Grid.Row="2" ></themes:ConditionControl>--> <!--<themes:ConditionControl Grid.Row="2" ></themes:ConditionControl>-->
</Grid> </Grid>
</local:NodeControlBase> </local:NodeControlBase>