From 4183866b42044f5a69bc5eef2e0c54a2d39a6308 Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Tue, 6 Aug 2024 16:09:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E5=88=86=E7=A6=BBNodeModel?= =?UTF-8?q?=E4=B8=8E=E4=BE=9D=E8=B5=96=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Flow/NodeModel/CompositeActionNode.cs | 6 +- .../Flow/NodeModel/CompositeConditionNode.cs | 8 +- Library/Flow/NodeModel/CompositeLoopNode.cs | 8 +- Library/Flow/NodeModel/NodeBase.cs | 9 +- Library/Flow/NodeModel/SingleActionNode.cs | 5 +- Library/Flow/NodeModel/SingleConditionNode.cs | 4 - Library/Flow/NodeModel/SingleExpOpNode.cs | 5 - Library/Flow/NodeModel/SingleFlipflopNode.cs | 38 +- Library/Flow/Tool/DelegateGenerator.cs | 10 +- Library/Flow/Tool/DynamicTool.cs | 11 +- Library/Flow/Tool/ExpressionHelper.cs | 6 +- Library/Flow/Tool/TcsSignal.cs | 7 +- Library/Flow/Tool/TypeDefinition.cs | 43 - Library/Serein.Library.csproj | 9 +- MyDll/MyDll.csproj | 1 + MyDll/SampleCondition.cs | 6 +- NodeFlow/Api.cs | 13 + NodeFlow/Attribute.cs | 73 ++ NodeFlow/DynamicContext.cs | 167 ++++ NodeFlow/MethodDetails.cs | 233 ++++++ NodeFlow/Model/CompositeActionNode.cs | 25 + NodeFlow/Model/CompositeConditionNode.cs | 65 ++ NodeFlow/Model/CompositeLoopNode.cs | 6 + NodeFlow/Model/NodeBase.cs | 511 ++++++++++++ NodeFlow/Model/SingleActionNode.cs | 68 ++ NodeFlow/Model/SingleConditionNode.cs | 71 ++ NodeFlow/Model/SingleExpOpNode.cs | 41 + NodeFlow/Model/SingleFlipflopNode.cs | 14 + NodeFlow/NodeFlowStarter.cs | 171 ++++ NodeFlow/Serein.NodeFlow.csproj | 14 + NodeFlow/SerinExpression/ConditionResolver.cs | 337 ++++++++ .../SerinExpression/SerinConditionParser.cs | 329 ++++++++ .../SerinExpressionEvaluator.cs | 214 +++++ NodeFlow/Tool/DelegateGenerator.cs | 193 +++++ NodeFlow/Tool/DynamicTool.cs | 202 +++++ NodeFlow/Tool/ExpressionHelper.cs | 741 ++++++++++++++++++ NodeFlow/Tool/TcsSignal.cs | 81 ++ SereinFlow.sln | 6 + SereinWAT/Serein.Module.WAT.csproj | 1 + SereinWAT/SereinWAT.cs | 4 +- WorkBench/App.xaml.cs | 8 +- WorkBench/MainWindow.xaml.cs | 20 +- WorkBench/Node/View/ActionNodeControl.xaml.cs | 2 +- .../Node/View/ActionRegionControl.xaml.cs | 3 +- .../Node/View/ConditionNodeControl.xaml.cs | 8 +- .../Node/View/ConditionRegionControl.xaml.cs | 4 +- WorkBench/Node/View/DllControlControl.xaml.cs | 3 +- WorkBench/Node/View/ExpOpNodeControl.xaml.cs | 16 +- .../Node/View/FlipflopNodeControl.xaml.cs | 5 +- WorkBench/Node/View/NodeControlBase.cs | 5 +- .../ViewModel/ActionNodeControlViewModel.cs | 5 +- .../ConditionNodeControlViewModel.cs | 6 +- .../Node/ViewModel/ExpOpNodeViewModel.cs | 7 +- .../ViewModel/FlipflopNodeControlViewModel.cs | 2 +- WorkBench/Serein.WorkBench.csproj | 1 + WorkBench/Themes/MethodDetailsControl.xaml.cs | 13 +- 56 files changed, 3623 insertions(+), 241 deletions(-) delete mode 100644 Library/Flow/Tool/TypeDefinition.cs create mode 100644 NodeFlow/Api.cs create mode 100644 NodeFlow/Attribute.cs create mode 100644 NodeFlow/DynamicContext.cs create mode 100644 NodeFlow/MethodDetails.cs create mode 100644 NodeFlow/Model/CompositeActionNode.cs create mode 100644 NodeFlow/Model/CompositeConditionNode.cs create mode 100644 NodeFlow/Model/CompositeLoopNode.cs create mode 100644 NodeFlow/Model/NodeBase.cs create mode 100644 NodeFlow/Model/SingleActionNode.cs create mode 100644 NodeFlow/Model/SingleConditionNode.cs create mode 100644 NodeFlow/Model/SingleExpOpNode.cs create mode 100644 NodeFlow/Model/SingleFlipflopNode.cs create mode 100644 NodeFlow/NodeFlowStarter.cs create mode 100644 NodeFlow/Serein.NodeFlow.csproj create mode 100644 NodeFlow/SerinExpression/ConditionResolver.cs create mode 100644 NodeFlow/SerinExpression/SerinConditionParser.cs create mode 100644 NodeFlow/SerinExpression/SerinExpressionEvaluator.cs create mode 100644 NodeFlow/Tool/DelegateGenerator.cs create mode 100644 NodeFlow/Tool/DynamicTool.cs create mode 100644 NodeFlow/Tool/ExpressionHelper.cs create mode 100644 NodeFlow/Tool/TcsSignal.cs diff --git a/Library/Flow/NodeModel/CompositeActionNode.cs b/Library/Flow/NodeModel/CompositeActionNode.cs index 4ca9e61..96aadf1 100644 --- a/Library/Flow/NodeModel/CompositeActionNode.cs +++ b/Library/Flow/NodeModel/CompositeActionNode.cs @@ -1,8 +1,4 @@ -using Serein.Flow; -using System.Collections.Generic; -using System.Diagnostics; - -namespace Serein.Flow.NodeModel +namespace Serein.Flow.NodeModel { /// diff --git a/Library/Flow/NodeModel/CompositeConditionNode.cs b/Library/Flow/NodeModel/CompositeConditionNode.cs index 0095abd..2a21e6f 100644 --- a/Library/Flow/NodeModel/CompositeConditionNode.cs +++ b/Library/Flow/NodeModel/CompositeConditionNode.cs @@ -1,8 +1,4 @@ -using Serein.Flow.Tool; -using System; -using System.Collections.Generic; -using System.Diagnostics; - + namespace Serein.Flow.NodeModel { @@ -59,7 +55,7 @@ namespace Serein.Flow.NodeModel } catch (Exception ex) { - Debug.Write(ex.Message); + Console.WriteLine(ex.Message); } return false; } diff --git a/Library/Flow/NodeModel/CompositeLoopNode.cs b/Library/Flow/NodeModel/CompositeLoopNode.cs index 649c71d..ff1d8f3 100644 --- a/Library/Flow/NodeModel/CompositeLoopNode.cs +++ b/Library/Flow/NodeModel/CompositeLoopNode.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.Flow.NodeModel +namespace Serein.Flow.NodeModel { public class CompositeLoopNode : NodeBase { diff --git a/Library/Flow/NodeModel/NodeBase.cs b/Library/Flow/NodeModel/NodeBase.cs index bc9e0c1..857ec9c 100644 --- a/Library/Flow/NodeModel/NodeBase.cs +++ b/Library/Flow/NodeModel/NodeBase.cs @@ -1,12 +1,5 @@ -using Serein.Flow; +using Newtonsoft.Json; using Serein.Flow.Tool; -using Newtonsoft.Json; -using SqlSugar; -using System; -using System.Threading.Tasks; -using System.Threading; -using System.Collections.Generic; -using System.Linq; namespace Serein.Flow.NodeModel { diff --git a/Library/Flow/NodeModel/SingleActionNode.cs b/Library/Flow/NodeModel/SingleActionNode.cs index 867ff33..ec83725 100644 --- a/Library/Flow/NodeModel/SingleActionNode.cs +++ b/Library/Flow/NodeModel/SingleActionNode.cs @@ -1,7 +1,4 @@ -using Serein.Flow.Tool; -using System.Diagnostics; - -namespace Serein.Flow.NodeModel +namespace Serein.Flow.NodeModel { /// /// 单动作节点(用于动作控件) diff --git a/Library/Flow/NodeModel/SingleConditionNode.cs b/Library/Flow/NodeModel/SingleConditionNode.cs index 50a72fc..391634f 100644 --- a/Library/Flow/NodeModel/SingleConditionNode.cs +++ b/Library/Flow/NodeModel/SingleConditionNode.cs @@ -1,8 +1,4 @@ using Serein.Flow.SerinExpression; -using Serein.Flow.Tool; -using System; -using System.Diagnostics; -using System.Linq.Expressions; namespace Serein.Flow.NodeModel { diff --git a/Library/Flow/NodeModel/SingleExpOpNode.cs b/Library/Flow/NodeModel/SingleExpOpNode.cs index 58b5866..f8811bc 100644 --- a/Library/Flow/NodeModel/SingleExpOpNode.cs +++ b/Library/Flow/NodeModel/SingleExpOpNode.cs @@ -1,9 +1,4 @@ using Serein.Flow.SerinExpression; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Serein.Flow.NodeModel { diff --git a/Library/Flow/NodeModel/SingleFlipflopNode.cs b/Library/Flow/NodeModel/SingleFlipflopNode.cs index 8292034..ace9fc2 100644 --- a/Library/Flow/NodeModel/SingleFlipflopNode.cs +++ b/Library/Flow/NodeModel/SingleFlipflopNode.cs @@ -1,40 +1,12 @@ -using Serein.Flow.Tool; - -namespace Serein.Flow.NodeModel +namespace Serein.Flow.NodeModel { public class SingleFlipflopNode : NodeBase { - //public override void Execute(DynamicContext context) - //{ - // throw new NotImplementedException("无法以非await/async的形式调用触发器"); - //} + public override object Execute(DynamicContext context) + { + throw new NotImplementedException("无法以非await/async的形式调用触发器"); + } - //public virtual async Task ExecuteAsync(DynamicContext context, Action NextTask = null) - //{ - // if (DelegateCache.GlobalDicDelegates.TryGetValue(MethodDetails.MethodName, out Delegate? del)) - // { - // object?[]? parameters = GetParameters(context, MethodDetails); - - // // 根据 ExplicitDatas.Length 判断委托类型 - // var func = (Func>)del; - - // // 调用委托并获取结果 - // FlipflopContext flipflopContext = await func.Invoke(MethodDetails.ActingInstance, parameters); - - // if (flipflopContext != null) - // { - // if (flipflopContext.State == FfState.Cancel) - // { - // throw new Exception("取消此异步"); - // } - // else - // { - // CurrentState = flipflopContext.State == FfState.Succeed; - // context.SetFlowData(flipflopContext.Data); - // } - // } - // } - //} } } diff --git a/Library/Flow/Tool/DelegateGenerator.cs b/Library/Flow/Tool/DelegateGenerator.cs index ce76fe1..47360ec 100644 --- a/Library/Flow/Tool/DelegateGenerator.cs +++ b/Library/Flow/Tool/DelegateGenerator.cs @@ -1,14 +1,6 @@ -using Serein; -using Serein.Flow; -using Serein.Flow.NodeModel; -using Serein.Library.IOC; -using System; +using Serein.Library.IOC; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Reflection; -using System.Threading.Tasks; namespace Serein.Flow.Tool; diff --git a/Library/Flow/Tool/DynamicTool.cs b/Library/Flow/Tool/DynamicTool.cs index 6b07fd2..a5d5c20 100644 --- a/Library/Flow/Tool/DynamicTool.cs +++ b/Library/Flow/Tool/DynamicTool.cs @@ -1,13 +1,4 @@ -using Serein; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Drawing.Printing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.Flow.Tool +namespace Serein.Flow.Tool { #region 锁、tsk工具 (已注释) diff --git a/Library/Flow/Tool/ExpressionHelper.cs b/Library/Flow/Tool/ExpressionHelper.cs index 2f794e1..16573b3 100644 --- a/Library/Flow/Tool/ExpressionHelper.cs +++ b/Library/Flow/Tool/ExpressionHelper.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Concurrent; using System.Linq.Expressions; using System.Reflection; -using System.Threading.Tasks; namespace Serein.Flow.Tool { diff --git a/Library/Flow/Tool/TcsSignal.cs b/Library/Flow/Tool/TcsSignal.cs index a60faf6..cd6e3f4 100644 --- a/Library/Flow/Tool/TcsSignal.cs +++ b/Library/Flow/Tool/TcsSignal.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Concurrent; namespace Serein.Flow.Tool { diff --git a/Library/Flow/Tool/TypeDefinition.cs b/Library/Flow/Tool/TypeDefinition.cs deleted file mode 100644 index 038c3ab..0000000 --- a/Library/Flow/Tool/TypeDefinition.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Serein.Flow; -using System; -using System.Collections.Concurrent; -using System.Reflection.Metadata.Ecma335; -using System.Runtime.CompilerServices; - -namespace Serein.Flow.Tool -{ - - - /* /// - /// 标记一个方法是什么类型,加载dll后用来拖拽到画布中 - /// - [AttributeUsage( AttributeTargets.Parameter)] - public class ObjDetailAttribute : Attribute - { - public bool Scan { get; set; } - public object @object { get; } - public DynamicNodeType MethodDynamicType { get; } - - public ObjDetailAttribute(DynamicNodeType methodDynamicType, object tmpObject = null, bool scan = true) - { - @object = tmpObject; - MethodDynamicType = methodDynamicType; - Scan = scan; - } - } - */ - - - - /* /// - /// 状态接口 - /// - public interface IState: IDynamic - { - /// - /// 返回状态 - /// - /// - string GetState(DynamicContext context); - }*/ -} diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index ec7db40..d766447 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -1,7 +1,7 @@  - net8.0 + net8.0-windows7.0 enable enable D:\Project\C#\DynamicControl\SereinFlow\.Output @@ -9,8 +9,11 @@ + + + @@ -22,8 +25,4 @@ - - - - diff --git a/MyDll/MyDll.csproj b/MyDll/MyDll.csproj index d6492f4..72543ce 100644 --- a/MyDll/MyDll.csproj +++ b/MyDll/MyDll.csproj @@ -16,6 +16,7 @@ + diff --git a/MyDll/SampleCondition.cs b/MyDll/SampleCondition.cs index d59ded4..70ca914 100644 --- a/MyDll/SampleCondition.cs +++ b/MyDll/SampleCondition.cs @@ -1,6 +1,6 @@ -using Serein.Flow; -using Serein.Flow.Tool; -using Serein.Library.Http; +using Serein.Library.Http; +using Serein.NodeFlow; +using Serein.NodeFlow.Tool; using static MyDll.PlcDevice; namespace MyDll { diff --git a/NodeFlow/Api.cs b/NodeFlow/Api.cs new file mode 100644 index 0000000..c5e1f56 --- /dev/null +++ b/NodeFlow/Api.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.NodeFlow +{ + public interface IDynamicFlowNode + { + } + +} diff --git a/NodeFlow/Attribute.cs b/NodeFlow/Attribute.cs new file mode 100644 index 0000000..8e564fb --- /dev/null +++ b/NodeFlow/Attribute.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.NodeFlow +{ + + public enum DynamicNodeType + { + /// + /// 初始化 + /// + Init, + /// + /// 开始载入 + /// + Loading, + /// + /// 结束 + /// + Exit, + + /// + /// 触发器 + /// + Flipflop, + /// + /// 条件节点 + /// + Condition, + /// + /// 动作节点 + /// + Action, + } + + + + /// + /// 用来判断一个类是否需要注册并构建实例(单例模式场景使用) + /// + [AttributeUsage(AttributeTargets.Class)] + public class DynamicFlowAttribute(bool scan = true) : Attribute + { + public bool Scan { get; set; } = scan; + } + + /// + /// 标记一个方法是什么类型,加载dll后用来拖拽到画布中 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class MethodDetailAttribute(DynamicNodeType methodDynamicType, + string methodTips = "", + bool scan = true, + string lockName = "") : Attribute + { + public bool Scan { get; set; } = scan; + public string MethodTips { get; } = methodTips; + public DynamicNodeType MethodDynamicType { get; } = methodDynamicType; + public string LockName { get; } = lockName; + } + + /// + /// 是否为显式参数 + /// + [AttributeUsage(AttributeTargets.Parameter)] + public class ExplicitAttribute : Attribute // where TEnum : Enum + { + } + +} diff --git a/NodeFlow/DynamicContext.cs b/NodeFlow/DynamicContext.cs new file mode 100644 index 0000000..2d47e3a --- /dev/null +++ b/NodeFlow/DynamicContext.cs @@ -0,0 +1,167 @@ +using Serein.Library.IOC; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using static System.Collections.Specialized.BitVector32; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace Serein.NodeFlow +{ + + public enum FfState + { + Succeed, + Cancel, + } + /// + /// 触发器上下文 + /// + public class FlipflopContext + { + public FfState State { get; set; } + public object? Data { get; set; } + /*public FlipflopContext() + { + State = FfState.Cancel; + }*/ + public FlipflopContext(FfState ffState, object? data = null) + { + State = ffState; + Data = data; + } + } + + + /// + /// 动态流程上下文 + /// + + public class DynamicContext(IServiceContainer serviceContainer) + + { + + private readonly string contextGuid = "";//System.Guid.NewGuid().ToString(); + + public IServiceContainer ServiceContainer { get; } = serviceContainer; + private List InitServices { get; set; } = []; + + // private ConcurrentDictionary ContextData { get; set; } = []; + + //public void SetFlowData(object data) + //{ + // var threadId = Thread.CurrentThread.ManagedThreadId.ToString(); + // var name = $"{threadId}.{contextGuid}FlowData"; + // SetData(name,data); + //} + //public object GetFlowData(bool IsRetain = false) + //{ + // var threadId = Thread.CurrentThread.ManagedThreadId.ToString(); + // var name = $"{threadId}.{contextGuid}FlowData"; + // if (IsRetain) + // { + // return GetData(name); + // } + // else + // { + // return GetAndRemoteData(name); + + // } + //} + + + public void InitService() + { + InitService(typeof(T)); + } + public void InitService(Type type) + { + if (!InitServices.Contains(type)) + { + InitServices.Add(type); + } + else + { + //throw new Exception("初始化时试图添加已存在的类型:"+type.Name); + Console.WriteLine("初始化时试图添加已存在的类型:" + type.Name); + } + } + public void Biuld() + { + foreach (var item in InitServices) + { + ServiceContainer.Register(item); + } + ServiceContainer.Build(); + } + + //public object? RemoveData(string key) + //{ + // if (ContextData.Remove(key, out var data)) + // { + // return data; + // } + // return null; + //} + + //public void SetData(string key, T value) + //{ + // ContextData[key] = value; + //} + + //public T? GetData(string key) + //{ + // if (ContextData.TryGetValue(key, out object? value)) + // { + // if(value == null) + // { + // return default; + // } + // if (value.GetType() == typeof(T)) + // { + // return (T)value; + // } + + // } + // return default; + //} + + //public object? GetData(string key) + //{ + // if (ContextData.TryGetValue(key, out object? value)) + // { + // return value; + // } + // return null; + //} + + + //public ConcurrentDictionary FlipFlopTasks { get; set; } = []; + + public NodeRunTcs NodeRunCts { get; set; } + public Task CreateTimingTask(Action action, int time = 100, int count = -1) + { + NodeRunCts ??= ServiceContainer.Get(); + return Task.Factory.StartNew(async () => + { + for (int i = 0; i < count; i++) + { + NodeRunCts.Token.ThrowIfCancellationRequested(); + await time; + action.Invoke(); + } + }); + } + } + + public static class MyExtensions + { + public static TaskAwaiter GetAwaiter(this int i) => Task.Delay(i).GetAwaiter(); + } + + + // if (time <= 0) throw new ArgumentException("时间不能≤0"); +} diff --git a/NodeFlow/MethodDetails.cs b/NodeFlow/MethodDetails.cs new file mode 100644 index 0000000..c73d38b --- /dev/null +++ b/NodeFlow/MethodDetails.cs @@ -0,0 +1,233 @@ +namespace Serein.NodeFlow +{ + /// + /// 显式参数 + /// + public class ExplicitData + { + /// + /// 索引 + /// + public int Index { get; set; } + /// + /// 是否为显式参数 + /// + public bool IsExplicitData { get; set; } + /// + /// 显式类型 + /// + public Type? ExplicitType { get; set; } + + /// + /// 显示类型编号> + /// + public string ExplicitTypeName { get; set; } + + /// + /// 方法需要的类型 + /// + public Type DataType { get; set; } + + /// + /// 方法入参参数名称 + /// + public string ParameterName { get; set; } + + /// + /// 入参值 + /// + + public string DataValue { get; set; } + + + + public string[] Items { get; set; } + + + + + public ExplicitData Clone() => new() + { + Index = Index, + IsExplicitData = IsExplicitData, + ExplicitType = ExplicitType, + DataType = DataType, + ParameterName = ParameterName, + ExplicitTypeName = ExplicitTypeName, + DataValue = string.IsNullOrEmpty(DataValue) ? string.Empty : DataValue, + Items = [.. Items], + }; + } + + + + public class MethodDetails + { + public MethodDetails Clone() + { + return new MethodDetails + { + ActingInstance = ActingInstance, + ActingInstanceType = ActingInstanceType, + MethodDelegate = MethodDelegate, + MethodDynamicType = MethodDynamicType, + MethodGuid = Guid.NewGuid().ToString(), + MethodTips = MethodTips, + ReturnType = ReturnType, + MethodName = MethodName, + MethodLockName = MethodLockName, + + ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(), + }; + } + + /// + /// 作用实例 + /// + + public Type ActingInstanceType { get; set; } + + /// + /// 作用实例 + /// + + public object ActingInstance { get; set; } + + /// + /// 方法GUID + /// + + public string MethodGuid { get; set; } + + /// + /// 方法名称 + /// + + public string MethodName { get; set; } + + /// + /// 方法委托 + /// + + public Delegate MethodDelegate { get; set; } + + /// + /// 节点类型 + /// + public DynamicNodeType MethodDynamicType { get; set; } + /// + /// 锁名称 + /// + + public string MethodLockName { get; set; } + + + /// + /// 方法说明 + /// + + public string MethodTips { get; set; } + + + /// + /// 参数内容 + /// + + public ExplicitData[] ExplicitDatas { get; set; } + + + + /// + /// 出参类型 + /// + + public Type ReturnType { get; set; } + + + + + + + public bool IsCanConnect(Type returnType) + { + if (ExplicitDatas.Length == 0) + { + // 目标不需要传参,可以舍弃结果? + return true; + } + var types = ExplicitDatas.Select(it => it.DataType).ToArray(); + // 检查返回类型是否是元组类型 + if (returnType.IsGenericType && IsValueTuple(returnType)) + { + + return CompareGenericArguments(returnType, types); + } + else + { + int index = 0; + if (types[index] == typeof(DynamicContext)) + { + index++; + if (types.Length == 1) + { + return true; + } + } + // 被连接节点检查自己需要的参数类型,与发起连接的节点比较返回值类型 + if (returnType == types[index]) + { + return true; + } + } + return false; + } + + /// + /// 检查元组类型 + /// + /// + /// + private bool IsValueTuple(Type type) + { + if (!type.IsGenericType) return false; + + var genericTypeDef = type.GetGenericTypeDefinition(); + return genericTypeDef == typeof(ValueTuple<>) || + genericTypeDef == typeof(ValueTuple<,>) || + genericTypeDef == typeof(ValueTuple<,,>) || + genericTypeDef == typeof(ValueTuple<,,,>) || + genericTypeDef == typeof(ValueTuple<,,,,>) || + genericTypeDef == typeof(ValueTuple<,,,,,>) || + genericTypeDef == typeof(ValueTuple<,,,,,,>) || + genericTypeDef == typeof(ValueTuple<,,,,,,,>); + } + + private bool CompareGenericArguments(Type returnType, Type[] parameterTypes) + { + var genericArguments = returnType.GetGenericArguments(); + var length = parameterTypes.Length; + + for (int i = 0; i < genericArguments.Length; i++) + { + if (i >= length) return false; + + if (IsValueTuple(genericArguments[i])) + { + // 如果当前参数也是 ValueTuple,递归检查嵌套的泛型参数 + if (!CompareGenericArguments(genericArguments[i], parameterTypes.Skip(i).ToArray())) + { + return false; + } + } + else if (genericArguments[i] != parameterTypes[i]) + { + return false; + } + } + + return true; + } + } + + +} diff --git a/NodeFlow/Model/CompositeActionNode.cs b/NodeFlow/Model/CompositeActionNode.cs new file mode 100644 index 0000000..cb06f4c --- /dev/null +++ b/NodeFlow/Model/CompositeActionNode.cs @@ -0,0 +1,25 @@ +namespace Serein.NodeFlow.Model +{ + + /// + /// 组合动作节点(用于动作区域) + /// + public class CompositeActionNode : NodeBase + { + public List ActionNodes; + /// + /// 组合动作节点(用于动作区域) + /// + public CompositeActionNode(List actionNodes) + { + ActionNodes = actionNodes; + } + public void AddNode(SingleActionNode node) + { + ActionNodes.Add(node); + MethodDetails ??= node.MethodDetails; + } + + } + +} diff --git a/NodeFlow/Model/CompositeConditionNode.cs b/NodeFlow/Model/CompositeConditionNode.cs new file mode 100644 index 0000000..1160167 --- /dev/null +++ b/NodeFlow/Model/CompositeConditionNode.cs @@ -0,0 +1,65 @@ +namespace Serein.NodeFlow.Model +{ + /// + /// 组合条件节点(用于条件区域) + /// + public class CompositeConditionNode : NodeBase + { + public List ConditionNodes { get; } = []; + + + public void AddNode(SingleConditionNode node) + { + ConditionNodes.Add(node); + MethodDetails ??= node.MethodDetails; + } + + public override object? Execute(DynamicContext context) + { + // bool allTrue = ConditionNodes.All(condition => Judge(context,condition.MethodDetails)); + // bool IsAllTrue = true; // 初始化为 true + FlowState = true; + foreach (SingleConditionNode? node in ConditionNodes) + { + if (!Judge(context, node)) + { + FlowState = false; + break;// 一旦发现条件为假,立即退出循环 + } + } + + return PreviousNode?.FlowData; + //if (IsAllTrue) + //{ + // foreach (var nextNode in TrueBranchNextNodes) + // { + // nextNode.ExecuteStack(context); + // } + //} + //else + //{ + // foreach (var nextNode in FalseBranchNextNodes) + // { + // nextNode.ExecuteStack(context); + // } + //} + } + private bool Judge(DynamicContext context, SingleConditionNode node) + { + try + { + node.Execute(context); + return node.FlowState; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + return false; + } + + + + } + +} diff --git a/NodeFlow/Model/CompositeLoopNode.cs b/NodeFlow/Model/CompositeLoopNode.cs new file mode 100644 index 0000000..faa9bfe --- /dev/null +++ b/NodeFlow/Model/CompositeLoopNode.cs @@ -0,0 +1,6 @@ +namespace Serein.NodeFlow.Model +{ + public class CompositeLoopNode : NodeBase + { + } +} diff --git a/NodeFlow/Model/NodeBase.cs b/NodeFlow/Model/NodeBase.cs new file mode 100644 index 0000000..3f14e93 --- /dev/null +++ b/NodeFlow/Model/NodeBase.cs @@ -0,0 +1,511 @@ +using Newtonsoft.Json; +using Serein.NodeFlow; +using Serein.NodeFlow.Tool; + +namespace Serein.NodeFlow.Model +{ + + public enum ConnectionType + { + /// + /// 真分支 + /// + IsSucceed, + /// + /// 假分支 + /// + IsFail, + /// + /// 异常发生分支 + /// + IsError, + /// + /// 上游分支(执行当前节点前会执行一次上游分支) + /// + Upstream, + } + + /// + /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域 + /// + public abstract class NodeBase : IDynamicFlowNode + { + + public MethodDetails MethodDetails { get; set; } + + + public string Guid { get; set; } + + + public string DisplayName { get; set; } + + public bool IsStart { get; set; } + + public string DelegateName { get; set; } + + + /// + /// 运行时的上一节点 + /// + public NodeBase? PreviousNode { get; set; } + + /// + /// 上一节点集合 + /// + public List PreviousNodes { get; set; } = []; + /// + /// 下一节点集合(真分支) + /// + public List SucceedBranch { get; set; } = []; + /// + /// 下一节点集合(假分支) + /// + public List FailBranch { get; set; } = []; + /// + /// 异常分支 + /// + public List ErrorBranch { get; set; } = []; + /// + /// 上游分支 + /// + public List UpstreamBranch { get; set; } = []; + + /// + /// 当前状态(进入真分支还是假分支,异常分支在异常中确定) + /// + public bool FlowState { get; set; } = true; + //public ConnectionType NextType { get; set; } = ConnectionType.IsTrue; + /// + /// 当前传递数据 + /// + public object? FlowData { get; set; } = null; + + + // 正常流程节点调用 + public virtual object? Execute(DynamicContext context) + { + MethodDetails md = MethodDetails; + object? result = null; + + if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del)) + { + if (md.ExplicitDatas.Length == 0) + { + if (md.ReturnType == typeof(void)) + { + ((Action)del).Invoke(md.ActingInstance); + } + else + { + result = ((Func)del).Invoke(md.ActingInstance); + } + } + else + { + object?[]? parameters = GetParameters(context, MethodDetails); + if (md.ReturnType == typeof(void)) + { + ((Action)del).Invoke(md.ActingInstance, parameters); + } + else + { + result = ((Func)del).Invoke(md.ActingInstance, parameters); + + + } + } + // context.SetFlowData(result); + // CurrentData = result; + } + + return result; + } + + // 触发器调用 + public virtual async Task ExecuteAsync(DynamicContext context) + { + MethodDetails md = MethodDetails; + object? result = null; + + if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del)) + { + if (md.ExplicitDatas.Length == 0) + { + // 调用委托并获取结果 + FlipflopContext flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance); + + if (flipflopContext != null) + { + if (flipflopContext.State == FfState.Cancel) + { + throw new Exception("this async task is cancel."); + } + else + { + if (flipflopContext.State == FfState.Succeed) + { + FlowState = true; + result = flipflopContext.Data; + } + else + { + FlowState = false; + } + } + } + } + else + { + object?[]? parameters = GetParameters(context, MethodDetails); + // 调用委托并获取结果 + + + FlipflopContext flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance, parameters); + + + + if (flipflopContext != null) + { + if (flipflopContext.State == FfState.Cancel) + { + throw new Exception("取消此异步"); + } + else + { + FlowState = flipflopContext.State == FfState.Succeed; + result = flipflopContext.Data; + } + } + } + } + + return result; + } + + public async Task ExecuteStack(DynamicContext context) + { + await Task.Run(async () => + { + await ExecuteStackTmp(context); + }); + } + + public async Task ExecuteStackTmp(DynamicContext context) + { + var cts = context.ServiceContainer.Get(); + + Stack stack = []; + stack.Push(this); + + while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环 + { + // 从栈中弹出一个节点作为当前节点进行处理 + var currentNode = stack.Pop(); + + if (currentNode.MethodDetails != null) + { + currentNode.MethodDetails.ActingInstance ??= context.ServiceContainer.Get(MethodDetails.ActingInstanceType); + } // 设置方法执行的对象 + + // 获取上游分支,首先执行一次 + var upstreamNodes = currentNode.UpstreamBranch; + for (int i = upstreamNodes.Count - 1; i >= 0; i--) + { + upstreamNodes[i].PreviousNode = currentNode; + await upstreamNodes[i].ExecuteStack(context); + } + + + if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == DynamicNodeType.Flipflop) + { + currentNode.FlowData = await currentNode.ExecuteAsync(context); + } + else + { + currentNode.FlowData = currentNode.Execute(context); + } + + + var nextNodes = currentNode.FlowState ? currentNode.SucceedBranch + : currentNode.FailBranch; + + // 将下一个节点集合中的所有节点逆序推入栈中 + for (int i = nextNodes.Count - 1; i >= 0; i--) + { + nextNodes[i].PreviousNode = currentNode; + stack.Push(nextNodes[i]); + } + } + } + + + public object[]? GetParameters(DynamicContext context, MethodDetails md) + { + // 用正确的大小初始化参数数组 + var types = md.ExplicitDatas.Select(it => it.DataType).ToArray(); + if (types.Length == 0) + { + return [md.ActingInstance]; + } + + object[]? parameters = new object[types.Length]; + + for (int i = 0; i < types.Length; i++) + { + + var mdEd = md.ExplicitDatas[i]; + Type type = mdEd.DataType; + + var f1 = PreviousNode?.FlowData?.GetType(); + var f2 = mdEd.DataType; + if (type == typeof(DynamicContext)) + { + parameters[i] = context; + } + else if (type == typeof(MethodDetails)) + { + parameters[i] = md; + } + else if (type == typeof(NodeBase)) + { + parameters[i] = this; + } + else if (mdEd.IsExplicitData) // 显式参数 + { + if (mdEd.DataType.IsEnum) + { + var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue); + parameters[i] = enumValue; + } + else if (mdEd.ExplicitType == typeof(string)) + { + parameters[i] = mdEd.DataValue; + } + else if (mdEd.ExplicitType == typeof(bool)) + { + parameters[i] = bool.Parse(mdEd.DataValue); + } + else if (mdEd.ExplicitType == typeof(int)) + { + parameters[i] = int.Parse(mdEd.DataValue); + } + else if (mdEd.ExplicitType == typeof(double)) + { + parameters[i] = double.Parse(mdEd.DataValue); + } + else + { + parameters[i] = ""; + + //parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType); + } + } + else if (f1 != null && f2 != null && f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName)) + { + parameters[i] = PreviousNode?.FlowData; + } + else + { + + + var tmpParameter = PreviousNode?.FlowData?.ToString(); + if (mdEd.DataType.IsEnum) + { + + var enumValue = Enum.Parse(mdEd.DataType, tmpParameter); + + parameters[i] = enumValue; + } + else if (mdEd.DataType == typeof(string)) + { + + parameters[i] = tmpParameter; + + } + else if (mdEd.DataType == typeof(bool)) + { + + parameters[i] = bool.Parse(tmpParameter); + + } + else if (mdEd.DataType == typeof(int)) + { + + parameters[i] = int.Parse(tmpParameter); + + } + else if (mdEd.DataType == typeof(double)) + { + + parameters[i] = double.Parse(tmpParameter); + + } + else + { + if (tmpParameter != null && mdEd.DataType != null) + { + + parameters[i] = ConvertValue(tmpParameter, mdEd.DataType); + + } + } + } + + } + return parameters; + } + + + private dynamic? ConvertValue(string value, Type targetType) + { + try + { + if (!string.IsNullOrEmpty(value)) + { + return JsonConvert.DeserializeObject(value, targetType); + } + else + { + return null; + } + } + catch (JsonReaderException ex) + { + Console.WriteLine(ex); + return value; + } + catch (JsonSerializationException ex) + { + // 如果无法转为对应的JSON对象 + int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引 + int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引 + var typeInfo = ex.Message[startIndex..endIndex]; // 提取出错类型信息,该怎么传出去? + Console.WriteLine("无法转为对应的JSON对象:" + typeInfo); + return null; + } + catch // (Exception ex) + { + return value; + } + } + + + + + + #region 完整的ExecuteAsync调用方法(不要删除) + //public virtual async Task ExecuteAsync(DynamicContext context) + //{ + // MethodDetails md = MethodDetails; + // object? result = null; + // if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del)) + // { + // if (md.ExplicitDatas.Length == 0) + // { + // if (md.ReturnType == typeof(void)) + // { + // ((Action)del).Invoke(md.ActingInstance); + // } + // else if (md.ReturnType == typeof(Task)) + // { + // // 调用委托并获取结果 + // FlipflopContext flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance); + + // if (flipflopContext != null) + // { + // if (flipflopContext.State == FfState.Cancel) + // { + // throw new Exception("this async task is cancel."); + // } + // else + // { + // if (flipflopContext.State == FfState.Succeed) + // { + // CurrentState = true; + // result = flipflopContext.Data; + // } + // else + // { + // CurrentState = false; + // } + // } + // } + // } + // else + // { + // result = ((Func)del).Invoke(md.ActingInstance); + // } + // } + // else + // { + // object?[]? parameters = GetParameters(context, MethodDetails); + // if (md.ReturnType == typeof(void)) + // { + // ((Action)del).Invoke(md.ActingInstance, parameters); + // } + // else if (md.ReturnType == typeof(Task)) + // { + // // 调用委托并获取结果 + // FlipflopContext flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance, parameters); + + // if (flipflopContext != null) + // { + // if (flipflopContext.State == FfState.Cancel) + // { + // throw new Exception("取消此异步"); + // } + // else + // { + // CurrentState = flipflopContext.State == FfState.Succeed; + // result = flipflopContext.Data; + // } + // } + // } + // else + // { + // result = ((Func)del).Invoke(md.ActingInstance, parameters); + // } + // } + // context.SetFlowData(result); + // } + // return result; + //} + #endregion + + + + + + + } + + +} + + +/* while (stack.Count > 0) // 循环中直到栈为空才会退出 + { + // 从栈中弹出一个节点作为当前节点进行处理 + var currentNode = stack.Pop(); + + if(currentNode is CompositeActionNode || currentNode is CompositeConditionNode) + { + currentNode.currentState = true; + } + else if (currentNode is CompositeConditionNode) + { + + } + currentNode.Execute(context); + // 根据当前节点的执行结果选择下一节点集合 + // 如果 currentState 为真,选择 TrueBranchNextNodes;否则选择 FalseBranchNextNodes + var nextNodes = currentNode.currentState ? currentNode.TrueBranchNextNodes + : currentNode.FalseBranchNextNodes; + + // 将下一个节点集合中的所有节点逆序推入栈中 + for (int i = nextNodes.Count - 1; i >= 0; i--) + { + stack.Push(nextNodes[i]); + } + + }*/ \ No newline at end of file diff --git a/NodeFlow/Model/SingleActionNode.cs b/NodeFlow/Model/SingleActionNode.cs new file mode 100644 index 0000000..daa8af4 --- /dev/null +++ b/NodeFlow/Model/SingleActionNode.cs @@ -0,0 +1,68 @@ +namespace Serein.NodeFlow.Model +{ + /// + /// 单动作节点(用于动作控件) + /// + public class SingleActionNode : NodeBase + { + //public override void Execute(DynamicContext context) + //{ + // try + // { + // Execute(context, base.MethodDetails); + // CurrentState = true; + // } + // catch (Exception ex) + // { + // Debug.Write(ex.Message); + // CurrentState = false; + // } + //} + + //public void Execute(DynamicContext context, MethodDetails md) + //{ + // if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del)) + // { + + // object? result = null; + + // if (md.ExplicitDatas.Length == 0) + // { + // if (md.ReturnType == typeof(void)) + // { + // ((Action)del).Invoke(md.ActingInstance); + // } + // else + // { + // result = ((Func)del).Invoke(md.ActingInstance); + // } + // } + // else + // { + // object?[]? parameters = GetParameters(context, MethodDetails); + // if (md.ReturnType == typeof(void)) + // { + // ((Action)del).Invoke(md.ActingInstance, parameters); + // } + // else + // { + // result = ((Func)del).Invoke(md.ActingInstance, parameters); + // } + // } + + // // 根据 ExplicitDatas.Length 判断委托类型 + // //var action = (Action)del; + + // // 调用委托并获取结果 + // // action.Invoke(MethodDetails.ActingInstance, parameters); + + // //parameters = [md.ActingInstance, "", 123, ""]; + + // context.SetFlowData(result); + // } + //} + + } + + +} diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs new file mode 100644 index 0000000..942b25c --- /dev/null +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -0,0 +1,71 @@ +using Serein.NodeFlow.SerinExpression; + +namespace Serein.NodeFlow.Model +{ + /// + /// 条件节点(用于条件控件) + /// + public class SingleConditionNode : NodeBase + { + + /// + /// 是否为自定义参数 + /// + public bool IsCustomData { get; set; } + /// + /// 自定义参数值 + /// + public object? CustomData { get; set; } + /// + /// 条件表达式 + /// + + public string Expression { get; set; } + + + public override object? Execute(DynamicContext context) + { + // 接收上一节点参数or自定义参数内容 + object? result; + if (IsCustomData) + { + result = CustomData; + } + else + { + result = PreviousNode?.FlowData; + } + FlowState = SerinConditionParser.To(result, Expression); + Console.WriteLine($"{result} {Expression} -> " + FlowState); + return result; + } + + //public override void Execute(DynamicContext context) + //{ + // CurrentState = Judge(context, base.MethodDetails); + //} + + //private bool Judge(DynamicContext context, MethodDetails md) + //{ + // try + // { + // if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del)) + // { + // object[] parameters = GetParameters(context, md); + // var temp = del.DynamicInvoke(parameters); + // //context.GetData(GetDyPreviousKey()); + // return (bool)temp; + // } + // } + // catch (Exception ex) + // { + // Debug.Write(ex.Message); + // } + // return false; + //} + + + } + + +} diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/SingleExpOpNode.cs new file mode 100644 index 0000000..6a3e3a3 --- /dev/null +++ b/NodeFlow/Model/SingleExpOpNode.cs @@ -0,0 +1,41 @@ +using Serein.NodeFlow; +using Serein.NodeFlow.SerinExpression; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.NodeFlow.Model +{ + /// + /// Expression Operation - 表达式操作 + /// + public class SingleExpOpNode : NodeBase + { + /// + /// 表达式 + /// + public string Expression { get; set; } + + + public override object? Execute(DynamicContext context) + { + var data = PreviousNode?.FlowData; + + var newData = SerinExpressionEvaluator.Evaluate(Expression, data, out bool isChange); + + FlowState = true; + Console.WriteLine(newData); + if (isChange) + { + return newData; + } + else + { + return PreviousNode?.FlowData; + } + + } + } +} diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs new file mode 100644 index 0000000..892cc12 --- /dev/null +++ b/NodeFlow/Model/SingleFlipflopNode.cs @@ -0,0 +1,14 @@ +using Serein.NodeFlow; + +namespace Serein.NodeFlow.Model +{ + + public class SingleFlipflopNode : NodeBase + { + public override object Execute(DynamicContext context) + { + throw new NotImplementedException("无法以非await/async的形式调用触发器"); + } + + } +} diff --git a/NodeFlow/NodeFlowStarter.cs b/NodeFlow/NodeFlowStarter.cs new file mode 100644 index 0000000..f75db86 --- /dev/null +++ b/NodeFlow/NodeFlowStarter.cs @@ -0,0 +1,171 @@ +using Serein.Library.Http; +using Serein.Library.IOC; +using Serein.NodeFlow; +using Serein.NodeFlow.Model; +using Serein.NodeFlow.Tool; +using SqlSugar; + +namespace Serein.NodeFlow +{ + + public class NodeRunTcs : CancellationTokenSource + { + + } + + + public class NodeFlowStarter(IServiceContainer serviceContainer, List methodDetails) + + { + private readonly IServiceContainer ServiceContainer = serviceContainer; + private readonly List methodDetails = methodDetails; + + private Action ExitAction = null; + + + private DynamicContext context = null; + + + public NodeRunTcs MainCts; + + /// + /// 运行测试 + /// + /// + /// + //public async Task RunAsync1(List nodes) + //{ + // await Task.Run(async ()=> await StartRunAsync(nodes)); + //} + + public async Task RunAsync(List nodes) + { + var startNode = nodes.FirstOrDefault(p => p.IsStart); + if (startNode == null) { return; } + context = new(ServiceContainer); + + MainCts = ServiceContainer.CreateServiceInstance(); + + var initMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Init).ToList(); + var loadingMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Loading).ToList(); + var exitMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Exit).ToList(); + ExitAction = () => + { + ServiceContainer.Run((web) => + { + web?.Stop(); + }); + foreach (MethodDetails? md in exitMethods) + { + object?[]? args = [context]; + object?[]? data = [md.ActingInstance, args]; + md.MethodDelegate.DynamicInvoke(data); + } + if (context != null && context.NodeRunCts != null && !context.NodeRunCts.IsCancellationRequested) + { + context.NodeRunCts.Cancel(); + } + if (MainCts != null && !MainCts.IsCancellationRequested) MainCts.Cancel(); + ServiceContainer.Reset(); + }; + + + foreach (var md in initMethods) // 初始化 - 调用方法 + { + //md.ActingInstance = context.ServiceContainer.Get(md.ActingInstanceType); + object?[]? args = [context]; + object?[]? data = [md.ActingInstance, args]; + md.MethodDelegate.DynamicInvoke(data); + } + context.Biuld(); + + foreach (var md in loadingMethods) // 加载 + { + //md.ActingInstance = context.ServiceContainer.Get(md.ActingInstanceType); + object?[]? args = [context]; + object?[]? data = [md.ActingInstance, args]; + md.MethodDelegate.DynamicInvoke(data); + } + + var flipflopNodes = nodes.Where(it => it.MethodDetails?.MethodDynamicType == DynamicNodeType.Flipflop + && it.PreviousNodes.Count == 0 + && it.IsStart != true).ToArray(); + + var singleFlipflopNodes = flipflopNodes.Select(it => (SingleFlipflopNode)it).ToArray(); + + // 使用 TaskCompletionSource 创建未启动的任务 + var tasks = singleFlipflopNodes.Select(async node => + { + await FlipflopExecute(node); + }).ToArray(); + + + try + { + await Task.WhenAll([startNode.ExecuteStack(context), .. tasks]); + } + catch (Exception ex) + { + await Console.Out.WriteLineAsync(ex.ToString()); + } + + } + + private async Task FlipflopExecute(SingleFlipflopNode singleFlipFlopNode) + { + DynamicContext context = new DynamicContext(ServiceContainer); + MethodDetails md = singleFlipFlopNode.MethodDetails; + + try + { + + if (!DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del)) + { + return; + } + + var func = md.ExplicitDatas.Length == 0 ? (Func>)del : (Func>)del; + + while (!MainCts.IsCancellationRequested) // 循环中直到栈为空才会退出 + { + object?[]? parameters = singleFlipFlopNode.GetParameters(context, md); + // 调用委托并获取结果 + + FlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters); + + + if (flipflopContext == null) + { + break; + } + else if (flipflopContext.State == FfState.Cancel) + { + break; + } + else if (flipflopContext.State == FfState.Succeed) + { + singleFlipFlopNode.FlowState = true; + singleFlipFlopNode.FlowData = flipflopContext.Data; + var tasks = singleFlipFlopNode.SucceedBranch.Select(nextNode => + { + var context = new DynamicContext(ServiceContainer); + nextNode.PreviousNode = singleFlipFlopNode; + return nextNode.ExecuteStack(context); + }).ToArray(); + Task.WaitAll(tasks); + } + } + } + catch (Exception ex) + { + await Console.Out.WriteLineAsync(ex.ToString()); + } + } + + + public void Exit() + { + ExitAction?.Invoke(); + } + } +} diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj new file mode 100644 index 0000000..e0a20fd --- /dev/null +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -0,0 +1,14 @@ + + + + net8.0-windows7.0 + enable + enable + D:\Project\C#\DynamicControl\SereinFlow\.Output + + + + + + + diff --git a/NodeFlow/SerinExpression/ConditionResolver.cs b/NodeFlow/SerinExpression/ConditionResolver.cs new file mode 100644 index 0000000..8e164aa --- /dev/null +++ b/NodeFlow/SerinExpression/ConditionResolver.cs @@ -0,0 +1,337 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace Serein.NodeFlow.SerinExpression +{ + + public abstract class ConditionResolver + { + public abstract bool Evaluate(object obj); + } + + public class PassConditionResolver : ConditionResolver + { + public Operator Op { get; set; } + public override bool Evaluate(object obj) + { + return Op switch + { + Operator.Pass => true, + Operator.NotPass => false, + _ => throw new NotSupportedException("不支持的条件类型") + }; + } + + public enum Operator + { + Pass, + NotPass, + } + + } + + public class ValueTypeConditionResolver : ConditionResolver where T : struct, IComparable + { + public enum Operator + { + /// + /// 不进行任何操作 + /// + Node, + /// + /// 大于 + /// + GreaterThan, + /// + /// 小于 + /// + LessThan, + /// + /// 等于 + /// + Equal, + /// + /// 大于或等于 + /// + GreaterThanOrEqual, + /// + /// 小于或等于 + /// + LessThanOrEqual, + /// + /// 在两者之间 + /// + InRange, + /// + /// 不在两者之间 + /// + OutOfRange + } + + public Operator Op { get; set; } + public T Value { get; set; } + public T RangeStart { get; set; } + public T RangeEnd { get; set; } + + public string ArithmeticExpression { get; set; } + + + public override bool Evaluate(object obj) + { + if (obj is T typedObj) + { + double numericValue = Convert.ToDouble(typedObj); + if (!string.IsNullOrEmpty(ArithmeticExpression)) + { + numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue); + } + + T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T)); + + return Op switch + { + Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0, + Operator.LessThan => evaluatedValue.CompareTo(Value) < 0, + Operator.Equal => evaluatedValue.CompareTo(Value) == 0, + Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0, + Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0, + Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0, + Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0, + _ => throw new NotSupportedException("不支持的条件类型") + }; + /* switch (Op) + { + case Operator.GreaterThan: + return evaluatedValue.CompareTo(Value) > 0; + case Operator.LessThan: + return evaluatedValue.CompareTo(Value) < 0; + case Operator.Equal: + return evaluatedValue.CompareTo(Value) == 0; + case Operator.GreaterThanOrEqual: + return evaluatedValue.CompareTo(Value) >= 0; + case Operator.LessThanOrEqual: + return evaluatedValue.CompareTo(Value) <= 0; + case Operator.InRange: + return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0; + case Operator.OutOfRange: + return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0; + }*/ + } + return false; + } + } + + public class BoolConditionResolver : ConditionResolver + { + public enum Operator + { + /// + /// 是 + /// + Is + } + + public Operator Op { get; set; } + public bool Value { get; set; } + + public override bool Evaluate(object obj) + { + + if (obj is bool boolObj) + { + return boolObj == Value; + /*switch (Op) + { + case Operator.Is: + return boolObj == Value; + }*/ + } + return false; + } + } + + public class StringConditionResolver : ConditionResolver + { + public enum Operator + { + /// + /// 出现过 + /// + Contains, + /// + /// 没有出现过 + /// + DoesNotContain, + /// + /// 相等 + /// + Equal, + /// + /// 不相等 + /// + NotEqual, + /// + /// 起始字符串等于 + /// + StartsWith, + /// + /// 结束字符串等于 + /// + EndsWith + } + + public Operator Op { get; set; } + + public string Value { get; set; } + + + public override bool Evaluate(object obj) + { + if (obj is string strObj) + { + return Op switch + { + Operator.Contains => strObj.Contains(Value), + Operator.DoesNotContain => !strObj.Contains(Value), + Operator.Equal => strObj == Value, + Operator.NotEqual => strObj != Value, + Operator.StartsWith => strObj.StartsWith(Value), + Operator.EndsWith => strObj.EndsWith(Value), + _ => throw new NotSupportedException("不支持的条件类型"), + }; + + /* switch (Op) + { + case Operator.Contains: + return strObj.Contains(Value); + case Operator.DoesNotContain: + return !strObj.Contains(Value); + case Operator.Equal: + return strObj == Value; + case Operator.NotEqual: + return strObj != Value; + case Operator.StartsWith: + return strObj.StartsWith(Value); + case Operator.EndsWith: + return strObj.EndsWith(Value); + }*/ + } + return false; + } + } + public class MemberConditionResolver : ConditionResolver where T : struct, IComparable + { + //public string MemberPath { get; set; } + public ValueTypeConditionResolver.Operator Op { get; set; } + public object? TargetObj { get; set; } + public T Value { get; set; } + + public string ArithmeticExpression { get; set; } + + public override bool Evaluate(object? obj) + { + //object? memberValue = GetMemberValue(obj, MemberPath); + if (TargetObj is T typedObj) + { + return new ValueTypeConditionResolver + { + Op = Op, + Value = Value, + ArithmeticExpression = ArithmeticExpression, + }.Evaluate(typedObj); + } + return false; + } + + //private object? GetMemberValue(object? obj, string memberPath) + //{ + // string[] members = memberPath[1..].Split('.'); + // foreach (var member in members) + // { + // if (obj == null) return null; + // Type type = obj.GetType(); + // PropertyInfo? propertyInfo = type.GetProperty(member); + // FieldInfo? fieldInfo = type.GetField(member); + // if (propertyInfo != null) + // obj = propertyInfo.GetValue(obj); + // else if (fieldInfo != null) + // obj = fieldInfo.GetValue(obj); + // else + // throw new ArgumentException($"Member {member} not found in type {type.FullName}"); + // } + // return obj; + //} + } + + public class MemberStringConditionResolver : ConditionResolver + { + + public string MemberPath { get; set; } + + public StringConditionResolver.Operator Op { get; set; } + + public string Value { get; set; } + + + public override bool Evaluate(object obj) + { + object memberValue = GetMemberValue(obj, MemberPath); + if (memberValue is string strObj) + { + return new StringConditionResolver + { + Op = Op, + Value = Value + }.Evaluate(strObj); + } + return false; + } + + private object GetMemberValue(object? obj, string memberPath) + { + string[] members = memberPath[1..].Split('.'); + foreach (var member in members) + { + + if (obj == null) return null; + + Type type = obj.GetType(); + PropertyInfo? propertyInfo = type.GetProperty(member); + FieldInfo? fieldInfo = type.GetField(member); + if (propertyInfo != null) + obj = propertyInfo.GetValue(obj); + else if (fieldInfo != null) + obj = fieldInfo.GetValue(obj); + else + throw new ArgumentException($"Member {member} not found in type {type.FullName}"); + } + + return obj; + + } + + + + + + private static string GetArithmeticExpression(string part) + { + int startIndex = part.IndexOf('['); + int endIndex = part.IndexOf(']'); + if (startIndex >= 0 && endIndex > startIndex) + { + return part.Substring(startIndex + 1, endIndex - startIndex - 1); + } + + return null; + + } + + + + + + } + +} diff --git a/NodeFlow/SerinExpression/SerinConditionParser.cs b/NodeFlow/SerinExpression/SerinConditionParser.cs new file mode 100644 index 0000000..210c14d --- /dev/null +++ b/NodeFlow/SerinExpression/SerinConditionParser.cs @@ -0,0 +1,329 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Reflection; + +namespace Serein.NodeFlow.SerinExpression; + +public class SerinConditionParser +{ + public static bool To(T data, string expression) + { + try + { + + return ConditionParse(data, expression).Evaluate(data); + + } + catch (Exception ex) + { + Console.WriteLine(ex); + throw; + } + } + + public static ConditionResolver ConditionParse(object data, string expression) + { + if (expression.StartsWith('.') /*&& expression.Contains('<') && expression.Contains('>')*/) + { + return ParseObjectExpression(data, expression); + } + else + { + return ParseSimpleExpression(data, expression); + } + + + bool ContainsArithmeticOperators(string expression) + { + return expression.Contains('+') || expression.Contains('-') || expression.Contains('*') || expression.Contains('/'); + } + + } + + private static string GetArithmeticExpression(string part) + { + int startIndex = part.IndexOf('['); + int endIndex = part.IndexOf(']'); + if (startIndex >= 0 && endIndex > startIndex) + { + return part.Substring(startIndex + 1, endIndex - startIndex - 1); + } + + return null; + + } + private static object? GetMemberValue(object? obj, string memberPath) + { + string[] members = memberPath[1..].Split('.'); + foreach (var member in members) + { + if (obj == null) return null; + Type type = obj.GetType(); + PropertyInfo? propertyInfo = type.GetProperty(member); + FieldInfo? fieldInfo = type.GetField(member); + if (propertyInfo != null) + obj = propertyInfo.GetValue(obj); + else if (fieldInfo != null) + obj = fieldInfo.GetValue(obj); + else + throw new ArgumentException($"Member {member} not found in type {type.FullName}"); + } + return obj; + } + + private static ConditionResolver ParseObjectExpression(object data, string expression) + { + var parts = expression.Split(' '); + string operatorStr = parts[0]; + string valueStr = string.Join(' ', parts, 1, parts.Length - 1); + + int typeStartIndex = expression.IndexOf('<'); + int typeEndIndex = expression.IndexOf('>'); + + string memberPath; + Type type; + object? targetObj; + if (typeStartIndex + typeStartIndex == -2) + { + memberPath = operatorStr; + targetObj = GetMemberValue(data, operatorStr); + + type = targetObj.GetType(); + + operatorStr = parts[1].ToLower(); + valueStr = string.Join(' ', parts.Skip(2)); + } + else + { + if (typeStartIndex >= typeEndIndex) + { + throw new ArgumentException("无效的表达式格式"); + } + memberPath = expression.Substring(0, typeStartIndex).Trim(); + string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1).Trim().ToLower(); + parts = expression.Substring(typeEndIndex + 1).Trim().Split(' '); + if (parts.Length == 3) + { + operatorStr = parts[1].ToLower(); + valueStr = string.Join(' ', parts.Skip(2)); + } + else + { + operatorStr = parts[0].ToLower(); + valueStr = string.Join(' ', parts.Skip(1)); + } + targetObj = GetMemberValue(data, memberPath); + + Type? tempType = typeStr switch + { + "int" => typeof(int), + "double" => typeof(double), + "bool" => typeof(bool), + "string" => typeof(string), + _ => Type.GetType(typeStr) + }; + type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明"); + } + + + + if (type == typeof(int)) + { + int value = int.Parse(valueStr, CultureInfo.InvariantCulture); + return new MemberConditionResolver + { + TargetObj = targetObj, + //MemberPath = memberPath, + Op = ParseValueTypeOperator(operatorStr), + Value = value, + ArithmeticExpression = GetArithmeticExpression(parts[0]) + }; + } + else if (type == typeof(double)) + { + double value = double.Parse(valueStr, CultureInfo.InvariantCulture); + return new MemberConditionResolver + { + //MemberPath = memberPath, + TargetObj = targetObj, + Op = ParseValueTypeOperator(operatorStr), + Value = value, + ArithmeticExpression = GetArithmeticExpression(parts[0]) + }; + + } + else if (type == typeof(bool)) + { + return new MemberConditionResolver + { + //MemberPath = memberPath, + TargetObj = targetObj, + Op = (ValueTypeConditionResolver.Operator)ParseBoolOperator(operatorStr) + }; + } + else if (type == typeof(string)) + { + return new MemberStringConditionResolver + { + MemberPath = memberPath, + Op = ParseStringOperator(operatorStr), + Value = valueStr + }; + } + + throw new NotSupportedException($"Type {type} is not supported."); + } + + private static ConditionResolver ParseSimpleExpression(object data, string expression) + { + if ("pass".Equals(expression.ToLower())) + { + return new PassConditionResolver + { + Op = PassConditionResolver.Operator.Pass, + }; + } + else + { + if ("not pass".Equals(expression.ToLower())) + { + return new PassConditionResolver + { + Op = PassConditionResolver.Operator.NotPass, + }; + } + if ("!pass".Equals(expression.ToLower())) + { + return new PassConditionResolver + { + Op = PassConditionResolver.Operator.NotPass, + }; + } + } + + + var parts = expression.Split(' '); + + if (parts.Length < 2) + throw new ArgumentException("无效的表达式格式。"); + + //string typeStr = parts[0]; + string operatorStr = parts[0]; + string valueStr = string.Join(' ', parts, 1, parts.Length - 1); + + Type type = data.GetType();//Type.GetType(typeStr); + if (type == typeof(int)) + { + var op = ParseValueTypeOperator(operatorStr); + if (op == ValueTypeConditionResolver.Operator.InRange || op == ValueTypeConditionResolver.Operator.OutOfRange) + { + var temp = valueStr.Split('-'); + if (temp.Length < 2) + throw new ArgumentException($"范围无效:{valueStr}。"); + int rangeStart = int.Parse(temp[0], CultureInfo.InvariantCulture); + int rangeEnd = int.Parse(temp[1], CultureInfo.InvariantCulture); + return new ValueTypeConditionResolver + { + Op = op, + RangeStart = rangeStart, + RangeEnd = rangeEnd, + ArithmeticExpression = GetArithmeticExpression(parts[0]), + }; + } + else + { + int value = int.Parse(valueStr, CultureInfo.InvariantCulture); + return new ValueTypeConditionResolver + { + Op = op, + Value = value, + ArithmeticExpression = GetArithmeticExpression(parts[0]) + }; + + } + + } + else if (type == typeof(double)) + { + double value = double.Parse(valueStr, CultureInfo.InvariantCulture); + return new ValueTypeConditionResolver + { + Op = ParseValueTypeOperator(operatorStr), + Value = value, + ArithmeticExpression = GetArithmeticExpression(parts[0]) + }; + } + else if (type == typeof(bool)) + { + bool value = bool.Parse(valueStr); + return new BoolConditionResolver + { + Op = ParseBoolOperator(operatorStr), + Value = value, + }; + } + else if (type == typeof(string)) + { + return new StringConditionResolver + { + Op = ParseStringOperator(operatorStr), + Value = valueStr + }; + } + + throw new NotSupportedException($"Type {type} is not supported."); + } + + + private static ValueTypeConditionResolver.Operator ParseValueTypeOperator(string operatorStr) where T : struct, IComparable + { + return operatorStr switch + { + ">" => ValueTypeConditionResolver.Operator.GreaterThan, + "<" => ValueTypeConditionResolver.Operator.LessThan, + "==" => ValueTypeConditionResolver.Operator.Equal, + ">=" => ValueTypeConditionResolver.Operator.GreaterThanOrEqual, + "≥" => ValueTypeConditionResolver.Operator.GreaterThanOrEqual, + "<=" => ValueTypeConditionResolver.Operator.LessThanOrEqual, + "≤" => ValueTypeConditionResolver.Operator.LessThanOrEqual, + "equals" => ValueTypeConditionResolver.Operator.Equal, + "in" => ValueTypeConditionResolver.Operator.InRange, + "!in" => ValueTypeConditionResolver.Operator.OutOfRange, + _ => throw new ArgumentException($"Invalid operator {operatorStr} for value type.") + }; + } + + private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr) + { + return operatorStr switch + { + "is" => BoolConditionResolver.Operator.Is, + "==" => BoolConditionResolver.Operator.Is, + "equals" => BoolConditionResolver.Operator.Is, + //"isFalse" => BoolConditionNode.Operator.IsFalse, + _ => throw new ArgumentException($"Invalid operator {operatorStr} for bool type.") + }; + } + + private static StringConditionResolver.Operator ParseStringOperator(string operatorStr) + { + return operatorStr switch + { + "c" => StringConditionResolver.Operator.Contains, + "nc" => StringConditionResolver.Operator.DoesNotContain, + "sw" => StringConditionResolver.Operator.StartsWith, + "ew" => StringConditionResolver.Operator.EndsWith, + + "contains" => StringConditionResolver.Operator.Contains, + "doesNotContain" => StringConditionResolver.Operator.DoesNotContain, + "equals" => StringConditionResolver.Operator.Equal, + "==" => StringConditionResolver.Operator.Equal, + "notEquals" => StringConditionResolver.Operator.NotEqual, + "!=" => StringConditionResolver.Operator.NotEqual, + "startsWith" => StringConditionResolver.Operator.StartsWith, + "endsWith" => StringConditionResolver.Operator.EndsWith, + _ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.") + }; + } +} diff --git a/NodeFlow/SerinExpression/SerinExpressionEvaluator.cs b/NodeFlow/SerinExpression/SerinExpressionEvaluator.cs new file mode 100644 index 0000000..decf1bc --- /dev/null +++ b/NodeFlow/SerinExpression/SerinExpressionEvaluator.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static Serein.NodeFlow.Model.SingleExpOpNode; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace Serein.NodeFlow.SerinExpression +{ + public class SerinArithmeticExpressionEvaluator + { + private static readonly DataTable table = new DataTable(); + + public static double Evaluate(string expression, double inputValue) + { + // 替换占位符@为输入值 + expression = expression.Replace("@", inputValue.ToString()); + try + { + // 使用 DataTable.Compute 方法计算表达式 + var result = table.Compute(expression, string.Empty); + return Convert.ToDouble(result); + } + catch + { + throw new ArgumentException("Invalid arithmetic expression."); + } + } + } + + public class SerinExpressionEvaluator + { + public static object Evaluate(string expression, object targetObJ, out bool IsChange) + { + var parts = expression.Split([' '], 2); + if (parts.Length != 2) + { + throw new ArgumentException("Invalid expression format."); + } + + var operation = parts[0].ToLower(); + var operand = parts[1][0] == '.' ? parts[1][1..] : parts[1]; + + var result = operation switch + { + "@num" => ComputedNumber(targetObJ, operand), + "@call" => InvokeMethod(targetObJ, operand), + "@get" => GetMember(targetObJ, operand), + "@set" => SetMember(targetObJ, operand), + _ => throw new NotSupportedException($"Operation {operation} is not supported.") + }; + + IsChange = operation switch + { + "@num" => true, + "@call" => true, + "@get" => true, + "@set" => false, + _ => throw new NotSupportedException($"Operation {operation} is not supported.") + }; + + return result; + } + + + private static readonly char[] separator = ['(', ')']; + private static readonly char[] separatorArray = [',']; + + private static object InvokeMethod(object target, string methodCall) + { + var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries); + if (methodParts.Length != 2) + { + throw new ArgumentException("Invalid method call format."); + } + + var methodName = methodParts[0]; + var parameterList = methodParts[1]; + var parameters = parameterList.Split(separatorArray, StringSplitOptions.RemoveEmptyEntries) + .Select(p => p.Trim()) + .ToArray(); + + var method = target.GetType().GetMethod(methodName); + if (method == null) + { + throw new ArgumentException($"Method {methodName} not found on target."); + } + + var parameterValues = method.GetParameters() + .Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType)) + .ToArray(); + + + return method.Invoke(target, parameterValues); + + } + + private static object GetMember(object target, string memberPath) + { + var members = memberPath.Split('.'); + foreach (var member in members) + { + + if (target == null) return null; + + + var property = target.GetType().GetProperty(member); + if (property != null) + { + + target = property.GetValue(target); + + } + else + { + var field = target.GetType().GetField(member); + if (field != null) + { + + target = field.GetValue(target); + + } + else + { + throw new ArgumentException($"Member {member} not found on target."); + } + } + } + + + return target; + + } + + private static object SetMember(object target, string assignment) + { + var parts = assignment.Split(new[] { '=' }, 2); + if (parts.Length != 2) + { + throw new ArgumentException("Invalid assignment format."); + } + + var memberPath = parts[0].Trim(); + var value = parts[1].Trim(); + + var members = memberPath.Split('.'); + for (int i = 0; i < members.Length - 1; i++) + { + var member = members[i]; + + var property = target.GetType().GetProperty(member); + + if (property != null) + { + + target = property.GetValue(target); + + } + else + { + var field = target.GetType().GetField(member); + if (field != null) + { + + target = field.GetValue(target); + + } + else + { + throw new ArgumentException($"Member {member} not found on target."); + } + } + } + + var lastMember = members.Last(); + + var lastProperty = target.GetType().GetProperty(lastMember); + + if (lastProperty != null) + { + var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType); + lastProperty.SetValue(target, convertedValue); + } + else + { + var lastField = target.GetType().GetField(lastMember); + if (lastField != null) + { + var convertedValue = Convert.ChangeType(value, lastField.FieldType); + lastField.SetValue(target, convertedValue); + } + else + { + throw new ArgumentException($"Member {lastMember} not found on target."); + } + } + + return target; + } + + private static double ComputedNumber(object value, string expression) + { + double numericValue = Convert.ToDouble(value); + if (!string.IsNullOrEmpty(expression)) + { + numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue); + } + + return numericValue; + } + } +} diff --git a/NodeFlow/Tool/DelegateGenerator.cs b/NodeFlow/Tool/DelegateGenerator.cs new file mode 100644 index 0000000..a3dbee9 --- /dev/null +++ b/NodeFlow/Tool/DelegateGenerator.cs @@ -0,0 +1,193 @@ +using Serein.Library.IOC; +using Serein.NodeFlow; +using System.Collections.Concurrent; +using System.Reflection; + +namespace Serein.NodeFlow.Tool; + + +public static class DelegateCache +{ + /// + /// 委托缓存全局字典 + /// + public static ConcurrentDictionary GlobalDicDelegates { get; } = new ConcurrentDictionary(); +} + +public static class DelegateGenerator +{ + // 缓存的实例对象(键:类型名称) + public static ConcurrentDictionary DynamicInstanceToType { get; } = new ConcurrentDictionary(); + // 缓存的实例对象 (键:生成的方法名称) + // public static ConcurrentDictionary DynamicInstance { get; } = new ConcurrentDictionary(); + + /// + /// 生成方法信息 + /// + /// + /// + /// + public static ConcurrentDictionary GenerateMethodDetails(IServiceContainer serviceContainer, Type type) + { + var methodDetailsDictionary = new ConcurrentDictionary(); + var assemblyName = type.Assembly.GetName().Name; + var methods = GetMethodsToProcess(type); + + foreach (var method in methods) + { + + var methodDetails = CreateMethodDetails(serviceContainer, type, method, assemblyName); + + methodDetailsDictionary.TryAdd(methodDetails.MethodName, methodDetails); + } + + return methodDetailsDictionary; + } + + /// + /// 获取处理方法 + /// + private static IEnumerable GetMethodsToProcess(Type type) + { + return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(m => m.GetCustomAttribute()?.Scan == true); + } + /// + /// 创建方法信息 + /// + /// + private static MethodDetails CreateMethodDetails(IServiceContainer serviceContainer, Type type, MethodInfo method, string assemblyName) + { + var methodName = method.Name; + var attribute = method.GetCustomAttribute(); + + var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters()); + // 生成委托 + var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型 + method, // 方法信息 + method.GetParameters(),// 方法参数 + method.ReturnType);// 返回值 + + + var dllTypeName = $"{assemblyName}.{type.Name}"; + serviceContainer.Register(type); + object instance = serviceContainer.GetOrCreateServiceInstance(type); + var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}"; + + + + return new MethodDetails + { + ActingInstanceType = type, + ActingInstance = instance, + MethodName = dllTypeMethodName, + MethodDelegate = methodDelegate, + MethodDynamicType = attribute.MethodDynamicType, + MethodLockName = attribute.LockName, + MethodTips = attribute.MethodTips, + ExplicitDatas = explicitDataOfParameters, + ReturnType = method.ReturnType, + }; + + } + + private static ExplicitData[] GetExplicitDataOfParameters(ParameterInfo[] parameters) + { + + return parameters.Select((it, index) => + { + //Console.WriteLine($"{it.Name}-{it.HasDefaultValue}-{it.DefaultValue}"); + string explicitTypeName = GetExplicitTypeName(it.ParameterType); + var items = GetExplicitItems(it.ParameterType, explicitTypeName); + if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型 + + + + return new ExplicitData + { + IsExplicitData = it.GetCustomAttribute(typeof(ExplicitAttribute)) is ExplicitAttribute, + Index = index, + ExplicitType = it.ParameterType, + ExplicitTypeName = explicitTypeName, + DataType = it.ParameterType, + ParameterName = it.Name, + DataValue = it.HasDefaultValue ? it.DefaultValue.ToString() : "", + Items = items.ToArray(), + }; + + + + }).ToArray(); + } + + private static string GetExplicitTypeName(Type type) + { + return type switch + { + Type t when t.IsEnum => "Select", + Type t when t == typeof(bool) => "Bool", + Type t when t == typeof(string) => "Value", + Type t when t == typeof(int) => "Value", + Type t when t == typeof(double) => "Value", + _ => "Value" + }; + } + + private static IEnumerable GetExplicitItems(Type type, string explicitTypeName) + { + return explicitTypeName switch + { + "Select" => Enum.GetNames(type), + "Bool" => ["True", "False"], + _ => [] + }; + + } + + private static Delegate GenerateMethodDelegate(Type type, MethodInfo methodInfo, ParameterInfo[] parameters, Type returnType) + { + var parameterTypes = parameters.Select(p => p.ParameterType).ToArray(); + var parameterCount = parameters.Length; + + if (returnType == typeof(void)) + { + if (parameterCount == 0) + { + // 无返回值,无参数 + return ExpressionHelper.MethodCaller(type, methodInfo); + } + else + { + // 无返回值,有参数 + return ExpressionHelper.MethodCaller(type, methodInfo, parameterTypes); + } + } + else if (returnType == typeof(Task)) // 触发器 + { + if (parameterCount == 0) + { + // 有返回值,无参数 + return ExpressionHelper.MethodCallerAsync(type, methodInfo); + } + else + { + // 有返回值,有参数 + return ExpressionHelper.MethodCallerAsync(type, methodInfo, parameterTypes); + } + } + else + { + if (parameterCount == 0) + { + // 有返回值,无参数 + return ExpressionHelper.MethodCallerHaveResult(type, methodInfo); + } + else + { + // 有返回值,有参数 + return ExpressionHelper.MethodCallerHaveResult(type, methodInfo, parameterTypes); + } + } + } + +} diff --git a/NodeFlow/Tool/DynamicTool.cs b/NodeFlow/Tool/DynamicTool.cs new file mode 100644 index 0000000..8645e6b --- /dev/null +++ b/NodeFlow/Tool/DynamicTool.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Drawing.Printing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.NodeFlow.Tool +{ + + #region 锁、tsk工具 (已注释) + /*public class LockManager + { + private readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); + + public void CreateLock(string name) + { + _locks.TryAdd(name, new LockQueue()); + } + + public async Task AcquireLockAsync(string name, CancellationToken cancellationToken = default) + { + if (!_locks.ContainsKey(name)) + { + throw new ArgumentException($"Lock with name '{name}' does not exist."); + } + + var lockQueue = _locks[name]; + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + lock (lockQueue.Queue) + { + lockQueue.Queue.Enqueue(tcs); + if (lockQueue.Queue.Count == 1) + { + tcs.SetResult(true); + } + } + + await tcs.Task.ConfigureAwait(false); + + // 处理取消操作 + if (cancellationToken.CanBeCanceled) + { + cancellationToken.Register(() => + { + lock (lockQueue.Queue) + { + if (lockQueue.Queue.Contains(tcs)) + { + tcs.TrySetCanceled(); + } + } + }); + } + } + + public void ReleaseLock(string name) + { + if (!_locks.ContainsKey(name)) + { + throw new ArgumentException($"Lock with name '{name}' does not exist."); + } + + var lockQueue = _locks[name]; + + lock (lockQueue.Queue) + { + if (lockQueue.Queue.Count > 0) + { + lockQueue.Queue.Dequeue(); + + if (lockQueue.Queue.Count > 0) + { + var next = lockQueue.Queue.Peek(); + next.SetResult(true); + } + } + } + } + + private class LockQueue + { + public Queue> Queue { get; } = new Queue>(); + } + } + + + public interface ITaskResult + { + object Result { get; } + } + + public class TaskResult : ITaskResult + { + public TaskResult(T result) + { + Result = result; + } + + public T Result { get; } + + object ITaskResult.Result => Result; + } + + public class DynamicTasks + { + private static readonly ConcurrentDictionary> TaskGuidPairs = new(); + public static Task GetTask(string Guid) + { + TaskGuidPairs.TryGetValue(Guid, out Task task); + return task; + } + + public static bool AddTask(string Guid, T result) + { + var task = Task.FromResult(new TaskResult(result)); + + return TaskGuidPairs.TryAdd(Guid, task); + } + } + public class TaskNodeManager + { + private readonly ConcurrentDictionary _taskQueues = new ConcurrentDictionary(); + + public void CreateTaskNode(string name) + { + _taskQueues.TryAdd(name, new TaskQueue()); + } + + public async Task WaitForTaskNodeAsync(string name, CancellationToken cancellationToken = default) + { + if (!_taskQueues.ContainsKey(name)) + { + throw new ArgumentException($"Task node with name '{name}' does not exist."); + } + + var taskQueue = _taskQueues[name]; + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + lock (taskQueue.Queue) + { + taskQueue.Queue.Enqueue(tcs); + if (taskQueue.Queue.Count == 1) + { + tcs.SetResult(true); + } + } + + await tcs.Task.ConfigureAwait(false); + + // 处理取消操作 + if (cancellationToken.CanBeCanceled) + { + cancellationToken.Register(() => + { + lock (taskQueue.Queue) + { + if (taskQueue.Queue.Contains(tcs)) + { + tcs.TrySetCanceled(); + } + } + }); + } + } + + public void CompleteTaskNode(string name) + { + if (!_taskQueues.ContainsKey(name)) + { + throw new ArgumentException($"Task node with name '{name}' does not exist."); + } + + var taskQueue = _taskQueues[name]; + + lock (taskQueue.Queue) + { + if (taskQueue.Queue.Count > 0) + { + taskQueue.Queue.Dequeue(); + + if (taskQueue.Queue.Count > 0) + { + var next = taskQueue.Queue.Peek(); + next.SetResult(true); + } + } + } + } + + private class TaskQueue + { + public Queue> Queue { get; } = new Queue>(); + } + }*/ + #endregion + + + +} diff --git a/NodeFlow/Tool/ExpressionHelper.cs b/NodeFlow/Tool/ExpressionHelper.cs new file mode 100644 index 0000000..e0cc5fd --- /dev/null +++ b/NodeFlow/Tool/ExpressionHelper.cs @@ -0,0 +1,741 @@ +using System.Collections.Concurrent; +using System.Linq.Expressions; +using System.Reflection; +using Serein.NodeFlow; + +namespace Serein.NodeFlow.Tool +{ + /// + /// 对于实例创建的表达式树反射 + /// + public static class ExpressionHelper + { + /// + /// 缓存表达式树反射方法 + /// + private static ConcurrentDictionary Cache { get; } = new ConcurrentDictionary(); + + public static List GetCacheKey() + { + return [.. Cache.Keys]; + } + + #region 基于类型的表达式反射构建委托 + + #region 属性、字段的委托创建(表达式反射) + + /// + /// 动态获取属性值 + /// + public static Delegate PropertyGetter(Type type, string propertyName) + { + string cacheKey = $"{type.FullName}.{propertyName}.Getter"; + return Cache.GetOrAdd(cacheKey, _ => CreateGetterDelegate(type, propertyName)); + } + /// + /// 动态获取属性值 + /// + private static Delegate CreateGetterDelegate(Type type, string propertyName) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var property = Expression.Property(Expression.Convert(parameter, type), propertyName); + var lambda = Expression.Lambda(Expression.Convert(property, typeof(object)), parameter); + return lambda.Compile(); + } + + /// + /// 动态设置属性值 + /// + public static Delegate PropertySetter(Type type, string propertyName) + { + string cacheKey = $"{type.FullName}.{propertyName}.Setter"; + return Cache.GetOrAdd(cacheKey, _ => CreateSetterDelegate(type, propertyName)); + } + + /// + /// 动态设置属性值 + /// + private static Delegate CreateSetterDelegate(Type type, string propertyName) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var value = Expression.Parameter(typeof(object), "value"); + var property = Expression.Property(Expression.Convert(parameter, type), propertyName); + var assign = Expression.Assign(property, Expression.Convert(value, property.Type)); + var lambda = Expression.Lambda(assign, parameter, value); + return lambda.Compile(); + } + + /// + /// 动态获取字段值 + /// + public static Delegate FieldGetter(Type type, string fieldName) + { + string cacheKey = $"{type.FullName}.{fieldName}.FieldGetter"; + return Cache.GetOrAdd(cacheKey, _ => CreateFieldGetterDelegate(type, fieldName)); + } + /// + /// 动态获取字段值 + /// + private static Delegate CreateFieldGetterDelegate(Type type, string fieldName) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var field = Expression.Field(Expression.Convert(parameter, type), fieldName); + var lambda = Expression.Lambda(Expression.Convert(field, typeof(object)), parameter); + return lambda.Compile(); + } + + /// + /// 动态设置字段值 + /// + public static Delegate FieldSetter(Type type, string fieldName) + { + string cacheKey = $"{type.FullName}.{fieldName}.FieldSetter"; + return Cache.GetOrAdd(cacheKey, _ => CreateFieldSetterDelegate(type, fieldName)); + } + /// + /// 动态设置字段值 + /// + private static Delegate CreateFieldSetterDelegate(Type type, string fieldName) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var value = Expression.Parameter(typeof(object), "value"); + var field = Expression.Field(Expression.Convert(parameter, type), fieldName); + var assign = Expression.Assign(field, Expression.Convert(value, field.Type)); + var lambda = Expression.Lambda(assign, parameter, value); + return lambda.Compile(); + } + + #endregion + + + + /// + /// 表达式树构建无参数,无返回值方法 + /// + public static Delegate MethodCaller(Type type, MethodInfo methodInfo) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCaller"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodInfo)); + } + + /// + /// 表达式树构建无参数,无返回值方法 + /// + private static Delegate CreateMethodCallerDelegate(Type type, MethodInfo methodInfo) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo); + var lambda = Expression.Lambda(methodCall, parameter); + // Action + return lambda.Compile(); + } + + /// + /// 表达式树构建无参数,有返回值方法 + /// + public static Delegate MethodCallerHaveResult(Type type, MethodInfo methodInfo) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerHaveResult"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodInfo)); + } + /// + /// 表达式树构建无参数,有返回值方法 + /// + private static Delegate CreateMethodCallerDelegateHaveResult(Type type, MethodInfo methodInfo) + { + 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(); + } + + + /// + /// 表达式树构建多个参数,无返回值的方法 + /// + public static Delegate MethodCaller(Type type, MethodInfo methodInfo, params Type[] parameterTypes) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCaller"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodInfo, parameterTypes)); + } + + /// + /// 表达式树构建多个参数,无返回值的方法 + /// + private static Delegate CreateMethodCallerDelegate(Type type, MethodInfo methodInfo, Type[] parameterTypes) + { + /* var parameter = Expression.Parameter(typeof(object), "instance"); + + var arguments = parameterTypes.Select((t, i) => Expression.Parameter(typeof(object), $"arg{i}")).ToArray(); + + var convertedArguments = arguments.Select((arg, i) => Expression.Convert(arg, parameterTypes[i])).ToArray(); + var methodCall = Expression.Call(Expression.Convert(parameter, type), + methodInfo, + convertedArguments); + var lambda = Expression.Lambda(methodCall, new[] { parameter }.Concat(arguments)); + var tmpAction = lambda.Compile(); + + // Action + return lambda.Compile();*/ + + var instanceParam = Expression.Parameter(typeof(object), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + + // 创建参数表达式 + var convertedArgs = parameterTypes.Select((paramType, index) => + Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType) + ).ToArray(); + + + // 创建方法调用表达式 + var methodCall = Expression.Call( + Expression.Convert(instanceParam, type), + methodInfo, + convertedArgs + ); + + // 创建 lambda 表达式 + var lambda = Expression.Lambda( + methodCall, + instanceParam, + argsParam + ); + + // Func + return lambda.Compile(); + } + + /// + /// 表达式树构建多个参数,有返回值的方法 + /// + public static Delegate MethodCallerHaveResult(Type type, MethodInfo methodInfo, Type[] parameterTypes) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerHaveResult"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodInfo, parameterTypes)); + } + /// + /// 表达式树构建多个参数,有返回值的方法 + /// + private static Delegate CreateMethodCallerDelegateHaveResult(Type type, MethodInfo methodInfo, Type[] parameterTypes) + { + /*var instanceParam = Expression.Parameter(typeof(object), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + + // 创建参数表达式 + var convertedArgs = parameterTypes.Select((paramType, index) => + Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType) + ).ToArray(); + + + // 创建方法调用表达式 + var methodCall = Expression.Call( + Expression.Convert(instanceParam, type), + methodInfo, + convertedArgs + ); + + // 创建 lambda 表达式 + var lambda = Expression.Lambda( + Expression.Convert(methodCall, typeof(object)), + instanceParam, + argsParam + ); + + // Func + return lambda.Compile();*/ + + var instanceParam = Expression.Parameter(typeof(object), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + + // 创建参数表达式 + var convertedArgs = parameterTypes.Select((paramType, index) => + Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType) + ).ToArray(); + + + // 创建方法调用表达式 + var methodCall = Expression.Call( + Expression.Convert(instanceParam, type), + methodInfo, + 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(); + } + + + /// + /// 表达式树构建无参数,有返回值(Task)的方法(触发器) + /// + public static Delegate MethodCallerAsync(Type type, MethodInfo methodInfo) + { + string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerAsync"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateAsync(type, methodInfo)); + } + /// + /// 表达式树构建无参数,有返回值(Task)的方法(触发器) + /// + private static Delegate CreateMethodCallerDelegateAsync(Type type, MethodInfo methodInfo) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo); + var lambda = Expression.Lambda>>( + Expression.Convert(methodCall, typeof(Task)), parameter); + // Func> + return lambda.Compile(); + } + + + + /// + /// 表达式树构建多个参数,有返回值(Task-object)的方法(触发器) + /// + public static Delegate MethodCallerAsync(Type type, MethodInfo method, params Type[] parameterTypes) + { + + string cacheKey = $"{type.FullName}.{method.Name}.MethodCallerAsync"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateAsync(type, method, parameterTypes)); + } + /// + /// 表达式树构建多个参数,有返回值(Task)的方法(触发器) + /// + private static Delegate CreateMethodCallerDelegateAsync(Type type, MethodInfo methodInfo, Type[] parameterTypes) + { + var instanceParam = Expression.Parameter(typeof(object), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + + // 创建参数表达式 + var convertedArgs = parameterTypes.Select((paramType, index) => + Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType) + ).ToArray(); + + + // 创建方法调用表达式 + var methodCall = Expression.Call( + Expression.Convert(instanceParam, type), + methodInfo, + convertedArgs + ); + + // 创建 lambda 表达式 + var lambda = Expression.Lambda>>( + Expression.Convert(methodCall, typeof(Task)), + instanceParam, + argsParam + ); + //var resule = task.DynamicInvoke((object)[Activator.CreateInstance(type), [new DynamicContext(null)]]); + return lambda.Compile(); + } + + + + #region 单参数 + + /// + /// 表达式树构建单参数,无返回值的方法 + /// + public static Delegate MethodCaller(Type type, string methodName, Type parameterType) + { + string cacheKey = $"{type.FullName}.{methodName}.MethodCallerWithParam"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodName, parameterType)); + } + /// + /// 表达式树构建单参数,无返回值的方法 + /// + private static Delegate CreateMethodCallerDelegate(Type type, string methodName, Type parameterType) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var argument = Expression.Parameter(typeof(object), "argument"); + var methodCall = Expression.Call(Expression.Convert(parameter, type), + type.GetMethod(methodName, [parameterType])!, + Expression.Convert(argument, parameterType)); + var lambda = Expression.Lambda(methodCall, parameter, argument); + return lambda.Compile(); + } + + /// + /// 表达式树构建单参数,有返回值的方法 + /// + public static Delegate MethodCallerWithResult(Type type, string methodName, Type parameterType, Type returnType) + { + string cacheKey = $"{type.FullName}.{methodName}.MethodCallerWithResult"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateWithResult(type, methodName, parameterType, returnType)); + } + /// + /// 表达式树构建单参数,有返回值的方法 + /// + private static Delegate CreateMethodCallerDelegateWithResult(Type type, string methodName, Type parameterType, Type returnType) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var argument = Expression.Parameter(typeof(object), "argument"); + var methodCall = Expression.Call(Expression.Convert(parameter, type), + type.GetMethod(methodName, [parameterType])!, + Expression.Convert(argument, parameterType)); + var lambda = Expression.Lambda(Expression.Convert(methodCall, typeof(object)), parameter, argument); + + + return lambda.Compile(); + } + + #endregion + + #endregion + + + #region 泛型表达式反射构建方法(已注释) + /* + + + /// + /// 动态获取属性值 + /// + /// + /// + /// + /// + public static Func PropertyGetter(string propertyName) + { + string cacheKey = $"{typeof(T).FullName}.{propertyName}.Getter"; + return (Func)Cache.GetOrAdd(cacheKey, _ => CreateGetterDelegate(propertyName)); + } + + private static Func CreateGetterDelegate(string propertyName) + { + var parameter = Expression.Parameter(typeof(T), "instance"); + var property = Expression.Property(parameter, propertyName); + var lambda = Expression.Lambda>(property, parameter); + return lambda.Compile(); + } + + /// + /// 动态设置属性值 + /// + /// + /// + /// + /// + public static Action PropertySetter(string propertyName) + { + string cacheKey = $"{typeof(T).FullName}.{propertyName}.Setter"; + return (Action)Cache.GetOrAdd(cacheKey, _ => CreateSetterDelegate(propertyName)); + } + + private static Action CreateSetterDelegate(string propertyName) + { + var parameter = Expression.Parameter(typeof(T), "instance"); + var value = Expression.Parameter(typeof(TProperty), "value"); + var property = Expression.Property(parameter, propertyName); + var assign = Expression.Assign(property, value); + var lambda = Expression.Lambda>(assign, parameter, value); + return lambda.Compile(); + } + + /// + /// 动态获取字段值 + /// + /// + /// + /// + /// + public static Func FieldGetter(string fieldName) + { + string cacheKey = $"{typeof(T).FullName}.{fieldName}.FieldGetter"; + return (Func)Cache.GetOrAdd(cacheKey, _ => CreateFieldGetterDelegate(fieldName)); + } + + private static Func CreateFieldGetterDelegate(string fieldName) + { + var parameter = Expression.Parameter(typeof(T), "instance"); + var field = Expression.Field(parameter, fieldName); + var lambda = Expression.Lambda>(field, parameter); + return lambda.Compile(); + } + + /// + /// 动态设置字段值 + /// + /// + /// + /// + /// + public static Action FieldSetter(string fieldName) + { + string cacheKey = $"{typeof(T).FullName}.{fieldName}.FieldSetter"; + return (Action)Cache.GetOrAdd(cacheKey, _ => CreateFieldSetterDelegate(fieldName)); + } + + private static Action CreateFieldSetterDelegate(string fieldName) + { + var parameter = Expression.Parameter(typeof(T), "instance"); + var value = Expression.Parameter(typeof(TField), "value"); + var field = Expression.Field(parameter, fieldName); + var assign = Expression.Assign(field, value); + var lambda = Expression.Lambda>(assign, parameter, value); + return lambda.Compile(); + } + + + + + + /// + /// 动态调用无参数方法 + /// + /// + /// + /// + public static Action MethodCaller(string methodName) + { + string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCaller"; + return (Action)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(methodName)); + } + + private static Action CreateMethodCallerDelegate(string methodName) + { + var parameter = Expression.Parameter(typeof(T), "instance"); + var methodCall = Expression.Call(parameter, typeof(T).GetMethod(methodName)); + var lambda = Expression.Lambda>(methodCall, parameter); + return lambda.Compile(); + } + + /// + /// 动态调用无参有返回值方法 + /// + /// + /// + /// + /// + public static Func MethodCallerHaveResul(string methodName) + { + string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCaller"; + return (Func)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(methodName)); + } + + private static Func CreateMethodCallerDelegateHaveResult(string methodName) + { + var parameter = Expression.Parameter(typeof(T), "instance"); + var methodCall = Expression.Call(parameter, typeof(T).GetMethod(methodName)); + var lambda = Expression.Lambda>(methodCall, parameter); + return lambda.Compile(); + } + + + /// + /// 动态调用单参数无返回值的方法 + /// + /// + /// + /// + /// + public static Action MethodCaller(string methodName) + { + string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCallerWithParam"; + return (Action)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(methodName)); + } + + private static Action CreateMethodCallerDelegate(string methodName) + { + var parameter = Expression.Parameter(typeof(T), "instance"); + var argument = Expression.Parameter(typeof(TParam), "argument"); + var methodCall = Expression.Call(parameter, typeof(T).GetMethod(methodName), argument); + var lambda = Expression.Lambda>(methodCall, parameter, argument); + return lambda.Compile(); + } + + /// + /// 动态调用单参数有返回值的方法 + /// + /// + /// + /// + /// + /// + public static Func MethodCallerWithResult(string methodName) + { + string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCallerWithResult"; + return (Func)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(methodName)); + } + + private static Func CreateMethodCallerDelegate(string methodName) + { + var parameter = Expression.Parameter(typeof(T), "instance"); + var argument = Expression.Parameter(typeof(TParam), "argument"); + var methodCall = Expression.Call(parameter, typeof(T).GetMethod(methodName), argument); + var lambda = Expression.Lambda>(methodCall, parameter, argument); + return lambda.Compile(); + } + + /// + /// 动态调用多参无返回值的方法 + /// + /// + /// + /// + /// + public static Action MethodCaller(string methodName, params Type[] parameterTypes) + { + string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCaller"; + return (Action)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(methodName, parameterTypes)); + } + + private static Action CreateMethodCallerDelegate(string methodName, Type[] parameterTypes) + { + var parameter = Expression.Parameter(typeof(T), "instance"); + var arguments = parameterTypes.Select((type, index) => + Expression.Parameter(typeof(object), $"arg{index}") + ).ToList(); + + var convertedArguments = arguments.Select((arg, index) => + Expression.Convert(arg, parameterTypes[index]) + ).ToList(); + + var methodInfo = typeof(T).GetMethod(methodName, parameterTypes); + + if (methodInfo == null) + { + throw new ArgumentException($"Method '{methodName}' not found in type '{typeof(T).FullName}' with given parameter types."); + } + + var methodCall = Expression.Call(parameter, methodInfo, convertedArguments); + var lambda = Expression.Lambda>(methodCall, new[] { parameter }.Concat(arguments)); + return lambda.Compile(); + } + + + + /// + /// 动态调用多参有返回值的方法 + /// + /// + /// + /// + /// + /// + public static Func MethodCallerHaveResult(string methodName, Type[] parameterTypes) + { + string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCallerHaveResult"; + return (Func)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(methodName, parameterTypes)); + } + + private static Func CreateMethodCallerDelegate(string methodName, Type[] parameterTypes) + { + var instanceParam = Expression.Parameter(typeof(T), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + + var convertedArgs = new Expression[parameterTypes.Length]; + for (int i = 0; i < parameterTypes.Length; i++) + { + var index = Expression.Constant(i); + var argType = parameterTypes[i]; + var arrayIndex = Expression.ArrayIndex(argsParam, index); + var convertedArg = Expression.Convert(arrayIndex, argType); + convertedArgs[i] = convertedArg; + } + + var methodInfo = typeof(T).GetMethod(methodName, parameterTypes); + + if (methodInfo == null) + { + throw new ArgumentException($"Method '{methodName}' not found in type '{typeof(T).FullName}' with given parameter types."); + } + + var methodCall = Expression.Call(instanceParam, methodInfo, convertedArgs); + var lambda = Expression.Lambda>(methodCall, instanceParam, argsParam); + return lambda.Compile(); + } + + + + + + + + + */ + + #endregion + #region 暂时不删(已注释) + /* /// + /// 表达式树构建多个参数,有返回值的方法 + /// + public static Delegate MethodCallerHaveResult(Type type, string methodName, Type[] parameterTypes) + { + string cacheKey = $"{type.FullName}.{methodName}.MethodCallerHaveResult"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodName, parameterTypes)); + } + + private static Delegate CreateMethodCallerDelegateHaveResult(Type type, string methodName, Type[] parameterTypes) + { + var instanceParam = Expression.Parameter(typeof(object), "instance"); + var argsParam = Expression.Parameter(typeof(object[]), "args"); + var convertedArgs = parameterTypes.Select((paramType, index) => + Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType) + ).ToArray(); + var methodCall = Expression.Call(Expression.Convert(instanceParam, type), type.GetMethod(methodName, parameterTypes), convertedArgs); + var lambda = Expression.Lambda(Expression.Convert(methodCall, typeof(object)), instanceParam, argsParam); + return lambda.Compile(); + }*/ + + + + /*/// + /// 表达式反射 构建 无返回值、无参数 的委托 + /// + /// + /// + /// + /// + public static Delegate MethodCaller(Type type, string methodName, Type[] parameterTypes) + { + string cacheKey = $"{type.FullName}.{methodName}.{string.Join(",", parameterTypes.Select(t => t.FullName))}.MethodCaller"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodName, parameterTypes)); + } + + /// + /// 表达式反射 构建 无返回值、无参数 的委托 + /// + /// + /// + /// + /// + private static Delegate CreateMethodCallerDelegate(Type type, string methodName, Type[] parameterTypes) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var arguments = parameterTypes.Select((paramType, index) => Expression.Parameter(paramType, $"param{index}")).ToArray(); + var methodCall = Expression.Call(Expression.Convert(parameter, type), type.GetMethod(methodName, parameterTypes), arguments); + + var delegateType = Expression.GetActionType(new[] { typeof(object) }.Concat(parameterTypes).ToArray()); + var lambda = Expression.Lambda(delegateType, methodCall, new[] { parameter }.Concat(arguments).ToArray()); + return lambda.Compile(); + } +*/ + /*public static Delegate MethodCallerHaveResult(Type type, string methodName, Type returnType, Type[] parameterTypes) + { + string cacheKey = $"{type.FullName}.{methodName}.{string.Join(",", parameterTypes.Select(t => t.FullName))}.MethodCallerHaveResult"; + return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodName, returnType, parameterTypes)); + } + + private static Delegate CreateMethodCallerDelegateHaveResult(Type type, string methodName, Type returnType, Type[] parameterTypes) + { + var parameter = Expression.Parameter(typeof(object), "instance"); + var arguments = parameterTypes.Select((paramType, index) => Expression.Parameter(paramType, $"param{index}")).ToArray(); + var methodCall = Expression.Call(Expression.Convert(parameter, type), type.GetMethod(methodName, parameterTypes), arguments); + + var delegateType = Expression.GetFuncType(new[] { typeof(object) }.Concat(parameterTypes).Concat(new[] { typeof(object) }).ToArray()); + var lambda = Expression.Lambda(delegateType, Expression.Convert(methodCall, typeof(object)), new[] { parameter }.Concat(arguments).ToArray()); + return lambda.Compile(); + } + +*/ + + #endregion + } +} diff --git a/NodeFlow/Tool/TcsSignal.cs b/NodeFlow/Tool/TcsSignal.cs new file mode 100644 index 0000000..f03147f --- /dev/null +++ b/NodeFlow/Tool/TcsSignal.cs @@ -0,0 +1,81 @@ +using System.Collections.Concurrent; +using Serein.NodeFlow; + +namespace Serein.NodeFlow.Tool +{ + public class TcsSignalException : Exception + { + public FfState FfState { get; set; } + public TcsSignalException(string? message) : base(message) + { + FfState = FfState.Cancel; + } + } + + public class TcsSignal where TSignal : struct, Enum + { + + public ConcurrentDictionary>> TcsEvent { get; } = new(); + + // public object tcsObj = new object(); + + public bool TriggerSignal(TSignal signal, T state) + { + if (TcsEvent.TryRemove(signal, out var waitTcss)) + { + while (waitTcss.Count > 0) + { + + waitTcss.Pop().SetResult(state); + + } + return true; + } + return false; + + + } + + public TaskCompletionSource CreateTcs(TSignal signal) + { + + var tcs = new TaskCompletionSource(); + TcsEvent.GetOrAdd(signal, _ => new Stack>()).Push(tcs); + return tcs; + + + + } + //public TaskCompletionSource GetOrCreateTcs(TSignal signal) + //{ + // lock (tcsObj) + // { + // var tcs = TcsEvent.GetOrAdd(signal, _ => new TaskCompletionSource()); + // if (tcs.Task.IsCompleted) + // { + // TcsEvent.TryRemove(signal, out _); + // tcs = new TaskCompletionSource(); + // TcsEvent[signal] = tcs; + // } + // return tcs; + // } + //} + + public void CancelTask() + { + lock (TcsEvent) + { + + foreach (var tcss in TcsEvent.Values) + { + while (tcss.Count > 0) + { + tcss.Pop().SetException(new TcsSignalException("Task Cancel")); + } + } + TcsEvent.Clear(); + } + } + + } +} diff --git a/SereinFlow.sln b/SereinFlow.sln index 561d053..4aec526 100644 --- a/SereinFlow.sln +++ b/SereinFlow.sln @@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Module.WAT", "SereinWAT\Serein.Module.WAT.csproj", "{C2F68A15-5D07-4418-87C0-E7402DD9F83A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.NodeFlow", "NodeFlow\Serein.NodeFlow.csproj", "{5C8444EB-A8D6-48BE-915D-82A585B93F2E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Release|Any CPU.ActiveCfg = Release|Any CPU {C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Release|Any CPU.Build.0 = Release|Any CPU + {5C8444EB-A8D6-48BE-915D-82A585B93F2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C8444EB-A8D6-48BE-915D-82A585B93F2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C8444EB-A8D6-48BE-915D-82A585B93F2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C8444EB-A8D6-48BE-915D-82A585B93F2E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SereinWAT/Serein.Module.WAT.csproj b/SereinWAT/Serein.Module.WAT.csproj index 8e4490e..d7c9966 100644 --- a/SereinWAT/Serein.Module.WAT.csproj +++ b/SereinWAT/Serein.Module.WAT.csproj @@ -13,6 +13,7 @@ + diff --git a/SereinWAT/SereinWAT.cs b/SereinWAT/SereinWAT.cs index f9c815e..f005f76 100644 --- a/SereinWAT/SereinWAT.cs +++ b/SereinWAT/SereinWAT.cs @@ -3,9 +3,7 @@ using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Edge; using OpenQA.Selenium.Firefox; using OpenQA.Selenium.IE; -using Serein.Flow; -using Serein.Flow.Tool; -using System.Diagnostics.CodeAnalysis; +using Serein.NodeFlow; namespace Serein.Module { diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index cce9f84..142fa5b 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -1,11 +1,5 @@ -using Serein; -using Serein.Flow.SerinExpression; -using Serein.WorkBench.Themes; -using Newtonsoft.Json; -using SqlSugar; -using SqlSugar.Extensions; +using Newtonsoft.Json; using System.Diagnostics; -using System.Linq.Expressions; using System.Windows; namespace Serein.WorkBench diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index fff7623..c8e323b 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -1,13 +1,12 @@ -using Serein.WorkBench.Node; +using Microsoft.Win32; +using Newtonsoft.Json.Linq; +using Serein.Library.IOC; +using Serein.NodeFlow; +using Serein.NodeFlow.Model; +using Serein.NodeFlow.Tool; using Serein.WorkBench.Node.View; using Serein.WorkBench.Themes; using Serein.WorkBench.tool; -using Microsoft.Win32; -using Newtonsoft.Json.Linq; -using Serein; -using Serein.Flow; -using Serein.Flow.NodeModel; -using Serein.Flow.Tool; using System.Collections.Concurrent; using System.Diagnostics; using System.IO; @@ -18,11 +17,6 @@ using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; -using static Serein.WorkBench.Connection; -using DynamicDemo.Node; -using Npgsql.Logging; -using System.Threading.Tasks.Dataflow; -using Serein.Library.IOC; namespace Serein.WorkBench { @@ -121,7 +115,7 @@ namespace Serein.WorkBench /// /// 节点的命名空间 /// - public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.Flow)}.{nameof(Serein.Flow.NodeModel)}"; + public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}"; /// /// 一种轻量的IOC容器 /// diff --git a/WorkBench/Node/View/ActionNodeControl.xaml.cs b/WorkBench/Node/View/ActionNodeControl.xaml.cs index a95f3c0..62c7b1b 100644 --- a/WorkBench/Node/View/ActionNodeControl.xaml.cs +++ b/WorkBench/Node/View/ActionNodeControl.xaml.cs @@ -1,4 +1,4 @@ -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using Serein.WorkBench.Node.ViewModel; namespace Serein.WorkBench.Node.View diff --git a/WorkBench/Node/View/ActionRegionControl.xaml.cs b/WorkBench/Node/View/ActionRegionControl.xaml.cs index 79b6805..d6528d1 100644 --- a/WorkBench/Node/View/ActionRegionControl.xaml.cs +++ b/WorkBench/Node/View/ActionRegionControl.xaml.cs @@ -1,9 +1,8 @@ -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; -using static Serein.WorkBench.MainWindow; namespace Serein.WorkBench.Node.View { diff --git a/WorkBench/Node/View/ConditionNodeControl.xaml.cs b/WorkBench/Node/View/ConditionNodeControl.xaml.cs index b1cf2ca..c7e6a4c 100644 --- a/WorkBench/Node/View/ConditionNodeControl.xaml.cs +++ b/WorkBench/Node/View/ConditionNodeControl.xaml.cs @@ -1,11 +1,5 @@ -using Serein.Flow; -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using Serein.WorkBench.Node.ViewModel; -using static Serein.WorkBench.MainWindow; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows; -using static Dm.net.buffer.ByteArrayBuffer; namespace Serein.WorkBench.Node.View { diff --git a/WorkBench/Node/View/ConditionRegionControl.xaml.cs b/WorkBench/Node/View/ConditionRegionControl.xaml.cs index f810460..351c4fd 100644 --- a/WorkBench/Node/View/ConditionRegionControl.xaml.cs +++ b/WorkBench/Node/View/ConditionRegionControl.xaml.cs @@ -1,10 +1,8 @@ -using Serein.Flow; -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; -using static Serein.WorkBench.MainWindow; namespace Serein.WorkBench.Node.View { diff --git a/WorkBench/Node/View/DllControlControl.xaml.cs b/WorkBench/Node/View/DllControlControl.xaml.cs index 6b4b290..6007da7 100644 --- a/WorkBench/Node/View/DllControlControl.xaml.cs +++ b/WorkBench/Node/View/DllControlControl.xaml.cs @@ -1,8 +1,7 @@ -using Serein.Flow; +using Serein.NodeFlow; using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using static Serein.WorkBench.MainWindow; namespace Serein.WorkBench.Node.View { diff --git a/WorkBench/Node/View/ExpOpNodeControl.xaml.cs b/WorkBench/Node/View/ExpOpNodeControl.xaml.cs index 3ec624d..4f02566 100644 --- a/WorkBench/Node/View/ExpOpNodeControl.xaml.cs +++ b/WorkBench/Node/View/ExpOpNodeControl.xaml.cs @@ -1,19 +1,5 @@ -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using Serein.WorkBench.Node.ViewModel; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; namespace Serein.WorkBench.Node.View { diff --git a/WorkBench/Node/View/FlipflopNodeControl.xaml.cs b/WorkBench/Node/View/FlipflopNodeControl.xaml.cs index 2d8c25f..2252ee9 100644 --- a/WorkBench/Node/View/FlipflopNodeControl.xaml.cs +++ b/WorkBench/Node/View/FlipflopNodeControl.xaml.cs @@ -1,8 +1,5 @@ -using Serein.Flow; -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using Serein.WorkBench.Node.ViewModel; -using System.Windows; -using System.Windows.Controls; namespace Serein.WorkBench.Node.View { diff --git a/WorkBench/Node/View/NodeControlBase.cs b/WorkBench/Node/View/NodeControlBase.cs index b433378..f1fe1dd 100644 --- a/WorkBench/Node/View/NodeControlBase.cs +++ b/WorkBench/Node/View/NodeControlBase.cs @@ -1,6 +1,5 @@ -using Serein.Flow; -using Serein.Flow.NodeModel; -using Serein.WorkBench.Themes; +using Serein.NodeFlow; +using Serein.NodeFlow.Model; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; diff --git a/WorkBench/Node/ViewModel/ActionNodeControlViewModel.cs b/WorkBench/Node/ViewModel/ActionNodeControlViewModel.cs index bdc1005..68605a5 100644 --- a/WorkBench/Node/ViewModel/ActionNodeControlViewModel.cs +++ b/WorkBench/Node/ViewModel/ActionNodeControlViewModel.cs @@ -1,8 +1,5 @@ -using Serein.Flow; -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using Serein.WorkBench.Node.View; -using System.ComponentModel; -using System.Runtime.CompilerServices; namespace Serein.WorkBench.Node.ViewModel { diff --git a/WorkBench/Node/ViewModel/ConditionNodeControlViewModel.cs b/WorkBench/Node/ViewModel/ConditionNodeControlViewModel.cs index 3656547..e35240e 100644 --- a/WorkBench/Node/ViewModel/ConditionNodeControlViewModel.cs +++ b/WorkBench/Node/ViewModel/ConditionNodeControlViewModel.cs @@ -1,9 +1,5 @@ -using Serein.Flow; -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using Serein.WorkBench.Node.View; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using static Dm.net.buffer.ByteArrayBuffer; namespace Serein.WorkBench.Node.ViewModel { diff --git a/WorkBench/Node/ViewModel/ExpOpNodeViewModel.cs b/WorkBench/Node/ViewModel/ExpOpNodeViewModel.cs index 53004be..2c51902 100644 --- a/WorkBench/Node/ViewModel/ExpOpNodeViewModel.cs +++ b/WorkBench/Node/ViewModel/ExpOpNodeViewModel.cs @@ -1,10 +1,5 @@ -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using Serein.WorkBench.Node.View; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Serein.WorkBench.Node.ViewModel { diff --git a/WorkBench/Node/ViewModel/FlipflopNodeControlViewModel.cs b/WorkBench/Node/ViewModel/FlipflopNodeControlViewModel.cs index fe03fff..8cea0bf 100644 --- a/WorkBench/Node/ViewModel/FlipflopNodeControlViewModel.cs +++ b/WorkBench/Node/ViewModel/FlipflopNodeControlViewModel.cs @@ -1,4 +1,4 @@ -using Serein.Flow.NodeModel; +using Serein.NodeFlow.Model; using Serein.WorkBench.Node.View; namespace Serein.WorkBench.Node.ViewModel diff --git a/WorkBench/Serein.WorkBench.csproj b/WorkBench/Serein.WorkBench.csproj index b222834..356d514 100644 --- a/WorkBench/Serein.WorkBench.csproj +++ b/WorkBench/Serein.WorkBench.csproj @@ -37,6 +37,7 @@ + diff --git a/WorkBench/Themes/MethodDetailsControl.xaml.cs b/WorkBench/Themes/MethodDetailsControl.xaml.cs index 4f1962c..4187c45 100644 --- a/WorkBench/Themes/MethodDetailsControl.xaml.cs +++ b/WorkBench/Themes/MethodDetailsControl.xaml.cs @@ -1,20 +1,9 @@ -using Serein.Flow; -using System; +using Serein.NodeFlow; using System.Collections; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; namespace Serein.WorkBench.Themes {