diff --git a/Library/Enums/NodeType.cs b/Library/Enums/NodeType.cs index 2b23cb6..10d2c56 100644 --- a/Library/Enums/NodeType.cs +++ b/Library/Enums/NodeType.cs @@ -9,15 +9,15 @@ namespace Serein.Library.Enums public enum NodeType { /// - /// 初始化(事件,不生成节点) + /// 初始化,流程启动时执行(不生成节点) /// Init, /// - /// 开始载入(事件,不生成节点) + /// 开始载入,流程启动时执行(不生成节点) /// Loading, /// - /// 结束(事件,不生成节点) + /// 结束,流程结束时执行(不生成节点) /// Exit, diff --git a/Net462DllTest/LogicControl/ParkingLogicControl.cs b/Net462DllTest/LogicControl/ParkingLogicControl.cs index 37aabaa..8976855 100644 --- a/Net462DllTest/LogicControl/ParkingLogicControl.cs +++ b/Net462DllTest/LogicControl/ParkingLogicControl.cs @@ -1,7 +1,4 @@ - -using Net462DllTest.Signal; -using Net462DllTest.Trigger; -using Net462DllTest.ViewModel; +using Net462DllTest.Trigger; using Serein.Library.Api; using Serein.Library.Attributes; using Serein.Library.Enums; @@ -9,9 +6,6 @@ using Serein.Library.Ex; using Serein.Library.Framework.NodeFlow; using Serein.Library.NodeFlow.Tool; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace Net462DllTest.LogicControl @@ -79,5 +73,7 @@ namespace Net462DllTest.LogicControl Console.WriteLine("发送命令失败:调取车位" + spaceNum); } } + + } } diff --git a/Net462DllTest/LogicControl/PlcLogicControl.cs b/Net462DllTest/LogicControl/PlcLogicControl.cs index 52c8b36..d7a575e 100644 --- a/Net462DllTest/LogicControl/PlcLogicControl.cs +++ b/Net462DllTest/LogicControl/PlcLogicControl.cs @@ -41,7 +41,7 @@ namespace Net462DllTest.LogicControl } #region 初始化、初始化完成以及退出的事件 - [NodeAction(NodeType.Init)] // Init : 初始化事件,流程启动时执行 + [NodeAction(NodeType.Init)] public void Init(IDynamicContext context) { context.Env.IOC.Register(); @@ -108,6 +108,15 @@ namespace Net462DllTest.LogicControl #region 动作节点 + [NodeAction(NodeType.Action, "等待")] + public async Task Delay(int ms = 5000) + { + await Console.Out.WriteLineAsync("开始等待"); + await Task.Delay(ms); + await Console.Out.WriteLineAsync("不再等待"); + + } + [NodeAction(NodeType.Action, "PLC初始化")] public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None, string ip = "192.168.10.100", @@ -153,7 +162,7 @@ namespace Net462DllTest.LogicControl } [NodeAction(NodeType.Action, "PLC写入变量")] - public SiemensPlcDevice WriteVar2(object value, PlcVarName varName) + public SiemensPlcDevice WriteVar(object value, PlcVarName varName) { var varInfo = varName.ToVarInfo(); if (MyPlc.State == PlcState.Runing) @@ -176,7 +185,6 @@ namespace Net462DllTest.LogicControl return MyPlc; } - [NodeAction(NodeType.Action, "批量读取")] public void BatchReadVar() { diff --git a/Net462DllTest/Main.cs b/Net462DllTest/Main.cs new file mode 100644 index 0000000..578a642 --- /dev/null +++ b/Net462DllTest/Main.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Net462DllTest.Properties +{ + /* + 理想的项目架构: + FlowEnv - LoginControl: + + + LoginControl + ↙ ↘ + (View-Interaction) (Node-Interaction) + ↓ ↕ + View ←→ ViewModel ←→ Trigger ← SingleEnum + ↓ + Model + · DataChanged → Trigger + + + 视图驱动触发器,触发器驱动数据。 + 数据驱动触发器,触发器驱动视图。 + + 所以,这个结构≈事件驱动。 + + + 动态的配置事件触发的原因、过程与结果。 + + */ +} diff --git a/Net462DllTest/Model/PlcVarModel.cs b/Net462DllTest/Model/PlcVarModel.cs index e0222f7..90ac957 100644 --- a/Net462DllTest/Model/PlcVarModel.cs +++ b/Net462DllTest/Model/PlcVarModel.cs @@ -9,21 +9,6 @@ using System.Threading.Tasks; namespace Net462DllTest.Model { - - [AttributeUsage(AttributeTargets.Property)] - public class PlcValueAttribute : Attribute - { - /// - /// 变量类型 - /// - public PlcVarName PlcVarEnum { get; } - public PlcValueAttribute(PlcVarName plcVarEnum) - { - this.PlcVarEnum = plcVarEnum; - } - } - - /// /// PLC变量 /// diff --git a/Net462DllTest/Net462DllTest.csproj b/Net462DllTest/Net462DllTest.csproj index 29a2c0e..d3e5791 100644 --- a/Net462DllTest/Net462DllTest.csproj +++ b/Net462DllTest/Net462DllTest.csproj @@ -61,6 +61,7 @@ + diff --git a/Net462DllTest/Trigger/PrakingDevice.cs b/Net462DllTest/Trigger/PrakingDevice.cs index 9da1b17..10420a6 100644 --- a/Net462DllTest/Trigger/PrakingDevice.cs +++ b/Net462DllTest/Trigger/PrakingDevice.cs @@ -12,7 +12,6 @@ namespace Net462DllTest.Trigger [AutoRegister] public class PrakingDevice : ChannelFlowTrigger { - } } diff --git a/Net462DllTest/Utils/GSModel.cs b/Net462DllTest/Utils/GSModel.cs index a94919c..a751c50 100644 --- a/Net462DllTest/Utils/GSModel.cs +++ b/Net462DllTest/Utils/GSModel.cs @@ -53,6 +53,8 @@ namespace Net462DllTest.Utils il.Emit(OpCodes.Callvirt, property.GetSetMethod()); // 调用属性的Setter方法 il.Emit(OpCodes.Ret); // 返回 + + return (Action)method.CreateDelegate(typeof(Action)); } diff --git a/Net462DllTest/Utils/RelayCommand.cs b/Net462DllTest/Utils/RelayCommand.cs index e3a77f3..3ae40cf 100644 --- a/Net462DllTest/Utils/RelayCommand.cs +++ b/Net462DllTest/Utils/RelayCommand.cs @@ -7,6 +7,9 @@ using System.Windows.Input; namespace Net462DllTest.Utils { + /// + /// 用于窗体View与ViewModel进行交互 + /// public class RelayCommand : ICommand { private readonly Action _execute; diff --git a/NodeFlow/Base/NodeModelBaseFunc.cs b/NodeFlow/Base/NodeModelBaseFunc.cs index df3210d..d5df6d3 100644 --- a/NodeFlow/Base/NodeModelBaseFunc.cs +++ b/NodeFlow/Base/NodeModelBaseFunc.cs @@ -6,8 +6,10 @@ using Serein.Library.Entity; using Serein.Library.Enums; using Serein.Library.Ex; using Serein.Library.Utils; +using Serein.NodeFlow.Tool; using Serein.NodeFlow.Tool.SereinExpression; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -101,48 +103,48 @@ namespace Serein.NodeFlow.Base { Stack stack = new Stack(); stack.Push(this); - var cts = context.Env.IOC.Get(FlowStarter.FlipFlopCtsName); + var flowCts = context.Env.IOC.Get(FlowStarter.FlipFlopCtsName); while (stack.Count > 0 ) // 循环中直到栈为空才会退出循环 { - if(cts is not null) + if(flowCts is not null) { - if (cts.IsCancellationRequested) + if (flowCts.IsCancellationRequested) break; } - // 节点执行异常时跳过执行 - // 从栈中弹出一个节点作为当前节点进行处理 var currentNode = stack.Pop(); - //// 设置方法执行的对象 - //if (currentNode.MethodDetails?.ActingInstance is not null && currentNode.MethodDetails?.ActingInstanceType is not null) - //{ - // currentNode.MethodDetails.ActingInstance = context.Env.IOC.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType); - //} - #region 执行相关 - // 首先执行上游分支 - var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream]; - for (int i = upstreamNodes.Count - 1; i >= 0; i--) + // 筛选出上游分支 + var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].Where( + node => node.DebugSetting.IsEnable + ).ToArray(); + // 执行上游分支 + foreach (var upstreamNode in upstreamNodes) { - // 筛选出启用的节点 - if (upstreamNodes[i].DebugSetting.IsEnable) + if (upstreamNode.DebugSetting.IsEnable) { - if (upstreamNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前 + if (upstreamNode.DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前 { - var cancelType = await upstreamNodes[i].DebugSetting.GetInterruptTask(); - await Console.Out.WriteLineAsync($"[{upstreamNodes[i]?.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支"); + var cancelType = await upstreamNode.DebugSetting.GetInterruptTask(); + await Console.Out.WriteLineAsync($"[{upstreamNode.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支"); + } + upstreamNode.PreviousNode = currentNode; + await upstreamNode.StartExecute(context); // 执行流程节点的上游分支 + if (upstreamNode.NextOrientation == ConnectionType.IsError) + { + // 如果上游分支执行失败,不再继续执行 + // 使上游节点(仅上游节点本身,不包含上游节点的后继节点) + // 具备通过抛出异常中断流程的能力 + break; } - upstreamNodes[i].PreviousNode = currentNode; - await upstreamNodes[i].StartExecute(context); // 执行流程节点的上游分支 } } - - - // 执行当前节点 + + // 上游分支执行完成,才执行当前节点 object? newFlowData = await currentNode.ExecutingAsync(context); - if (cts is null || cts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None) + if (flowCts is null || flowCts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None) { // 不再执行 break; @@ -159,14 +161,9 @@ namespace Serein.NodeFlow.Base // 将下一个节点集合中的所有节点逆序推入栈中 for (int i = nextNodes.Count - 1; i >= 0; i--) { - // 筛选出启用的节点、未被中断的节点 - if (nextNodes[i].DebugSetting.IsEnable /*&& nextNodes[i].DebugSetting.InterruptClass == InterruptClass.None*/) + // 筛选出启用的节点的节点 + if (nextNodes[i].DebugSetting.IsEnable) { - if (nextNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前 - { - var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask(); - await Console.Out.WriteLineAsync($"[{nextNodes[i]?.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支"); - } nextNodes[i].PreviousNode = currentNode; stack.Push(nextNodes[i]); } @@ -176,7 +173,7 @@ namespace Serein.NodeFlow.Base } } - + /// /// 执行节点对应的方法 /// @@ -186,14 +183,9 @@ namespace Serein.NodeFlow.Base { #region 调试中断 - if (DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前 + if (DebugSetting.InterruptClass != InterruptClass.None) // 执行触发检查是否需要中断 { - var cancelType = await this.DebugSetting.GetInterruptTask(); - //if(cancelType == CancelType.Discard) - //{ - // this.NextOrientation = ConnectionType.None; - // return null; - //} + var cancelType = await this.DebugSetting.GetInterruptTask(); // 等待中断结束 await Console.Out.WriteLineAsync($"[{this.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支"); } @@ -212,19 +204,41 @@ namespace Serein.NodeFlow.Base md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType); object instance = md.ActingInstance; - var haveParameter = md.ExplicitDatas.Length > 0; - var haveResult = md.ReturnType != typeof(void); + bool haveParameter = md.ExplicitDatas.Length > 0; + bool haveResult = md.ReturnType != typeof(void); + Type? taskResult = null; + bool isTask = md.ReturnType is not null && MethodDetailsHelperTmp.IsGenericTask(md.ReturnType, out taskResult); + bool isTaskHaveResult = taskResult is not null; + object? result; + + Console.WriteLine($"(isTask, isTaskHaveResult):{(isTask, isTaskHaveResult)}"); try { // Action/Func([方法作用的实例],[可能的参数值],[可能的返回值]) + object?[]? parameters = GetParameters(context, this, md); - object? result = (haveParameter, haveResult) switch + if (isTask) { - (false, false) => Execution((Action)del, instance), // 调用节点方法,返回null - (true, false) => Execution((Action)del, instance, parameters), // 调用节点方法,返回null - (false, true) => Execution((Func)del, instance), // 调用节点方法,返回方法传回类型 - (true, true) => Execution((Func)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法忏悔类型 - }; + // 异步方法(因为返回了Task,所以排除Action<>委托的可能) + result = (haveParameter, isTaskHaveResult) switch + { + (false, false) => await ExecutionAsync((Func)del, instance), // 调用节点方法,返回方法传回类型 + (true, false) => await ExecutionAsync((Func)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型 + (false, true) => await ExecutionAsync((Func>)del, instance), // 调用节点方法,返回方法传回类型 + (true, true) => await ExecutionAsync((Func>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型 + }; + } + else + { + // 非异步方法 + result = (haveParameter, haveResult) switch + { + (false, false) => Execution((Action)del, instance), // 调用节点方法,返回null + (true, false) => Execution((Action)del, instance, parameters), // 调用节点方法,返回null + (false, true) => Execution((Func)del, instance), // 调用节点方法,返回方法传回类型 + (true, true) => Execution((Func)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型 + }; + } NextOrientation = ConnectionType.IsSucceed; return result; @@ -242,21 +256,41 @@ namespace Serein.NodeFlow.Base #region 节点转换的委托类型 public static object? Execution(Action del, object instance) { - del?.Invoke(instance); + del.Invoke(instance); return null; } public static object? Execution(Action del, object instance, object?[]? parameters) { - del?.Invoke(instance, parameters); + del.Invoke(instance, parameters); return null; } public static object? Execution(Func del, object instance) { - return del?.Invoke(instance); + return del.Invoke(instance); } public static object? Execution(Func del, object instance, object?[]? parameters) { - return del?.Invoke(instance, parameters); + return del.Invoke(instance, parameters); + } + + + public static async Task ExecutionAsync(Func del, object instance) + { + await del.Invoke(instance); + return null; + } + public static async Task ExecutionAsync(Func del, object instance, object?[]? parameters) + { + await del.Invoke(instance, parameters); + return null; + } + public static async Task ExecutionAsync(Func> del, object instance) + { + return await del.Invoke(instance); + } + public static async Task ExecutionAsync(Func> del, object instance, object?[]? parameters) + { + return await del.Invoke(instance, parameters); } #endregion diff --git a/NodeFlow/Tool/ExpressionHelper.cs b/NodeFlow/Tool/ExpressionHelper.cs index 832d86b..53395fc 100644 --- a/NodeFlow/Tool/ExpressionHelper.cs +++ b/NodeFlow/Tool/ExpressionHelper.cs @@ -146,9 +146,26 @@ namespace Serein.NodeFlow.Tool { var parameter = Expression.Parameter(typeof(object), "instance"); var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo); - var lambda = Expression.Lambda(Expression.Convert(methodCall, typeof(object)), parameter); - // Func - return lambda.Compile(); + + if(MethodDetailsHelperTmp.IsGenericTask(methodInfo.ReturnType,out var taskResult)) + { + if(taskResult is null) + { + var lambda = Expression.Lambda>(Expression.Convert(methodCall, typeof(Task)), parameter); + return lambda.Compile(); + } + else + { + var lambda = Expression.Lambda>>(Expression.Convert(methodCall, typeof(Task)), parameter); + return lambda.Compile(); + } + } + else + { + var lambda = Expression.Lambda>(Expression.Convert(methodCall, typeof(object)), parameter); + return lambda.Compile(); + } + } @@ -262,14 +279,29 @@ namespace Serein.NodeFlow.Tool convertedArgs ); - // 创建 lambda 表达式 - var lambda = Expression.Lambda>( - Expression.Convert(methodCall, typeof(object)), - instanceParam, - argsParam - ); - //var resule = task.DynamicInvoke((object)[Activator.CreateInstance(type), [new DynamicContext(null)]]); - return lambda.Compile(); + if (MethodDetailsHelperTmp.IsGenericTask(methodInfo.ReturnType, out var taskResult)) + { + if (taskResult is null) + { + var lambda = Expression.Lambda> + (Expression.Convert(methodCall, typeof(Task)), instanceParam, argsParam); + return lambda.Compile(); + } + else + { + var lambda = Expression.Lambda>> + (Expression.Convert(methodCall, typeof(Task)), instanceParam, argsParam); + return lambda.Compile(); + } + } + else + { + var lambda = Expression.Lambda> + (Expression.Convert(methodCall, typeof(object)), instanceParam, argsParam); + return lambda.Compile(); + } + + } diff --git a/NodeFlow/Tool/MethodDetailsHelper.cs b/NodeFlow/Tool/MethodDetailsHelper.cs index c600e5f..6337500 100644 --- a/NodeFlow/Tool/MethodDetailsHelper.cs +++ b/NodeFlow/Tool/MethodDetailsHelper.cs @@ -71,7 +71,7 @@ public static class MethodDetailsHelperTmp method.ReturnType);// 返回值 - Type returnType; + Type? returnType; bool isTask = IsGenericTask(method.ReturnType, out var taskResult); if (attribute.MethodDynamicType == Library.Enums.NodeType.Flipflop) @@ -85,7 +85,7 @@ public static class MethodDetailsHelperTmp } else if(isTask) { - returnType = taskResult; + returnType = taskResult is null ? typeof(Task) : taskResult; } else { @@ -119,12 +119,12 @@ public static class MethodDetailsHelperTmp } - public static bool IsGenericTask(Type returnType, out Type taskResult) + public static bool IsGenericTask(Type returnType, out Type? taskResult) { // 判断是否为 Task 类型或泛型 Task if (returnType == typeof(Task)) { - taskResult = returnType; + taskResult = null; return true; } else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index b1c393f..bc452f9 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -36,6 +36,7 @@ namespace Serein.WorkBench public App() { + #if false //测试 操作表达式,条件表达式 #region 测试数据 string expression = "";