diff --git a/Library.Core/NodeFlow/DynamicContext.cs b/Library.Core/NodeFlow/DynamicContext.cs index ed74041..7c73a31 100644 --- a/Library.Core/NodeFlow/DynamicContext.cs +++ b/Library.Core/NodeFlow/DynamicContext.cs @@ -9,22 +9,22 @@ namespace Serein.Library.Core.NodeFlow /// public class DynamicContext: IDynamicContext { - public DynamicContext(ISereinIOC sereinIoc, IFlowEnvironment flowEnvironment) + public DynamicContext(/*ISereinIOC sereinIoc, */IFlowEnvironment flowEnvironment) { - SereinIoc = sereinIoc; - FlowEnvironment = flowEnvironment; + //SereinIoc = sereinIoc; + Env = flowEnvironment; } public NodeRunCts NodeRunCts { get; set; } - public ISereinIOC SereinIoc { get; } - public IFlowEnvironment FlowEnvironment { get; } + //public ISereinIOC SereinIoc { get; } + public IFlowEnvironment Env { get; } public Task CreateTimingTask(Action action, int time = 100, int count = -1) { if (NodeRunCts == null) { - NodeRunCts = SereinIoc.GetOrRegisterInstantiate(); + NodeRunCts = Env.IOC.GetOrRegisterInstantiate(); } // 使用局部变量,避免捕获外部的 `action` Action localAction = action; diff --git a/Library.Framework/NodeFlow/DynamicContext.cs b/Library.Framework/NodeFlow/DynamicContext.cs index ff835b8..a012f9d 100644 --- a/Library.Framework/NodeFlow/DynamicContext.cs +++ b/Library.Framework/NodeFlow/DynamicContext.cs @@ -13,21 +13,21 @@ namespace Serein.Library.Framework.NodeFlow /// public class DynamicContext : IDynamicContext { - public DynamicContext(ISereinIOC sereinIoc, IFlowEnvironment flowEnvironment) + public DynamicContext(/*ISereinIOC sereinIoc,*/ IFlowEnvironment flowEnvironment) { - SereinIoc = sereinIoc; - FlowEnvironment = flowEnvironment; + // SereinIoc = sereinIoc; + Env = flowEnvironment; } public NodeRunCts NodeRunCts { get; set; } - public ISereinIOC SereinIoc { get; } - public IFlowEnvironment FlowEnvironment { get; } + // public ISereinIOC SereinIoc { get; } + public IFlowEnvironment Env { get; } public Task CreateTimingTask(Action action, int time = 100, int count = -1) { if(NodeRunCts == null) { - NodeRunCts = SereinIoc.GetOrRegisterInstantiate(); + NodeRunCts = Env.IOC.GetOrRegisterInstantiate(); } // 使用局部变量,避免捕获外部的 `action` Action localAction = action; diff --git a/Library/Api/IDynamicContext.cs b/Library/Api/IDynamicContext.cs index 10e1158..60a1c8b 100644 --- a/Library/Api/IDynamicContext.cs +++ b/Library/Api/IDynamicContext.cs @@ -4,10 +4,23 @@ using System.Threading.Tasks; namespace Serein.Library.Api { + /// + /// 流程上下文 + /// public interface IDynamicContext { - IFlowEnvironment FlowEnvironment { get; } - ISereinIOC SereinIoc { get; } - Task CreateTimingTask(Action action, int time = 100, int count = -1); + /// + /// 运行环境 + /// + IFlowEnvironment Env { get; } + + /// + /// 定时循环触发 + /// + /// + /// + /// + /// + Task CreateTimingTask(Action callback, int time = 100, int count = -1); } } diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index 48d5b47..f2d4214 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -47,6 +47,26 @@ namespace Serein.Library.Api /// /// public delegate void StartNodeChangeHandler(StartNodeChangeEventArgs eventArgs); + + /// + /// 被监视的对象改变事件 + /// + /// + public delegate void MonitorObjectChangeHandler(MonitorObjectEventArgs eventArgs); + + /// + /// 节点中断状态改变事件(开启了中断/取消了中断) + /// + /// + public delegate void NodeInterruptStateChangeHandler(NodeInterruptStateChangeEventArgs eventArgs); + + /// + /// 节点触发中断事件 + /// + /// + public delegate void ExpInterruptTriggerHandler(InterruptTriggerEventArgs eventArgs); + + #endregion #region 环境事件签名 @@ -210,45 +230,35 @@ namespace Serein.Library.Api /// public string NewNodeGuid { get; private set; } } - #endregion - /// - /// 被监视的对象改变事件 - /// - /// - public delegate void MonitorObjectChangeHandler(MonitorObjectEventArgs eventArgs); - /// - /// 节点中断状态改变事件(开启了中断/取消了中断) - /// - /// - public delegate void NodeInterruptStateChangeHandler(NodeInterruptStateChangeEventArgs eventArgs); - /// - /// 节点触发中断事件 - /// - /// - public delegate void ExpInterruptTriggerHandler(InterruptTriggerEventArgs eventArgs); /// /// 监视的节点数据发生变化 /// public class MonitorObjectEventArgs : FlowEventArgs { - public MonitorObjectEventArgs(string nodeGuid,object newData) + public enum ObjSourceType + { + NodeFlowData, + IOCObj, + } + public MonitorObjectEventArgs(string nodeGuid, object monitorData, ObjSourceType objSourceType) { NodeGuid = nodeGuid; - NewData = newData; + NewData = monitorData; + ObjSource = objSourceType; } /// /// 中断的节点Guid /// public string NodeGuid { get; protected set; } - + public ObjSourceType ObjSource { get; protected set; } /// /// 新的数据 /// - public object NewData { get; protected set; } + public object NewData { get; protected set; } } /// @@ -256,7 +266,7 @@ namespace Serein.Library.Api /// public class NodeInterruptStateChangeEventArgs : FlowEventArgs { - public NodeInterruptStateChangeEventArgs(string nodeGuid,InterruptClass @class) + public NodeInterruptStateChangeEventArgs(string nodeGuid, InterruptClass @class) { NodeGuid = nodeGuid; Class = @class; @@ -283,6 +293,10 @@ namespace Serein.Library.Api /// 表达式中断 /// Exp, + /// + /// 对象监视中断 + /// + Obj, } public InterruptTriggerEventArgs(string nodeGuid, string expression, InterruptTriggerType type) @@ -299,26 +313,62 @@ namespace Serein.Library.Api public string Expression { get; protected set; } public InterruptTriggerType Type { get; protected set; } } + #endregion + + + /// + /// IOC容器发生变化 + /// + public delegate void IOCMembersChangedHandler(); + + + /// + /// 流程事件签名基类 + /// + public class IOCMembersChangedEventArgs : FlowEventArgs + { + public enum EventType + { + /// + /// 登记了类型 + /// + Registered, + /// + /// 构建了类型 + /// + Completeuild, + } + public IOCMembersChangedEventArgs(Type[] types, object[] dependencies, object[] unfinishedDependencies) + { + this.Types = types; + this.Dependencies = dependencies; + this.UnfinishedDependencies = unfinishedDependencies; + } + public Type[] Types { get; protected set; } + public object[] Dependencies { get; private set; } + public object[] UnfinishedDependencies { get; private set; } + + } public interface IFlowEnvironment { + #region 属性 + /// + /// IOC容器 + /// + ISereinIOC IOC { get; } + /// /// 环境名称 /// - string EnvName {get;} + string EnvName { get; } /// /// 是否全局中断 /// - bool IsGlobalInterrupt { get; } - /// - /// 设置中断时的中断级别 - /// - //InterruptClass EnvInterruptClass { get; set; } + bool IsGlobalInterrupt { get; } + #endregion - /// - /// 调试管理 - /// - //ChannelFlowInterrupt ChannelFlowInterrupt { get; set; } + #region 事件 /// /// 加载Dll @@ -371,6 +421,10 @@ namespace Serein.Library.Api event ExpInterruptTriggerHandler OnInterruptTrigger; + #endregion + + #region Workbench + /// /// 保存当前项目 /// @@ -397,13 +451,20 @@ namespace Serein.Library.Api /// /// /// - bool TryGetMethodDetails(string methodName,out MethodDetails md); + bool TryGetMethodDetails(string methodName, out MethodDetails md); /// /// 开始运行 /// Task StartAsync(); + /// + /// 从选定的节点开始运行 + /// + /// + /// + Task StartFlowInSelectNodeAsync(string startNodeGuid); + /// /// 结束运行 /// @@ -442,7 +503,6 @@ namespace Serein.Library.Api /// 待移除的节点Guid void RemoteNode(string nodeGuid); - /// /// 设置节点中断级别 /// @@ -452,38 +512,42 @@ namespace Serein.Library.Api bool SetNodeInterrupt(string nodeGuid, InterruptClass interruptClass); /// - /// 添加中断表达式 + /// 添加作用于某个对象的中断表达式 /// /// /// /// - bool AddInterruptExpression(string nodeGuid,string expression); + bool AddInterruptExpression(object obj, string expression); + /// + /// 添加作用于指定节点的中断表达式 + /// + /// + /// + /// + // bool AddInterruptExpression(string nodeGuid,string expression); + + // + // 设置节点数据监视状态 + // + // 需要监视的节点Guid + // 是否监视 + // void SetNodeFLowDataMonitorState(string nodeGuid, bool isMonitor); /// - /// 设置节点数据监视状态 + /// 监视指定对象 /// - /// 需要监视的节点Guid - /// 是否监视 - void SetNodeFLowDataMonitorState(string nodeGuid, bool isMonitor); - - - - + /// 需要监视的对象 + /// 是否启用监视 + void SetMonitorObjState(object obj, bool isMonitor); /// - /// 流程启动器调用,节点数据更新通知 + /// 检查一个对象是否处于监听状态,如果是,则传出与该对象相关的表达式(用于中断),如果不是,则返回false。 /// - /// 更新了数据的节点Guid - /// 更新的数据 - void FlowDataNotification(string nodeGuid, object flowData); + /// 判断的对象 + /// 表达式 + /// + bool CheckObjMonitorState(object obj, out List exps); - /// - /// 流程启动器调用,节点触发了中断 - /// - /// 被中断的节点Guid - /// 被触发的表达式 - /// 中断类型。0主动监视,1表达式 - void TriggerInterrupt(string nodeGuid,string expression, InterruptTriggerEventArgs.InterruptTriggerType type); /// /// 全局中断 @@ -494,6 +558,26 @@ namespace Serein.Library.Api Task GetOrCreateGlobalInterruptAsync(); + #endregion + #region Start + + /// + /// 流程启动器调用,监视数据更新通知 + /// + /// 更新了数据的节点Guid + /// 更新的数据 + void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType); + + /// + /// 流程启动器调用,节点触发了中断 + /// + /// 被中断的节点Guid + /// 被触发的表达式 + /// 中断类型。0主动监视,1表达式 + void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type); + + + #endregion } } diff --git a/Library/Api/ISereinIoc.cs b/Library/Api/ISereinIoc.cs index f58169c..0953496 100644 --- a/Library/Api/ISereinIoc.cs +++ b/Library/Api/ISereinIoc.cs @@ -15,31 +15,63 @@ namespace Serein.Library.Api /// 注册实例 /// ISereinIOC Register(Type type, params object[] parameters); + /// + /// 注册实例 + /// + /// + /// + /// ISereinIOC Register(params object[] parameters); + /// + /// 注册接口的实例 + /// + /// + /// + /// + /// ISereinIOC Register(params object[] parameters) where TImplementation : TService; /// - /// 获取或创建并注入目标类型 + /// 获取或创建并注入目标类型,会记录到IOC容器中。 /// T GetOrRegisterInstantiate(); /// - /// 获取或创建并注入目标类型 + /// 获取或创建并注入目标类型,会记录到IOC容器中。 /// object GetOrRegisterInstantiate(Type type); + /// + /// 获取类型的实例 + /// + /// + /// object Get(Type type); - - T Get(string name); - object Get(string name); - void CustomRegisterInstance(string name, object instance, bool needInjectProperty = true); - - ISereinIOC Run(string name, Action action); - + /// + /// 获取指定名称的实例 + /// + /// + /// + /// + T Get(string key); /// - /// 创建目标类型的对象, 并注入依赖项 + /// 通过名称注册实例 + /// + /// 注入名称 + /// 实例对象 + /// 是否需要注入依赖项 + void CustomRegisterInstance(string key, object instance, bool needInjectProperty = true); + + /// + /// 用于临时实例的创建,不注册到IOC容器中,依赖项注入失败时也不记录。 /// object Instantiate(Type type, params object[] parameters); + + /// + /// 实例化注册的类型,并注入依赖项 + /// + /// ISereinIOC Build(); + ISereinIOC Run(Action action); ISereinIOC Run(Action action); ISereinIOC Run(Action action); diff --git a/Library/Entity/NodeDebugSetting.cs b/Library/Entity/NodeDebugSetting.cs index de5bed9..0cd2163 100644 --- a/Library/Entity/NodeDebugSetting.cs +++ b/Library/Entity/NodeDebugSetting.cs @@ -16,7 +16,7 @@ namespace Serein.Library.Entity /// /// 是否监视数据改变 /// - public bool IsMonitorFlowData { get; set; } = false; + // public bool IsMonitorFlowData { get; set; } = false; /// /// 中断级别,暂时停止继续执行后继分支。 @@ -26,8 +26,7 @@ namespace Serein.Library.Entity /// /// 中断表达式 /// - public List InterruptExpressions { get; } = new List(); - + // public List InterruptExpressions { get; } = new List(); /// /// 取消中断的回调函数 diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs index 21c1479..da5f435 100644 --- a/Library/Utils/SereinIoc.cs +++ b/Library/Utils/SereinIoc.cs @@ -14,7 +14,7 @@ namespace Serein.Library.Utils /// /// IOC管理容器 /// - public class SereinIOC : ISereinIOC + public class SereinIOC/* : ISereinIOC*/ { /// /// 类型集合,暂放待实例化的类型,完成实例化之后移除 @@ -22,7 +22,7 @@ namespace Serein.Library.Utils private readonly ConcurrentDictionary _typeMappings; /// - /// 实例集合(包含已完成注入、未完成注入的对象实例,计划在未来的版本中区分:) + /// 已完成注入的实例集合 /// private readonly ConcurrentDictionary _dependencies; @@ -34,10 +34,6 @@ namespace Serein.Library.Utils private readonly ConcurrentDictionary> _unfinishedDependencies; - /// - /// 待实例化的类型 - /// - // private readonly List _waitingForInstantiation; public SereinIOC() { @@ -49,13 +45,13 @@ namespace Serein.Library.Utils public void InitRegister() { - _dependencies[typeof(ISereinIOC).FullName] = this; + //_dependencies[typeof(ISereinIOC).FullName] = this; Register(); - /*foreach (var type in _typeMappings.Values) - { - Register(type); - } - Build();*/ + //foreach (var type in _typeMappings.Values) + //{ + // Register(type); + //} + //Build(); } #region 类型的注册 @@ -65,21 +61,19 @@ namespace Serein.Library.Utils /// /// 目标类型 /// 参数 - public ISereinIOC Register(Type type, params object[] parameters) + public bool Register(Type type, params object[] parameters) { - RegisterType(type?.FullName, type); - return this; + return RegisterType(type?.FullName, type); } /// /// 注册类型 /// /// 目标类型 /// 参数 - public ISereinIOC Register(params object[] parameters) + public bool Register(params object[] parameters) { var type = typeof(T); - RegisterType(type.FullName, type); - return this; + return RegisterType(type.FullName, type); } /// @@ -87,14 +81,20 @@ namespace Serein.Library.Utils /// /// 目标类型 /// 参数 - public ISereinIOC Register(params object[] parameters) + public bool Register(params object[] parameters) where TImplementation : TService { - RegisterType(typeof(TService).FullName, typeof(TImplementation)); - return this; + return RegisterType(typeof(TService).FullName, typeof(TImplementation)); } #endregion + /// + /// 尝试从容器中获取对象,如果不存在目标类型的对象,则将类型信息登记到容器,并实例化注入依赖项。如果依然无法注册,则返回null。 + /// + public T GetOrRegisterInstantiate() + { + return (T)GetOrRegisterInstantiate(typeof(T)); + } /// /// 尝试从容器中获取对象,如果不存在目标类型的对象,则将类型信息登记到容器,并实例化注入依赖项。如果依然无法注册,则返回null。 @@ -134,16 +134,25 @@ namespace Serein.Library.Utils } /// - /// 尝试从容器中获取对象,如果不存在目标类型的对象,则将类型信息登记到容器,并实例化注入依赖项。如果依然无法注册,则返回null。 + /// 用于临时实例的创建,不登记到IOC容器中,依赖项注入失败时也不记录。 /// - public T GetOrRegisterInstantiate() + /// + /// + /// + public object Instantiate(Type controllerType, params object[] parameters) { - return (T)GetOrRegisterInstantiate(typeof(T)); + var instance = Activator.CreateInstance(controllerType, parameters); // CreateInstance(controllerType, parameters); // 创建目标类型的实例 + if (instance != null) + { + InjectDependencies(instance, false); // 完成创建后注入实例需要的依赖项 + } + return instance; } + #region 通过名称记录或获取一个实例 - public void CustomRegisterInstance(string name,object instance, bool needInjectProperty = true) + public void CustomRegisterInstance(string name, object instance, bool needInjectProperty = true) { // 不存在时才允许创建 if (!_dependencies.ContainsKey(name)) @@ -151,7 +160,7 @@ namespace Serein.Library.Utils _dependencies.TryAdd(name, instance); } - if (needInjectProperty) + if (needInjectProperty) { InjectDependencies(instance); // 注入实例需要的依赖项 } @@ -180,7 +189,7 @@ namespace Serein.Library.Utils { return (T)Get(name); } - public object Get(string name) + private object Get(string name) { object value; if (!_dependencies.TryGetValue(name, out value)) @@ -190,23 +199,10 @@ namespace Serein.Library.Utils return value; } + #endregion + - /// - /// 根据类型生成对应的实例,并注入其中的依赖项(类型信息不登记到IOC容器中),类型创建后自动注入其它需要此类型的对象 - /// - /// - /// - /// - public object Instantiate(Type controllerType, params object[] parameters) - { - var instance = CreateInstance(controllerType, parameters); // 创建目标类型的实例 - if(instance != null) - { - InjectDependencies(instance); // 完成创建后注入实例需要的依赖项 - } - return instance; - } #region 容器管理(清空,绑定) @@ -214,7 +210,7 @@ namespace Serein.Library.Utils /// 清空容器对象 /// /// - public ISereinIOC Reset() + public bool Reset() { // 检查是否存在非托管资源 foreach (var instancei in _dependencies.Values) @@ -228,14 +224,14 @@ namespace Serein.Library.Utils _typeMappings?.Clear(); _dependencies?.Clear(); // _waitingForInstantiation?.Clear(); - return this; + return true; } /// /// 实例化所有已注册的类型,并尝试绑定 /// /// - public ISereinIOC Build() + public bool Build() { InitRegister(); // 遍历已注册类型 @@ -265,7 +261,7 @@ namespace Serein.Library.Utils // TryInstantiateWaitingDependencies(); - return this; + return true; } #endregion @@ -277,11 +273,16 @@ namespace Serein.Library.Utils /// /// /// - private void RegisterType(string typeFull, Type type) + private bool RegisterType(string typeFull, Type type) { if (!_typeMappings.ContainsKey(typeFull)) { _typeMappings[typeFull] = type; + return true; + } + else + { + return false; } } @@ -310,8 +311,9 @@ namespace Serein.Library.Utils /// /// 注入目标实例的依赖项 /// - /// - private bool InjectDependencies(object instance) + /// 实例 + /// 未完成依赖项注入时是否记录 + private bool InjectDependencies(object instance,bool isRecord = true) { var properties = instance.GetType() .GetProperties(BindingFlags.Instance | BindingFlags.Public).ToArray() @@ -327,7 +329,7 @@ namespace Serein.Library.Utils { property.SetValue(instance, dependencyInstance); // 尝试写入到目标实例的属性中 } - else + else if(isRecord) { // 存在依赖项,但目标类型的实例暂未加载,需要等待需要实例完成注册 var unfinishedDependenciesList = _unfinishedDependencies.GetOrAdd(propertyType.FullName, _ = new List<(object, PropertyInfo)>()); @@ -366,66 +368,62 @@ namespace Serein.Library.Utils #endregion #region run() - public ISereinIOC Run(string name, Action action) - { - var obj = Get(name); - if (obj != null) - { - if(obj is T service) - { - try - { - action(service); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - } - } - return this; - } + //public bool Run(string name, Action action) + //{ + // var obj = Get(name); + // if (obj != null) + // { + // if(obj is T service) + // { + // try + // { + // action(service); + // } + // catch (Exception ex) + // { + // Console.WriteLine(ex.Message); + // } + // } + // } + // return this; + //} - public ISereinIOC Run(Action action) + public void Run(Action action) { var service = GetOrRegisterInstantiate(); if (service != null) { action(service); } - return this; } - public ISereinIOC Run(Action action) + public void Run(Action action) { var service1 = GetOrRegisterInstantiate(); var service2 = GetOrRegisterInstantiate(); action(service1, service2); - return this; } - public ISereinIOC Run(Action action) + public void Run(Action action) { var service1 = GetOrRegisterInstantiate(); var service2 = GetOrRegisterInstantiate(); var service3 = GetOrRegisterInstantiate(); action(service1, service2, service3); - return this; } - public ISereinIOC Run(Action action) + public void Run(Action action) { var service1 = GetOrRegisterInstantiate(); var service2 = GetOrRegisterInstantiate(); var service3 = GetOrRegisterInstantiate(); var service4 = GetOrRegisterInstantiate(); action(service1, service2, service3, service4); - return this; } - public ISereinIOC Run(Action action) + public void Run(Action action) { var service1 = GetOrRegisterInstantiate(); var service2 = GetOrRegisterInstantiate(); @@ -433,10 +431,9 @@ namespace Serein.Library.Utils var service4 = GetOrRegisterInstantiate(); var service5 = GetOrRegisterInstantiate(); action(service1, service2, service3, service4, service5); - return this; } - public ISereinIOC Run(Action action) + public void Run(Action action) { var service1 = GetOrRegisterInstantiate(); var service2 = GetOrRegisterInstantiate(); @@ -445,10 +442,9 @@ namespace Serein.Library.Utils var service5 = GetOrRegisterInstantiate(); var service6 = GetOrRegisterInstantiate(); action(service1, service2, service3, service4, service5, service6); - return this; } - public ISereinIOC Run(Action action) + public void Run(Action action) { var service1 = GetOrRegisterInstantiate(); var service2 = GetOrRegisterInstantiate(); @@ -458,10 +454,9 @@ namespace Serein.Library.Utils var service6 = GetOrRegisterInstantiate(); var service7 = GetOrRegisterInstantiate(); action(service1, service2, service3, service4, service5, service6, service7); - return this; } - public ISereinIOC Run(Action action) + public void Run(Action action) { var service1 = GetOrRegisterInstantiate(); var service2 = GetOrRegisterInstantiate(); @@ -472,7 +467,6 @@ namespace Serein.Library.Utils var service7 = GetOrRegisterInstantiate(); var service8 = GetOrRegisterInstantiate(); action(service1, service2, service3, service4, service5, service6, service7, service8); - return this; } diff --git a/NodeFlow/Base/NodeModelBaseData.cs b/NodeFlow/Base/NodeModelBaseData.cs index 7880547..6a98e7a 100644 --- a/NodeFlow/Base/NodeModelBaseData.cs +++ b/NodeFlow/Base/NodeModelBaseData.cs @@ -9,12 +9,12 @@ namespace Serein.NodeFlow.Base /// public abstract partial class NodeModelBase :IDynamicFlowNode { + private static readonly ConnectionType[] ct = [ConnectionType.IsSucceed, + ConnectionType.IsFail, + ConnectionType.IsError, + ConnectionType.Upstream]; public NodeModelBase() { - ConnectionType[] ct = [ConnectionType.IsSucceed, - ConnectionType.IsFail, - ConnectionType.IsError, - ConnectionType.Upstream]; PreviousNodes = []; SuccessorNodes = []; foreach (ConnectionType ctType in ct) diff --git a/NodeFlow/Base/NodeModelBaseFunc.cs b/NodeFlow/Base/NodeModelBaseFunc.cs index 09bcb23..24de488 100644 --- a/NodeFlow/Base/NodeModelBaseFunc.cs +++ b/NodeFlow/Base/NodeModelBaseFunc.cs @@ -98,7 +98,7 @@ namespace Serein.NodeFlow.Base { Stack stack = new Stack(); stack.Push(this); - var cts = context.SereinIoc.Get(FlowStarter.FlipFlopCtsName); + var cts = context.Env.IOC.Get(FlowStarter.FlipFlopCtsName); while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环 { // 节点执行异常时跳过执行 @@ -109,7 +109,7 @@ namespace Serein.NodeFlow.Base // 设置方法执行的对象 if (currentNode.MethodDetails?.ActingInstance == null && currentNode.MethodDetails?.ActingInstanceType is not null) { - currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType); + currentNode.MethodDetails.ActingInstance ??= context.Env.IOC.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType); } #region 执行相关 @@ -330,52 +330,139 @@ namespace Serein.NodeFlow.Base } /// - /// 更新节点数据,并检查监视表达式 + /// 更新节点数据,并检查监视表达式是否生效 /// /// - public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object? newData = null) + public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context,NodeModelBase nodeModel, object? newData = null) { + string guid = nodeModel.Guid; - // 检查是否存在监视表达式 - if (newData is not null && nodeModel.DebugSetting.InterruptExpressions.Count > 0) + if(newData is not null) { - // 表达式环境下判断是否需要执行中断 - bool isExpInterrupt = false; - string? exp = ""; - // 判断执行监视表达式,直到为 true 时退出 - for (int i = 0; i < nodeModel.DebugSetting.InterruptExpressions.Count && !isExpInterrupt; i++) - { - exp = nodeModel.DebugSetting.InterruptExpressions[i]; - isExpInterrupt = SereinConditionParser.To(newData, exp); - } - - if (isExpInterrupt) // 触发中断 - { - InterruptClass interruptClass = InterruptClass.Branch; // 分支中断 - if (context.FlowEnvironment.SetNodeInterrupt(nodeModel.Guid, interruptClass)) - { - context.FlowEnvironment.TriggerInterrupt(guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp); - var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); - await Console.Out.WriteLineAsync($"[{nodeModel.MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); - } - - } + await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象 + await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点 + nodeModel.FlowData = newData; // 替换数据 } + + + + //if(context.Env.CheckObjMonitorState(newData, out List exps)) // 如果新的数据处于查看状态,通知UI进行更新?交给运行环境判断? + //{ + // context.Env.MonitorObjectNotification(guid, newData); // 对象处于监视状态,通知UI更新数据显示 + // if (exps.Count > 0) + // { + // // 表达式环境下判断是否需要执行中断 + // bool isExpInterrupt = false; + // string? exp = ""; + // // 判断执行监视表达式,直到为 true 时退出 + // for (int i = 0; i < exps.Count && !isExpInterrupt; i++) + // { + // exp = exps[i]; + // isExpInterrupt = SereinConditionParser.To(newData, exp); + // } + + // if (isExpInterrupt) // 触发中断 + // { + // InterruptClass interruptClass = InterruptClass.Branch; // 分支中断 + // if (context.Env.SetNodeInterrupt(nodeModel.Guid, interruptClass)) + // { + // context.Env.TriggerInterrupt(guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Obj); + // var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); + // await Console.Out.WriteLineAsync($"[{newData}]中断已{cancelType},开始执行后继分支"); + // } + // } + // } + + //} + + + //if (newData is not null && nodeModel.DebugSetting.InterruptExpressions.Count > 0) // 检查节点是否存在监视表达式 + //{ + // // 表达式环境下判断是否需要执行中断 + // bool isExpInterrupt = false; + // string? exp = ""; + // // 判断执行监视表达式,直到为 true 时退出 + // for (int i = 0; i < nodeModel.DebugSetting.InterruptExpressions.Count && !isExpInterrupt; i++) + // { + // exp = nodeModel.DebugSetting.InterruptExpressions[i]; + // isExpInterrupt = SereinConditionParser.To(newData, exp); + // } + + // if (isExpInterrupt) // 触发中断 + // { + // InterruptClass interruptClass = InterruptClass.Branch; // 分支中断 + // if (context.Env.SetNodeInterrupt(nodeModel.Guid, interruptClass)) + // { + // context.Env.TriggerInterrupt(guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp); + // var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); + // await Console.Out.WriteLineAsync($"[{nodeModel.MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); + // } + + // } + //} + + + //else if (nodeModel.DebugSetting.InterruptClass != InterruptClass.None) //{ // var cancelType = await nodeModel.DebugSetting.InterruptTask; // await Console.Out.WriteLineAsync($"[{nodeModel.MethodDetails.MethodName}]中断已{(cancelType == CancelType.Manual ? "手动取消" : "自动取消")},开始执行后继分支"); //} - nodeModel.FlowData = newData; // 替换数据 - // 节点是否监视了数据,如果是,调用环境接口触发其相关事件。 - if (nodeModel.DebugSetting.IsMonitorFlowData) + + + //if (nodeModel.DebugSetting.IsMonitorFlowData) + //{ + // // 节点是否监视了数据,如果是,调用环境接口触发其相关事件。 + // context.Env.FlowDataNotification(guid, newData); + //} + } + + private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object data, int type) + { + MonitorObjectEventArgs.ObjSourceType sourceType; + object key; + if(type == 0) { - context.FlowEnvironment.FlowDataNotification(guid, newData); + key = data; + sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; + } + else + { + key = nodeModel.Guid; + sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; } + if (context.Env.CheckObjMonitorState(key, out List exps)) // 如果新的数据处于查看状态,通知UI进行更新?交给运行环境判断? + { + context.Env.MonitorObjectNotification(nodeModel.Guid, data, sourceType); // 对象处于监视状态,通知UI更新数据显示 + if (exps.Count > 0) + { + // 表达式环境下判断是否需要执行中断 + bool isExpInterrupt = false; + string? exp = ""; + // 判断执行监视表达式,直到为 true 时退出 + for (int i = 0; i < exps.Count && !isExpInterrupt; i++) + { + exp = exps[i]; + isExpInterrupt = SereinConditionParser.To(data, exp); + } + if (isExpInterrupt) // 触发中断 + { + InterruptClass interruptClass = InterruptClass.Branch; // 分支中断 + if (context.Env.SetNodeInterrupt(nodeModel.Guid, interruptClass)) + { + context.Env.TriggerInterrupt(nodeModel.Guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Obj); + var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); + await Console.Out.WriteLineAsync($"[{data}]中断已{cancelType},开始执行后继分支"); + } + } + } + + } } + /// /// 释放对象 diff --git a/NodeFlow/FlowEnvironment.cs b/NodeFlow/FlowEnvironment.cs index 2bfae7d..0fe00f4 100644 --- a/NodeFlow/FlowEnvironment.cs +++ b/NodeFlow/FlowEnvironment.cs @@ -8,6 +8,7 @@ using Serein.Library.Utils; using Serein.NodeFlow.Base; using Serein.NodeFlow.Model; using Serein.NodeFlow.Tool; +using System.Collections; using System.Collections.Concurrent; using System.Reflection; using System.Xml.Linq; @@ -41,10 +42,11 @@ namespace Serein.NodeFlow /// /// 运行环境 /// - public class FlowEnvironment : IFlowEnvironment + public class FlowEnvironment : IFlowEnvironment, ISereinIOC { public FlowEnvironment() { + sereinIOC = new SereinIOC(); ChannelFlowInterrupt = new ChannelFlowInterrupt(); LoadedAssemblyPaths = new List(); LoadedAssemblies = new List(); @@ -113,6 +115,8 @@ namespace Serein.NodeFlow #endregion + #region 属性 + /// /// 环境名称 /// @@ -121,38 +125,46 @@ namespace Serein.NodeFlow /// /// 是否全局中断 /// - public bool IsGlobalInterrupt { get; set; } + public bool IsGlobalInterrupt { get; set; } /// /// 流程中断器 /// public ChannelFlowInterrupt ChannelFlowInterrupt { get; set; } - + + public ISereinIOC IOC { get => this; } + #endregion + + #region 私有变量 + /// + /// 容器管理 + /// + private SereinIOC sereinIOC; /// /// 存储加载的程序集路径 /// - public List LoadedAssemblyPaths { get; } + private List LoadedAssemblyPaths { get; } /// /// 存储加载的程序集 /// - public List LoadedAssemblies { get; } + private List LoadedAssemblies { get; } /// /// 存储所有方法信息 /// - public List MethodDetailss { get; } + private List MethodDetailss { get; } /// /// 环境加载的节点集合 /// - public Dictionary Nodes { get; } + private Dictionary Nodes { get; } /// /// 存放触发器节点(运行时全部调用) /// - public List FlipflopNodes { get; } + private List FlipflopNodes { get; } /// /// 起始节点私有属性 @@ -162,7 +174,7 @@ namespace Serein.NodeFlow /// /// 起始节点 /// - public NodeModelBase StartNode + private NodeModelBase StartNode { get { @@ -171,7 +183,7 @@ namespace Serein.NodeFlow set { if (_startNode is not null) - { + { _startNode.IsStart = false; } value.IsStart = true; @@ -179,12 +191,16 @@ namespace Serein.NodeFlow } } - /// /// 流程启动器(每次运行时都会重新new一个) /// private FlowStarter? flowStarter; + + #endregion + + #region 对外暴露的接口 + /// /// 异步运行 /// @@ -204,13 +220,34 @@ namespace Serein.NodeFlow await flowStarter.RunAsync(this, nodes, initMethods, loadingMethods, exitMethods); - if(flowStarter?.FlipFlopState == RunState.NoStart) + if (flowStarter?.FlipFlopState == RunState.NoStart) { this.Exit(); // 未运行触发器时,才会调用结束方法 } flowStarter = null; } + public async Task StartFlowInSelectNodeAsync(string startNodeGuid) + { + if (flowStarter is null) + { + return; + } + if (flowStarter.FlowState == RunState.Running || flowStarter.FlipFlopState == RunState.Running) + { + NodeModelBase? nodeModel = GuidToModel(startNodeGuid); + if (nodeModel is null || nodeModel is SingleFlipflopNode) + { + return; + } + await flowStarter.StartFlowInSelectNodeAsync(nodeModel); + } + else + { + return; + } + } + /// /// 退出 /// @@ -221,7 +258,7 @@ namespace Serein.NodeFlow foreach (var node in Nodes.Values) { - if(node is not null) + if (node is not null) { node.ReleaseFlowData(); // 退出时释放对象计数 } @@ -244,32 +281,6 @@ namespace Serein.NodeFlow } - /// - /// 运行环节加载了项目文件,需要创建节点控件 - /// - /// - /// - /// - /// - private NodeControlType GetNodeControlType(NodeInfo nodeInfo) - { - // 创建控件实例 - NodeControlType controlType = nodeInfo.Type switch - { - $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" => NodeControlType.Action,// 动作节点控件 - $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" => NodeControlType.Flipflop, // 触发器节点控件 - - $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" => NodeControlType.ExpCondition,// 条件表达式控件 - $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" => NodeControlType.ExpOp, // 操作表达式控件 - - $"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" => NodeControlType.ConditionRegion, // 条件区域控件 - _ => NodeControlType.None, - }; - - return controlType; - } - - #region 对外暴露的接口 /// /// 加载项目文件 @@ -304,7 +315,7 @@ namespace Serein.NodeFlow foreach (var nodeInfo in project.Nodes) { var controlType = GetNodeControlType(nodeInfo); - if(controlType == NodeControlType.None) + if (controlType == NodeControlType.None) { continue; } @@ -319,9 +330,9 @@ namespace Serein.NodeFlow continue; } TryAddNode(nodeModel); - if(nodeInfo.ChildNodeGuids?.Length > 0) + if (nodeInfo.ChildNodeGuids?.Length > 0) { - regionChildNodes.Add((nodeModel,nodeInfo.ChildNodeGuids)); + regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids)); OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)); } else @@ -331,7 +342,7 @@ namespace Serein.NodeFlow } } // 加载区域的子项 - foreach((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes) + foreach ((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes) { foreach (var childNodeGuid in item.childNodeGuids) { @@ -438,8 +449,8 @@ namespace Serein.NodeFlow var nodeModel = CreateNode(nodeControlType, methodDetails); TryAddNode(nodeModel); - if(flowStarter?.FlowState != RunState.Completion - && nodeControlType == NodeControlType.Flipflop + if (flowStarter?.FlowState != RunState.Completion + && nodeControlType == NodeControlType.Flipflop && nodeModel is SingleFlipflopNode flipflopNode) { // 当前添加节点属于触发器,且当前正在运行,则加载到运行环境中 @@ -553,9 +564,9 @@ namespace Serein.NodeFlow { fromNode.SuccessorNodes[connectionType].Remove(toNode); toNode.PreviousNodes[connectionType].Remove(fromNode); - if(toNode is SingleFlipflopNode flipflopNode) + if (toNode is SingleFlipflopNode flipflopNode) { - if (flowStarter?.FlowState != RunState.Completion + if (flowStarter?.FlowState != RunState.Completion && flipflopNode.NotExitPreviousNode()) { // 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中 @@ -586,7 +597,7 @@ namespace Serein.NodeFlow md = null; return false; } - + } @@ -618,7 +629,7 @@ namespace Serein.NodeFlow else if (interruptClass == InterruptClass.Branch) { nodeModel.DebugSetting.CancelInterruptCallback?.Invoke(); - nodeModel.DebugSetting.GetInterruptTask = () => + nodeModel.DebugSetting.GetInterruptTask = () => { TriggerInterrupt(nodeGuid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor); return ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid); @@ -627,7 +638,7 @@ namespace Serein.NodeFlow { ChannelFlowInterrupt.TriggerSignal(nodeGuid); }; - + } else if (interruptClass == InterruptClass.Global) // 全局……做不了omg { @@ -645,66 +656,130 @@ namespace Serein.NodeFlow /// /// /// - public bool AddInterruptExpression(string nodeGuid, string expression) + public bool AddInterruptExpression(object obj, string expression) { - var nodeModel = GuidToModel(nodeGuid); - if (nodeModel is null) return false; - if (string.IsNullOrEmpty(expression)) + if (dictMonitorObjExpInterrupt.TryGetValue(obj, out var condition)) { - nodeModel.DebugSetting.InterruptExpressions.Clear();// 暂时删除,等UI做好了 + condition.Clear(); // 暂时 + condition.Add(expression);// 暂时 return true; } - - if (nodeModel.DebugSetting.InterruptExpressions.Contains(expression)) - { - Console.WriteLine("表达式已存在"); - return false; - } else { - nodeModel.DebugSetting.InterruptExpressions.Clear();// 暂时删除,等UI做好了 - nodeModel.DebugSetting.InterruptExpressions.Add(expression); + var exps = new List(); + exps.Add(expression); + dictMonitorObjExpInterrupt.TryAdd(obj, exps); return true; } } + //public bool AddInterruptExpression(string nodeGuid, string expression) + //{ + // var nodeModel = GuidToModel(nodeGuid); + // if (nodeModel is null) return false; + // if (string.IsNullOrEmpty(expression)) + // { + // nodeModel.DebugSetting.InterruptExpressions.Clear(); // 传入空表达式时清空 + // return true; + // } + + // if (nodeModel.DebugSetting.InterruptExpressions.Contains(expression)) + // { + // Console.WriteLine("表达式已存在"); + // return false; + // } + // else + // { + // nodeModel.DebugSetting.InterruptExpressions.Clear();// 暂时删除,等UI做好了 + // nodeModel.DebugSetting.InterruptExpressions.Add(expression); + // return true; + // } + //} /// - /// 监视节点的数据 + /// 监视节点的数据(暂时注释) /// /// 需要监视的节点Guid - public void SetNodeFLowDataMonitorState(string nodeGuid, bool isMonitor) - { - var nodeModel = GuidToModel(nodeGuid); - if (nodeModel is null) return; - nodeModel.DebugSetting.IsMonitorFlowData = isMonitor; - - if (isMonitor) - { - var obj = nodeModel.GetFlowData(); - if(obj is not null) - { - FlowDataNotification(nodeGuid, obj); + //public void SetMonitorObjState(string nodeGuid, bool isMonitor) + //{ + // var nodeModel = GuidToModel(nodeGuid); + // if (nodeModel is null) return; + // nodeModel.DebugSetting.IsMonitorFlowData = isMonitor; + // if (isMonitor) + // { + // var obj = nodeModel.GetFlowData(); + // if(obj is not null) + // { + // FlowDataNotification(nodeGuid, obj); + + // } + // } + // else + // { + // // 不再监视的节点清空表达式 + // nodeModel.DebugSetting.InterruptExpressions.Clear(); + // } + //} + + /// + /// 要监视的对象,以及与其关联的表达式 + /// + private ConcurrentDictionary> dictMonitorObjExpInterrupt = []; + + /// + /// 设置对象的监视状态 + /// + /// + /// + /// + public void SetMonitorObjState(object obj, bool isMonitor) + { + if (obj is null) { return; } + var isExist = dictMonitorObjExpInterrupt.ContainsKey(obj); + if (isExist) + { + if (!isMonitor) // 对象存在且需要不监视 + { + dictMonitorObjExpInterrupt.Remove(obj, out _); + } + } + else + { + if (isMonitor) // 对象不存在且需要监视,添加在集合中。 + { + dictMonitorObjExpInterrupt.TryAdd(obj, new List()); ; } } } /// - /// 节点数据更新通知 + /// 检查一个对象是否处于监听状态,如果是,则传出与该对象相关的表达式(用于中断),如果不是,则返回false。 /// /// - public void FlowDataNotification(string nodeGuid, object flowData) + /// + /// + public bool CheckObjMonitorState(object obj, out List? exps) { - OnMonitorObjectChange?.Invoke(new MonitorObjectEventArgs(nodeGuid, flowData)); + if (obj is null) { exps = null; return false; } + return dictMonitorObjExpInterrupt.TryGetValue(obj, out exps); } /// - /// + /// 启动器调用,节点数据更新通知 + /// + /// + public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) + { + OnMonitorObjectChange?.Invoke(new MonitorObjectEventArgs(nodeGuid, monitorData, sourceType)); + } + + /// + /// 启动器调用,节点触发了中断。 /// /// 节点 /// 表达式 - /// 类型,0节点,1表达式 + /// 类型,0用户主动的中断,1表达式中断 public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type) { OnInterruptTrigger?.Invoke(new InterruptTriggerEventArgs(nodeGuid, expression, type)); @@ -719,16 +794,6 @@ namespace Serein.NodeFlow - - - - - - - - - - /// /// Guid 转 NodeModel /// @@ -796,13 +861,37 @@ namespace Serein.NodeFlow return (null, []); } } + /// + /// 运行环节加载了项目文件,需要创建节点控件 + /// + /// + /// + /// + /// + private NodeControlType GetNodeControlType(NodeInfo nodeInfo) + { + // 创建控件实例 + NodeControlType controlType = nodeInfo.Type switch + { + $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" => NodeControlType.Action,// 动作节点控件 + $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" => NodeControlType.Flipflop, // 触发器节点控件 + + $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" => NodeControlType.ExpCondition,// 条件表达式控件 + $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" => NodeControlType.ExpOp, // 操作表达式控件 + + $"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" => NodeControlType.ConditionRegion, // 条件区域控件 + _ => NodeControlType.None, + }; + + return controlType; + } /// /// 创建节点 /// /// - private NodeModelBase CreateNode(NodeControlType nodeControlType,MethodDetails? methodDetails = null) + private NodeModelBase CreateNode(NodeControlType nodeControlType, MethodDetails? methodDetails = null) { // 确定创建的节点类型 Type? nodeType = nodeControlType switch @@ -929,13 +1018,127 @@ namespace Serein.NodeFlow } - #endregion #region 网络交互 #endregion + + + #region IOC容器相关 + ISereinIOC ISereinIOC.Reset() + { + sereinIOC.Reset(); + return this; + } + + ISereinIOC ISereinIOC.Register(Type type, params object[] parameters) + { + sereinIOC.Register(type, parameters); + return this; + } + + ISereinIOC ISereinIOC.Register(params object[] parameters) + { + sereinIOC.Register(parameters); + return this; + } + + ISereinIOC ISereinIOC.Register(params object[] parameters) + { + sereinIOC.Register(parameters); + return this; + } + + T ISereinIOC.GetOrRegisterInstantiate() + { + return sereinIOC.GetOrRegisterInstantiate(); + + } + + object ISereinIOC.GetOrRegisterInstantiate(Type type) + { + return sereinIOC.GetOrRegisterInstantiate(type); + } + + object ISereinIOC.Get(Type type) + { + return sereinIOC.Get(type); + + } + + T ISereinIOC.Get(string key) + { + return sereinIOC.Get(key); + } + + void ISereinIOC.CustomRegisterInstance(string key, object instance, bool needInjectProperty) + { + sereinIOC.CustomRegisterInstance(key, instance, needInjectProperty); + } + + object ISereinIOC.Instantiate(Type type, params object[] parameters) + { + return sereinIOC.Instantiate(type, parameters); + } + + ISereinIOC ISereinIOC.Build() + { + sereinIOC.Build(); + return this; + } + + ISereinIOC ISereinIOC.Run(Action action) + { + sereinIOC.Run(action); + return this; + } + + ISereinIOC ISereinIOC.Run(Action action) + { + sereinIOC.Run(action); + return this; + } + + ISereinIOC ISereinIOC.Run(Action action) + { + sereinIOC.Run(action); + return this; + } + + ISereinIOC ISereinIOC.Run(Action action) + { + sereinIOC.Run(action); + return this; + } + + ISereinIOC ISereinIOC.Run(Action action) + { + sereinIOC.Run(action); + return this; + } + + ISereinIOC ISereinIOC.Run(Action action) + { + sereinIOC.Run(action); + return this; + } + + ISereinIOC ISereinIOC.Run(Action action) + { + sereinIOC.Run(action); + return this; + } + + ISereinIOC ISereinIOC.Run(Action action) + { + sereinIOC.Run(action); + return this; + } + #endregion + + } public static class FlowFunc { @@ -961,7 +1164,7 @@ namespace Serein.NodeFlow } - public static Type? ControlTypeToModel(this NodeControlType nodeControlType ) + public static Type? ControlTypeToModel(this NodeControlType nodeControlType) { // 确定创建的节点类型 Type? nodeType = nodeControlType switch @@ -1000,7 +1203,7 @@ namespace Serein.NodeFlow ConnectionType.Upstream]; foreach (ConnectionType ctType in ct) { - if(node.PreviousNodes[ctType].Count > 0) + if (node.PreviousNodes[ctType].Count > 0) { return false; } diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index f46e408..e60f7d5 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -21,9 +21,13 @@ namespace Serein.NodeFlow /// public class FlowStarter { + /// + /// 全局触发器CTS + /// + public const string FlipFlopCtsName = "<>.FlowFlipFlopCts"; + public FlowStarter() { - SereinIOC = new SereinIOC(); } /// @@ -44,22 +48,25 @@ namespace Serein.NodeFlow /// Completion, } + /// + /// 起点流程运行状态 + /// + public RunState FlowState { get; private set; } = RunState.NoStart; + /// + /// 全局触发器运行状态 + /// + public RunState FlipFlopState { get; private set; } = RunState.NoStart; + /// /// 控制触发器 /// private CancellationTokenSource _flipFlopCts = null; - public const string FlipFlopCtsName = "<>.FlowFlipFlopCts"; + + /// + /// 是否停止启动 + /// + private bool IsStopStart = false; - public bool IsStopStart = false; - /// - /// 运行状态 - /// - public RunState FlowState { get; private set; } = RunState.NoStart; - public RunState FlipFlopState { get; private set; } = RunState.NoStart; - /// - /// 运行时的IOC容器 - /// - private ISereinIOC SereinIOC { get; } = null; /// /// 结束运行时需要执行的方法 /// @@ -77,15 +84,17 @@ namespace Serein.NodeFlow } } + /// + /// 从选定的节点开始运行 + /// + /// + /// + public async Task StartFlowInSelectNodeAsync(NodeModelBase startNode) + { + if (Context is null) return; + await startNode.StartExecute(Context); // 开始运行时从选定节点开始运行 + } - // - // 开始运行 - // - // 起始节点 - // 运行环境 - // 环境中已加载的所有节点方法 - // 触发器节点 - // /// /// 开始运行 @@ -130,11 +139,11 @@ namespace Serein.NodeFlow var isNetFramework = false; if (isNetFramework) { - Context = new Serein.Library.Framework.NodeFlow.DynamicContext(SereinIOC, env); + Context = new Serein.Library.Framework.NodeFlow.DynamicContext(env); } else { - Context = new Serein.Library.Core.NodeFlow.DynamicContext(SereinIOC, env); // 从起始节点启动流程时创建上下文 + Context = new Serein.Library.Core.NodeFlow.DynamicContext(env); // 从起始节点启动流程时创建上下文 } #endregion @@ -151,13 +160,14 @@ namespace Serein.NodeFlow { nodeMd.ActingInstance = null; } - SereinIOC.Reset(); // 开始运行时清空ioc中注册的实例 + env.IOC.Reset(); // 开始运行时清空ioc中注册的实例 + env.IOC.CustomRegisterInstance(typeof(ISereinIOC).FullName, env); // 初始化ioc容器中的类型对象 foreach (var md in thisRuningMds) { if (md.ActingInstanceType != null) { - SereinIOC.Register(md.ActingInstanceType); + env.IOC.Register(md.ActingInstanceType); } else { @@ -168,11 +178,11 @@ namespace Serein.NodeFlow CheckStartState(); // 初始化IOC后检查状态 - SereinIOC.Build(); // 流程启动前的初始化 + env.IOC.Build(); // 流程启动前的初始化 foreach (var md in thisRuningMds) { - md.ActingInstance = SereinIOC.GetOrRegisterInstantiate(md.ActingInstanceType); + md.ActingInstance = env.IOC.GetOrRegisterInstantiate(md.ActingInstanceType); if(md.ActingInstance is null) { await Console.Out.WriteLineAsync($"{md.MethodName} - 无法获取类型[{md.ActingInstanceType}]的实例"); @@ -206,20 +216,20 @@ namespace Serein.NodeFlow { ((Action)md.MethodDelegate).Invoke(md.ActingInstance, [Context]); } - Context.SereinIoc.Build(); // 绑定初始化时注册的类型 + Context.Env.IOC.Build(); // 绑定初始化时注册的类型 foreach (var md in loadingMethods) // 加载 { //object?[]? data = [md.ActingInstance, args]; //md.MethodDelegate.DynamicInvoke(data); ((Action)md.MethodDelegate).Invoke(md.ActingInstance, [Context]); } - Context.SereinIoc.Build(); // 预防有人在加载时才注册类型,再绑定一次 + Context.Env.IOC.Build(); // 预防有人在加载时才注册类型,再绑定一次 #endregion #region 设置流程退出时的回调函数 ExitAction = () => { - SereinIOC.Run(web => { + env.IOC.Run(web => { web?.Stop(); }); @@ -248,7 +258,7 @@ namespace Serein.NodeFlow FlipFlopState = RunState.Running; // 如果存在需要启动的触发器,则开始启动 _flipFlopCts = new CancellationTokenSource(); - SereinIOC.CustomRegisterInstance(FlipFlopCtsName, _flipFlopCts,false); + env.IOC.CustomRegisterInstance(FlipFlopCtsName, _flipFlopCts,false); // 使用 TaskCompletionSource 创建未启动的触发器任务 var tasks = flipflopNodes.Select(async node => @@ -270,7 +280,6 @@ namespace Serein.NodeFlow catch (Exception ex) { await Console.Out.WriteLineAsync(ex.ToString()); - // await Console.Out.WriteLineAsync(ex.Message); } finally { @@ -279,26 +288,26 @@ namespace Serein.NodeFlow #endregion } - public void AddFlipflopInRuning(SingleFlipflopNode singleFlipFlopNode, IFlowEnvironment flowEnvironment) + public void AddFlipflopInRuning(SingleFlipflopNode singleFlipFlopNode, IFlowEnvironment env) { _ = Task.Run(async () => { // 设置对象 - singleFlipFlopNode.MethodDetails.ActingInstance = SereinIOC.GetOrRegisterInstantiate(singleFlipFlopNode.MethodDetails.ActingInstanceType); - await FlipflopExecute(flowEnvironment,singleFlipFlopNode); // 启动触发器 + singleFlipFlopNode.MethodDetails.ActingInstance = env.IOC.GetOrRegisterInstantiate(singleFlipFlopNode.MethodDetails.ActingInstanceType); + await FlipflopExecute(env,singleFlipFlopNode); // 启动触发器 }); } /// /// 启动全局触发器 /// - /// 流程运行全局环境 + /// 流程运行全局环境 /// 需要全局监听信号的触发器 /// - private async Task FlipflopExecute(IFlowEnvironment flowEnvironment,SingleFlipflopNode singleFlipFlopNode) + private async Task FlipflopExecute(IFlowEnvironment env,SingleFlipflopNode singleFlipFlopNode) { - var context = new DynamicContext(SereinIOC, flowEnvironment); // 启动全局触发器时新建上下文 + var context = new DynamicContext(env); // 启动全局触发器时新建上下文 try { @@ -368,75 +377,6 @@ namespace Serein.NodeFlow } -#if false - - /// - /// 全局触发器开始执行相关分支 - /// - /// 上下文 - /// 被触发的全局触发器 - /// 分支类型 - /// - public async Task GlobalFlipflopExecute(IDynamicContext context, SingleFlipflopNode singleFlipFlopNode, - - ConnectionType connectionType, CancellationTokenSource cts) - { - - - bool skip = true; - Stack stack = new Stack(); - stack.Push(singleFlipFlopNode); - - - while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环 - { - // 从栈中弹出一个节点作为当前节点进行处理 - var currentNode = stack.Pop(); - - // 设置方法执行的对象 - //if (currentNode.MethodDetails?.ActingInstance == null && currentNode.MethodDetails?.ActingInstanceType is not null) - //{ - // currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType); - //} - - // 首先执行上游分支 - var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream]; - for (int i = upstreamNodes.Count - 1; i >= 0; i--) - { - upstreamNodes[i].PreviousNode = currentNode; - await upstreamNodes[i].StartExecute(context); // 执行全局触发器的上游分支 - } - - // 当前节点是已经触发了的全局触发器,所以跳过,难道每次都要判断一次? - if (skip) - { - skip = false; - } - else - { - currentNode.FlowData = await currentNode.ExecutingAsync(context); - - - if (currentNode.NextOrientation == ConnectionType.None) - { - break; // 不再执行 - } - connectionType = currentNode.NextOrientation; - } - - // 获取下一分支 - var nextNodes = currentNode.SuccessorNodes[connectionType]; - - // 将下一个节点集合中的所有节点逆序推入栈中 - for (int i = nextNodes.Count - 1; i >= 0; i--) - { - nextNodes[i].PreviousNode = currentNode; - stack.Push(nextNodes[i]); - } - }} -#endif - - } } diff --git a/NodeFlow/NodeStaticConfig.cs b/NodeFlow/NodeStaticConfig.cs index 8933251..0ae2063 100644 --- a/NodeFlow/NodeStaticConfig.cs +++ b/NodeFlow/NodeStaticConfig.cs @@ -1,4 +1,5 @@ -using System; +using Serein.Library.Api; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,5 +13,7 @@ namespace Serein.NodeFlow /// 节点的命名空间 /// public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}"; + + } } diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 452a921..844d211 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -28,6 +28,7 @@ + diff --git a/WorkBench/App.xaml b/WorkBench/App.xaml index ff29067..8cc76d1 100644 --- a/WorkBench/App.xaml +++ b/WorkBench/App.xaml @@ -11,6 +11,13 @@ + + + + diff --git a/WorkBench/MainWindow.xaml b/WorkBench/MainWindow.xaml index d85197f..5b09700 100644 --- a/WorkBench/MainWindow.xaml +++ b/WorkBench/MainWindow.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Serein.WorkBench" - xmlns:custom="clr-namespace:Serein.WorkBench.Node.View" + xmlns:nodeView="clr-namespace:Serein.WorkBench.Node.View" xmlns:themes="clr-namespace:Serein.WorkBench.Themes" Title="Dynamic Node Flow" Height="900" Width="1400" AllowDrop="True" Drop="Window_Drop" DragOver="Window_DragOver" @@ -21,51 +21,49 @@ - - - + + + + + + - - + + - + + - + + + --> - - - - - - - - - - - + + + + + + + + + + + + - + - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index e793d38..cfef947 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -148,25 +148,15 @@ namespace Serein.WorkBench ViewModel = new MainWindowViewModel(this); FlowEnvironment = ViewModel.FlowEnvironment; - ObjectViewer.FlowEnvironment = FlowEnvironment; + ViewObjectViewer.FlowEnvironment = FlowEnvironment; InitFlowEnvironmentEvent(); // 配置环境事件 - - logWindow = new LogWindow(); - logWindow.Show(); - // 重定向 Console 输出 - var logTextWriter = new LogTextWriter(msg => logWindow.AppendText(msg), () => logWindow.Clear());; - Console.SetOut(logTextWriter); + logWindow = InitConsoleOut(); // 重定向 Console 输出 - InitCanvasUI(); + InitCanvasUI(); // 配置画布 - var project = App.FlowProjectData; - if (project == null) - { - return; - } - InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Lenght);// 设置画布大小 - FlowEnvironment.LoadProject(project, App.FileDataPath); // 加载项目 + + FlowEnvironment.LoadProject(App.FlowProjectData, App.FileDataPath); // 加载项目 } private void InitFlowEnvironmentEvent() @@ -186,9 +176,6 @@ namespace Serein.WorkBench FlowEnvironment.OnInterruptTrigger += FlowEnvironment_OnInterruptTrigger; } - - - private void InitCanvasUI() { canvasTransformGroup = new TransformGroup(); @@ -202,7 +189,15 @@ namespace Serein.WorkBench //FlowChartCanvas.RenderTransformOrigin = new Point(0.5, 0.5); } - + private LogWindow InitConsoleOut() + { + var logWindow = new LogWindow(); + logWindow.Show(); + // 重定向 Console 输出 + var logTextWriter = new LogTextWriter(msg => logWindow.AppendText(msg), () => logWindow.Clear()); ; + Console.SetOut(logTextWriter); + return logWindow; + } #region 窗体加载方法 private void Window_Loaded(object sender, RoutedEventArgs e) @@ -215,15 +210,20 @@ namespace Serein.WorkBench } private void Window_ContentRendered(object sender, EventArgs e) { + var project = App.FlowProjectData; + if (project == null) + { + return; + } + InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Lenght);// 设置画布大小 foreach (var connection in Connections) { connection.Refresh(); } - var canvasData = App.FlowProjectData?.Basic.Canvas; + var canvasData = project.Basic.Canvas; if (canvasData != null) { - scaleTransform.ScaleX = 1; scaleTransform.ScaleY = 1; translateTransform.X = 0; @@ -453,35 +453,36 @@ namespace Serein.WorkBench /// - /// 被监视的对象发生改变(节点执行了一次) + /// 被监视的对象发生改变 /// /// private void FlowEnvironment_OnMonitorObjectChange(MonitorObjectEventArgs eventArgs) { string nodeGuid = eventArgs.NodeGuid; - NodeControlBase nodeControl = GuidToControl(nodeGuid); - ObjectViewer.Dispatcher.BeginInvoke(() => { - if (string.IsNullOrEmpty(ObjectViewer.NodeGuid)) // 如果没有加载过 + + object monitorKey = MonitorObjectEventArgs.ObjSourceType.NodeFlowData switch + { + MonitorObjectEventArgs.ObjSourceType.NodeFlowData => nodeGuid, + _ => eventArgs.NewData, + }; + + //NodeControlBase nodeControl = GuidToControl(nodeGuid); + ViewObjectViewer.Dispatcher.BeginInvoke(() => { + if (ViewObjectViewer.MonitorObj is null) // 如果没有加载过对象 { - ObjectViewer.NodeGuid = nodeGuid; - ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点 - //ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点 - } + ViewObjectViewer.LoadObjectInformation(monitorKey, eventArgs.NewData); // 加载对象 ViewObjectViewerControl.MonitorType.Obj + } else { - // 加载过,如果显示的对象来源并非同一个节点,则停止监听之前的节点 - if (!ObjectViewer.NodeGuid.Equals(nodeGuid)) + if (ViewObjectViewer.MonitorKey.Equals(monitorKey)) // 相同对象 { - FlowEnvironment.SetNodeFLowDataMonitorState(ObjectViewer.NodeGuid, false); - ObjectViewer.NodeGuid = nodeGuid; - //ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点 + ViewObjectViewer.RefreshObjectTree(eventArgs.NewData); // 刷新 } else { - ObjectViewer.RefreshObjectTree(eventArgs.NewData); + ViewObjectViewer.LoadObjectInformation(monitorKey, eventArgs.NewData); // 加载对象 } } - }); @@ -733,18 +734,6 @@ namespace Serein.WorkBench #endregion - contextMenu.Items.Add(CreateMenuItem("查看数据", (s, e) => - { - var node = nodeControl?.ViewModel?.Node; - if(node is not null) - { - FlowEnvironment.SetNodeFLowDataMonitorState(ObjectViewer.NodeGuid, false); // 通知环境,该节点的数据更新后需要传到UI - ObjectViewer.NodeGuid = node.Guid; - FlowEnvironment.SetNodeFLowDataMonitorState(node.Guid, true); // 通知环境,该节点的数据更新后需要传到UI - } - - })); - contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => FlowEnvironment.SetStartNode(nodeGuid))); contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => FlowEnvironment.RemoteNode(nodeGuid))); @@ -847,7 +836,7 @@ namespace Serein.WorkBench //{ // try // { - // var typeViewerWindow = new ObjectViewerWindow(); + // var typeViewerWindow = new ViewObjectViewerWindow(); // typeViewerWindow.LoadObjectInformation(@object); // typeViewerWindow.Show(); // } @@ -1076,16 +1065,59 @@ namespace Serein.WorkBench } /// - /// 控件的鼠标左键按下事件,启动拖动操作。 + /// 控件的鼠标左键按下事件,启动拖动操作。同时显示当前正在传递的数据。 /// private void Block_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { - IsControlDragging = true; - startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置 - ((UIElement)sender).CaptureMouse(); // 捕获鼠标 - e.Handled = true; // 防止事件传播影响其他控件 + if(sender is NodeControlBase nodeControl) + { + ChangeViewerObjOfNode(nodeControl); + if (nodeControl.ViewModel.Node.MethodDetails.IsProtectionParameter) return; + IsControlDragging = true; + startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置 + ((UIElement)sender).CaptureMouse(); // 捕获鼠标 + e.Handled = true; // 防止事件传播影响其他控件 + + + } + } + private void ChangeViewerObjOfNode(NodeControlBase nodeControl) + { + // int i = false; + var node = nodeControl?.ViewModel?.Node; + if (node is not null && node.MethodDetails.ReturnType != typeof(void)) + { + if (ViewObjectViewer.MonitorObj is null) + { + FlowEnvironment.SetMonitorObjState(node.Guid, true); // 通知环境,该节点的数据更新后需要传到UI + // FlowEnvironment.SetMonitorObjState(nodeObj, true); // 通知环境,该节点的数据更新后需要传到UI + return; + } + var nodeObj = node.GetFlowData(); + if (nodeObj is null) + { + return; + } + //if (nodeObj.Equals(ViewObjectViewer.MonitorObj) == true) + //{ + // // 选择同一个控件,不再监视 + // ViewObjectViewer.RefreshObjectTree(nodeObj); + // return; + //} + if (node.Guid.Equals(ViewObjectViewer.MonitorKey) == true) + { + ViewObjectViewer.RefreshObjectTree(nodeObj); + return; + } + else + { + FlowEnvironment.SetMonitorObjState(ViewObjectViewer.MonitorKey, false); // 取消对旧节点的监视 + FlowEnvironment.SetMonitorObjState(node.Guid, true); // 通知环境,该节点的数据更新后需要传到UI + } + } + } /// /// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。 /// @@ -1691,12 +1723,18 @@ namespace Serein.WorkBench } private void SelectedNode() { - if(selectNodeControls.Count == 0) + + if (selectNodeControls.Count == 0) { //Console.WriteLine($"没有选择控件"); SelectionRectangle.Visibility = Visibility.Collapsed; return; } + if(selectNodeControls.Count == 1) + { + // ChangeViewerObjOfNode(selectNodeControls[0]); + } + //Console.WriteLine($"一共选取了{selectNodeControls.Count}个控件"); foreach (var node in selectNodeControls) { @@ -2018,7 +2056,7 @@ namespace Serein.WorkBench #endregion - #region 窗体静态方法 + #region 静态方法:创建节点,创建菜单子项,获取区域 private static TControl CreateNodeControl(NodeModelBase model) @@ -2115,6 +2153,12 @@ namespace Serein.WorkBench #endregion + #region IOC视图管理 + void LoadIOCObjectViewer() + { + + } + #endregion /// @@ -2165,6 +2209,27 @@ namespace Serein.WorkBench private void ButtonDebugFlipflopNode_Click(object sender, RoutedEventArgs e) { FlowEnvironment?.Exit(); // 在运行平台上点击了退出 + } + + /// + /// 从选定的节点开始运行 + /// + /// + /// + private async void ButtonStartFlowInSelectNode_Click(object sender, RoutedEventArgs e) + { + if(selectNodeControls.Count == 0) + { + Console.WriteLine("请至少选择一个节点"); + } + else if (selectNodeControls.Count > 1) + { + Console.WriteLine("请只选择一个节点"); + } + else + { + await this.FlowEnvironment.StartFlowInSelectNodeAsync(selectNodeControls[0].ViewModel.Node.Guid); + } } diff --git a/WorkBench/Node/View/ActionNodeControl.xaml b/WorkBench/Node/View/ActionNodeControl.xaml index 72aac64..4faf4b3 100644 --- a/WorkBench/Node/View/ActionNodeControl.xaml +++ b/WorkBench/Node/View/ActionNodeControl.xaml @@ -12,9 +12,13 @@ + + + + - + @@ -40,9 +44,7 @@ - - - + @@ -50,17 +52,17 @@ - - + - - + + - + @@ -71,6 +73,7 @@ - - + + + diff --git a/WorkBench/Node/View/DllControlControl.xaml b/WorkBench/Node/View/DllControlControl.xaml index 2db012e..43a5078 100644 --- a/WorkBench/Node/View/DllControlControl.xaml +++ b/WorkBench/Node/View/DllControlControl.xaml @@ -27,10 +27,10 @@ --> - + - + diff --git a/WorkBench/Node/View/FlipflopNodeControl.xaml b/WorkBench/Node/View/FlipflopNodeControl.xaml index 2c56308..6f09813 100644 --- a/WorkBench/Node/View/FlipflopNodeControl.xaml +++ b/WorkBench/Node/View/FlipflopNodeControl.xaml @@ -12,10 +12,13 @@ - + + + + - + @@ -23,23 +26,25 @@ - - + + + - + - + - + - + + diff --git a/WorkBench/Themes/IOCObjectViewControl.xaml b/WorkBench/Themes/IOCObjectViewControl.xaml new file mode 100644 index 0000000..4913c16 --- /dev/null +++ b/WorkBench/Themes/IOCObjectViewControl.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/WorkBench/Themes/IOCObjectViewControl.xaml.cs b/WorkBench/Themes/IOCObjectViewControl.xaml.cs new file mode 100644 index 0000000..6432b42 --- /dev/null +++ b/WorkBench/Themes/IOCObjectViewControl.xaml.cs @@ -0,0 +1,40 @@ +using Serein.Library.Api; +using Serein.Library.Utils; +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.Themes +{ + /// + /// IOCObjectViewControl.xaml 的交互逻辑 + /// + public partial class IOCObjectViewControl : UserControl + { + private IOCObjectViewMoel IOCObjectViewMoel; + private SereinIOC sereinIOC; + public void SetIOC(SereinIOC sereinIOC) + { + this.sereinIOC = sereinIOC; + } + + public IOCObjectViewControl() + { + InitializeComponent(); + IOCObjectViewMoel = new IOCObjectViewMoel(); + DataContext = IOCObjectViewMoel; + } + + } +} diff --git a/WorkBench/Themes/IOCObjectViewMoel.cs b/WorkBench/Themes/IOCObjectViewMoel.cs new file mode 100644 index 0000000..366a8a2 --- /dev/null +++ b/WorkBench/Themes/IOCObjectViewMoel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.WorkBench.Themes +{ + internal class IOCObjectViewMoel + { + } +} diff --git a/WorkBench/Themes/LazyTreeView.xaml b/WorkBench/Themes/LazyTreeView.xaml new file mode 100644 index 0000000..cbf7d7f --- /dev/null +++ b/WorkBench/Themes/LazyTreeView.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + diff --git a/WorkBench/Themes/LazyTreeView.xaml.cs b/WorkBench/Themes/LazyTreeView.xaml.cs new file mode 100644 index 0000000..2c68696 --- /dev/null +++ b/WorkBench/Themes/LazyTreeView.xaml.cs @@ -0,0 +1,58 @@ +using Serein.Library.Enums; +using Serein.NodeFlow.Base; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +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 +{ + /// + /// LazyTreeView.xaml 的交互逻辑 + /// + public partial class LazyTreeView : UserControl + { + public ObservableCollection RootNodes { get; set; } + + public LazyTreeView() + { + InitializeComponent(); + RootNodes = new ObservableCollection(); + treeView.DataContext = this; + } + + private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) + { + if (e.NewValue is NodeModelBase node) + { + // 在这里设置 Expanded 事件 + var treeViewItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(node); + treeViewItem.Expanded += (s, args) => LoadChildren(node, treeViewItem); + } + } + private void LoadChildren(NodeModelBase node, TreeViewItem treeViewItem) + { + // 懒加载逻辑 + if (node.SuccessorNodes.Count > 0) + { + treeViewItem.Items.Clear(); + foreach (var child in node.SuccessorNodes[ConnectionType.IsSucceed]) // 根据类型加载子项 + { + treeViewItem.Items.Add(child); + } + } + } + } +} diff --git a/WorkBench/Themes/NodeTreeView.cs b/WorkBench/Themes/NodeTreeView.cs new file mode 100644 index 0000000..67b0b52 --- /dev/null +++ b/WorkBench/Themes/NodeTreeView.cs @@ -0,0 +1,71 @@ +using Serein.NodeFlow.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls.Primitives; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows; +using Serein.Library.Enums; + +namespace Serein.WorkBench.Themes +{ + public class NodeTreeView : TreeView + { + public NodeTreeView() + { + this.ItemContainerGenerator.StatusChanged += OnStatusChanged; + } + + private void OnStatusChanged(object sender, EventArgs e) + { + if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) + { + foreach (var item in Items) + { + var treeViewItem = (TreeViewItem)this.ItemContainerGenerator.ContainerFromItem(item); + if (treeViewItem != null) + { + treeViewItem.Expanded += TreeViewItem_Expanded; + ApplyColor(treeViewItem, item as NodeModelBase); + } + } + } + } + + private void TreeViewItem_Expanded(object sender, RoutedEventArgs e) + { + if (sender is TreeViewItem item && item.DataContext is NodeModelBase node) + { + if (item.Items.Count == 0) // 懒加载 + { + foreach (var childNode in node.SuccessorNodes[ConnectionType.Upstream]) + { + item.Items.Add(childNode); + } + } + } + } + + private void ApplyColor(TreeViewItem item, NodeModelBase node) + { + // 根据 ControlType 设置颜色 + switch (node.ControlType) + { + case NodeControlType.Flipflop: + item.Background = Brushes.LightGreen; + break; + case NodeControlType.Action: + item.Background = Brushes.LightCoral; + break; + // 添加更多条件 + default: + item.Background = Brushes.Transparent; + break; + } + } + } + +} diff --git a/WorkBench/Themes/ObjectViewerControl.xaml.cs b/WorkBench/Themes/ObjectViewerControl.xaml.cs index 3eb7d8a..a346e13 100644 --- a/WorkBench/Themes/ObjectViewerControl.xaml.cs +++ b/WorkBench/Themes/ObjectViewerControl.xaml.cs @@ -1,7 +1,9 @@ -using Serein.Library.Api; +using Newtonsoft.Json.Linq; +using Serein.Library.Api; using Serein.NodeFlow.Base; using Serein.NodeFlow.Tool.SereinExpression; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -14,6 +16,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Markup.Primitives; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; @@ -54,73 +57,83 @@ namespace Serein.WorkBench.Themes /// public partial class ObjectViewerControl : UserControl { - private object _objectInstance; - public string NodeGuid { get;set; } - public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); } - public IFlowEnvironment FlowEnvironment { get;set; } - public NodeModelBase NodeModel { get;set; } - public ObjectViewerControl() { InitializeComponent(); } - private DateTime _lastRefreshTime = DateTime.MinValue; // 上次刷新时间 - private TimeSpan _refreshInterval = TimeSpan.FromSeconds(0.1); // 刷新间隔(2秒) + /// + /// 监视类型 + /// + public enum MonitorType + { + /// + /// 作用于对象(对象的引用)的监视 + /// + NodeFlowData, + /// + /// 作用与节点(FLowData)的监视 + /// + IOCObj, + } + + /// + /// 运行环境 + /// + public IFlowEnvironment FlowEnvironment { get; set; } + + /// + /// 监视对象的键 + /// + public object MonitorKey { get => monitorKey; } + /// + /// 正在监视的对象 + /// + public object MonitorObj { get => monitorObj; } + + /// + /// 监视表达式 + /// + public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); } + + private object monitorKey; + private object monitorObj; + + // 用于存储当前展开的节点路径 + private HashSet expandedNodePaths = new HashSet(); + /// /// 加载对象信息,展示其成员 /// /// 要展示的对象 - public void LoadObjectInformation(object obj) + public void LoadObjectInformation(object key, object obj) { - if (obj == null) - return; - - - // 当前时间 - var currentTime = DateTime.Now; - - // 如果上次刷新时间和当前时间之间的差值小于设定的间隔,则跳过 - if (currentTime - _lastRefreshTime < _refreshInterval) - { - // 跳过过于频繁的刷新调用 - return; - } - - // 记录这次的刷新时间 - _lastRefreshTime = currentTime; - - _objectInstance = obj; - RefreshObjectTree(obj); + if (obj == null) return; + monitorKey = key; + monitorObj = obj; + expandedNodePaths.Clear(); + LoadTree(obj); } - ///// - ///// 添加表达式 - ///// - ///// - ///// - //private void AddMonitorExpressionButton_Click(object sender, RoutedEventArgs e) - //{ - - // OpenInputDialog((exp) => - // { - // FlowEnvironment.AddInterruptExpression(NodeGuid, exp); - // }); - //} - - + /// + /// 刷新对象 + /// + /// + /// private void RefreshButton_Click(object sender, RoutedEventArgs e) { - //RefreshObjectTree(_objectInstance); - FlowEnvironment.SetNodeFLowDataMonitorState(NodeGuid, true); + RefreshObjectTree(monitorObj); } + /// + /// 更新表达式 + /// + /// + /// private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e) { - //MonitorExpression = ExpressionTextBox.Text.ToString(); - - if(FlowEnvironment.AddInterruptExpression(NodeGuid, MonitorExpression)) + if (FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式 { if (string.IsNullOrEmpty(MonitorExpression)) { @@ -131,98 +144,72 @@ namespace Serein.WorkBench.Themes UpMonitorExpressionButton.Content = "更新监视表达式"; } } - } - - - // 用于存储当前展开的节点路径 - private HashSet _expandedNodePaths = new HashSet(); + private TreeViewItem? LoadTree(object obj) + { + if (obj is null) return null; + var objectType = obj.GetType(); + FlowDataDetails flowDataDetails = new FlowDataDetails + { + Name = objectType.Name, + DataType = objectType, + DataValue = obj, + DataPath = "" + }; + var rootNode = new TreeViewItem + { + Header = objectType.Name, + Tag = flowDataDetails, + }; + + + ObjectTreeView.Items.Clear(); // 移除对象树的所有节点 + ObjectTreeView.Items.Add(rootNode); // 添加所有节点 + rootNode.Expanded += TreeViewItem_Expanded; // 监听展开事件 + rootNode.Collapsed += TreeViewItem_Collapsed; // 监听折叠事件 + // 这里创建了一个子项,并给这个子项创建了“正在加载”的子项 + // 然后移除了原来对象树的所有项,再把这个新创建的子项添加上去 + // 绑定了展开/折叠事件后,自动展开第一层,开始反射obj的成员,并判断obj的成员生成什么样的节点 + return rootNode; + } /// /// 刷新对象属性树 /// public void RefreshObjectTree(object obj) { - if (obj is null) - return; - // 当前时间 - var currentTime = DateTime.Now; - - // 如果上次刷新时间和当前时间之间的差值小于设定的间隔,则跳过 - if (currentTime - _lastRefreshTime < _refreshInterval) + monitorObj = obj; + var rootNode = LoadTree(obj); + if (rootNode is not null) { - // 跳过过于频繁的刷新调用 - return; + ExpandPreviouslyExpandedNodes(rootNode); // 遍历节点,展开之前记录的节点 + } - - // 记录这次的刷新时间 - _lastRefreshTime = currentTime; - - var objectType = obj.GetType(); - - FlowDataDetails flowDataDetails = new FlowDataDetails - { - Name = objectType.Name, - DataType = objectType, - DataValue = obj - }; - var rootNode = new TreeViewItem - { - Header = objectType.Name, - Tag = flowDataDetails, - }; - - // 添加占位符节点 - AddPlaceholderNode(rootNode); - ObjectTreeView.Items.Clear(); - ObjectTreeView.Items.Add(rootNode); - - // 监听展开事件 - rootNode.Expanded += TreeViewItem_Expanded; - - // 自动展开第一层 - rootNode.IsExpanded = true; // 直接展开根节点 - - // 加载根节点的属性和字段 - if (rootNode.Items.Count == 1 && rootNode.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...") - { - rootNode.Items.Clear(); - AddMembersToTreeNode(rootNode, obj, objectType); - } - // 遍历节点,展开之前记录的节点 - ExpandPreviouslyExpandedNodes(rootNode); + } - // 遍历并展开之前记录的节点 - private void ExpandPreviouslyExpandedNodes(TreeViewItem node) + /// + /// 展开父节点,如果路径存在哈希记录,则将其自动展开,并递归展开后的子节点。 + /// + /// + private void ExpandPreviouslyExpandedNodes(TreeViewItem node) { - if (_expandedNodePaths.Contains(GetNodeFullPath(node))) + if (node == null) return; + if(node.Tag is FlowDataDetails flowDataDetails) { - node.IsExpanded = true; + if (expandedNodePaths.Contains(flowDataDetails.DataPath)) + { + node.IsExpanded = true; + } } - + foreach (TreeViewItem child in node.Items) { ExpandPreviouslyExpandedNodes(child); } } - - - - - - - /// - /// 添加父节点 - /// - /// - private void AddPlaceholderNode(TreeViewItem node) - { - node.Items.Add(new TreeViewItem { Header = "Loading..." }); - } - /// /// 展开子项事件 /// @@ -231,19 +218,45 @@ namespace Serein.WorkBench.Themes private void TreeViewItem_Expanded(object sender, RoutedEventArgs e) { var item = (TreeViewItem)sender; - - if (item.Items.Count == 1 && item.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...") + + + // 判断有没有加载过 + //if (item.Items.Count == 1 && item.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...") + if (item.Items.Count == 0) { - item.Items.Clear(); if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj { // 记录当前节点的路径 - _expandedNodePaths.Add(GetNodeFullPath(item)); + var path = flowDataDetails.DataPath; + expandedNodePaths.Add(path); AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType); } } } + /// + /// 折叠事件 + /// + /// + /// + private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e) + { + var item = (TreeViewItem)sender; + + if (item.Items.Count > 0) + { + if (item.Tag is FlowDataDetails flowDataDetails) + { + // 记录当前节点的路径 + var path = flowDataDetails.DataPath; + if(path != "") + { + expandedNodePaths.Remove(path); + } + } + } + } + /// /// 反射对象数据添加子节点 /// @@ -252,81 +265,168 @@ namespace Serein.WorkBench.Themes /// private void AddMembersToTreeNode(TreeViewItem treeViewNode, object obj, Type type) { - // 获取属性和字段 + // 获取公开的属性 var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); foreach (var member in members) { - TreeViewItem memberNode = ConfigureTreeViewItem(obj, member); - treeViewNode.Items.Add(memberNode); - if (ConfigureTreeItemMenu(memberNode, member, out ContextMenu? contextMenu)) + if (member.Name.StartsWith(".") || + member.Name.StartsWith("get_") || + member.Name.StartsWith("set_") + ) { - memberNode.ContextMenu = contextMenu; // 设置子项节点的事件 - + // 跳过构造函数、属性的get/set方法 + continue; } - + TreeViewItem memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象 + if (memberNode != null) + { + treeViewNode.Items.Add(memberNode); // 添加到当前节点 + // 配置数据路径 + FlowDataDetails subFlowDataDetails = (FlowDataDetails)memberNode.Tag; + string superPath = ((FlowDataDetails)treeViewNode.Tag).DataPath; + string subPath = superPath + "." + subFlowDataDetails.Name; + subFlowDataDetails.DataPath = subPath; + + // 配置右键菜单 + var contextMenu = new ContextMenu(); + contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) => + { + ExpressionTextBox.Text = subPath; // 获取表达式 + + })); + memberNode.ContextMenu = contextMenu; + } } } /// - /// 配置右键菜单功能 + /// 配置节点子项 /// /// /// /// - private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member) + private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member) { TreeViewItem memberNode = new TreeViewItem { Header = member.Name }; - + #region 属性 if (member is PropertyInfo property) { - - string propertyValue = GetPropertyValue(obj, property,out object value); - FlowDataDetails flowDataDetails = new FlowDataDetails + + if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.GetValue(obj) is IEnumerable collection && collection is not null) { - ItemType = TreeItemType.Property, - DataType = property.PropertyType, - Name = property.Name, - DataValue = value, - DataPath = GetNodeFullPath(memberNode), - }; + // 处理集合类型的属性 + memberNode.Tag = new FlowDataDetails + { + ItemType = TreeItemType.IEnumerable, + DataType = property.PropertyType, + Name = property.Name, + DataValue = collection, + }; - memberNode.Tag = flowDataDetails; - - memberNode.Header = $"{property.Name} : {property.PropertyType.Name} = {propertyValue}"; - - if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string)) - { - AddPlaceholderNode(memberNode); - memberNode.Expanded += TreeViewItem_Expanded; + int index = 0; + foreach (var item in collection) + { + var itemNode = new TreeViewItem { Header = $"[{index++}] {item}" ?? "null" }; + memberNode.Tag = new FlowDataDetails + { + ItemType = TreeItemType.Item, + DataType = item?.GetType(), + Name = property.Name, + DataValue = itemNode, + }; + memberNode.Items.Add(itemNode); + } + memberNode.Header = $"{property.Name} : {property.PropertyType.Name} [{index}]"; + return memberNode; } + else + { + string propertyValue = GetPropertyValue(obj, property, out object value); + memberNode.Tag = new FlowDataDetails + { + ItemType = TreeItemType.Property, + DataType = property.PropertyType, + Name = property.Name, + DataValue = value, + }; ; + + memberNode.Header = $"{property.Name} : {property.PropertyType.Name} = {propertyValue}"; + + if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string)) + { + memberNode.Expanded += TreeViewItem_Expanded; + memberNode.Collapsed += TreeViewItem_Collapsed; + } + return memberNode; + } + } + #endregion + #region 字段 else if (member is FieldInfo field) { - string fieldValue = GetFieldValue(obj, field, out object value); - FlowDataDetails flowDataDetails = new FlowDataDetails + if (typeof(IEnumerable).IsAssignableFrom(field.FieldType) && field.GetValue(obj) is IEnumerable collection && collection is not null) { - ItemType = TreeItemType.Field, - DataType = field.FieldType, - Name = field.Name, - DataValue = value, - DataPath = GetNodeFullPath(memberNode), - }; - - memberNode.Tag = flowDataDetails; - - memberNode.Header = $"{field.Name} : {field.FieldType.Name} = {fieldValue}"; - - if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string)) + // 处理集合类型的字段 + memberNode.Tag = new FlowDataDetails + { + ItemType = TreeItemType.IEnumerable, + DataType = field.FieldType, + Name = field.Name, + DataValue = collection, + }; + + int index = 0; + foreach (var item in collection) + { + var itemNode = new TreeViewItem { Header = $"[{index++}] {item}" ?? "null" }; + memberNode.Tag = new FlowDataDetails + { + ItemType = TreeItemType.Item, + DataType = item?.GetType(), + Name = field.Name, + DataValue = itemNode, + }; + //collectionNode.Items.Add(itemNode); + memberNode.Items.Add(itemNode); + } + memberNode.Header = $"{field.Name} : {field.FieldType.Name} [{index}]"; + return memberNode; + } + else { - AddPlaceholderNode(memberNode); - memberNode.Expanded += TreeViewItem_Expanded; + + string fieldValue = GetFieldValue(obj, field, out object value); + + memberNode.Tag = new FlowDataDetails + { + ItemType = TreeItemType.Field, + DataType = field.FieldType, + Name = field.Name, + DataValue = value, + + }; + memberNode.Header = $"{field.Name} : {field.FieldType.Name} = {fieldValue}"; + + if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string)) + { + memberNode.Expanded += TreeViewItem_Expanded; + memberNode.Collapsed += TreeViewItem_Collapsed; + } + return memberNode; } } + #endregion + #region 返回null + else + { + return null; + } + #endregion - return memberNode; } /// @@ -335,15 +435,12 @@ namespace Serein.WorkBench.Themes /// /// /// - private string GetPropertyValue(object obj, PropertyInfo property,out object value) + private string GetPropertyValue(object obj, PropertyInfo property,out object value) { try { - var properties = obj.GetType().GetProperties(); - - // 获取实例属性值 value = property.GetValue(obj); return value?.ToString() ?? "null"; // 返回值或“null” @@ -355,14 +452,13 @@ namespace Serein.WorkBench.Themes } } - /// /// 获取字段类型的成员 /// /// /// /// - private string GetFieldValue(object obj, FieldInfo field, out object value) + private string GetFieldValue(object obj, FieldInfo field, out object value) { try { @@ -376,165 +472,170 @@ namespace Serein.WorkBench.Themes } } - /// - /// 根据成员类别配置右键菜单 - /// - /// - /// - /// - /// - private bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member, out ContextMenu? contextMenu) - { - bool isChange = false; - if (member is PropertyInfo property) - { - isChange = true; - contextMenu = new ContextMenu(); - contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) => - { - string fullPath = GetNodeFullPath(memberNode); - string copyValue = /*"@Get " + */fullPath; - ExpressionTextBox.Text = copyValue; - // Clipboard.SetDataObject(copyValue); - - })); - } - else if (member is MethodInfo method) - { - //isChange = true; - contextMenu = new ContextMenu(); - } - else if (member is FieldInfo field) - { - isChange = true; - contextMenu = new ContextMenu(); - contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) => - { - string fullPath = GetNodeFullPath(memberNode); - string copyValue = /*"@Get " +*/ fullPath; - ExpressionTextBox.Text = copyValue; - // Clipboard.SetDataObject(copyValue); - })); - } - else - { - contextMenu = new ContextMenu(); - } - return isChange; - } - - - - /// - /// 获取当前节点的完整路径,例如 "node1.node2.node3.node4" - /// - /// 目标节点 - /// 节点路径 - private string GetNodeFullPath(TreeViewItem node) - { - if (node == null) - return string.Empty; - - FlowDataDetails flowDataDetails = (FlowDataDetails)node.Tag; - var parent = GetParentTreeViewItem(node); - if (parent != null) - { - // 递归获取父节点的路径,并拼接当前节点的 Header - return $"{GetNodeFullPath(parent)}.{flowDataDetails.Name}"; - } - else - { - // 没有父节点,则说明这是根节点,直接返回 Header - return ""; - // return typeNodeDetails.Name.ToString(); - } - } - - /// - /// 获取指定节点的父级节点 - /// - /// 目标节点 - /// 父节点 - private TreeViewItem GetParentTreeViewItem(TreeViewItem node) - { - DependencyObject parent = VisualTreeHelper.GetParent(node); - while (parent != null && !(parent is TreeViewItem)) - { - parent = VisualTreeHelper.GetParent(parent); - } - return parent as TreeViewItem; - } - - - - private InputDialog OpenInputDialog(Action action) - { - var inputDialog = new InputDialog(); - inputDialog.Closed += (s, e) => - { - if (inputDialog.DialogResult == true) - { - string userInput = inputDialog.InputValue; - action?.Invoke(userInput); - } - }; - inputDialog.ShowDialog(); - return inputDialog; - - } - - - - - - ///// - ///// 刷新按钮的点击事件 - ///// - //private void RefreshButton_Click(object sender, RoutedEventArgs e) - //{ - // RefreshObjectTree(); - //} - - //private bool IsTimerRefres = false; - //private void TimerRefreshButton_Click(object sender, RoutedEventArgs e) - //{ - // if (IsTimerRefres) - // { - // IsTimerRefres = false; - // TimerRefreshButton.Content = "定时刷新"; - // } - // else - // { - // IsTimerRefres = true; - // TimerRefreshButton.Content = "取消刷新"; - - // _ = Task.Run(async () => { - // while (true) - // { - // if (IsTimerRefres) - // { - // Application.Current.Dispatcher.Invoke(() => - // { - // RefreshObjectTree(); // 刷新UI - // }); - // await Task.Delay(100); - // } - // else - // { - // break; - // } - // } - // IsTimerRefres = false; - // }); - // } - - //} - - - - - } } +/// +/// 上次刷新事件 +/// +//private DateTime lastRefreshTime = DateTime.MinValue; +/// +/// 刷新间隔 +/// +//private readonly TimeSpan refreshInterval = TimeSpan.FromSeconds(0.1); +// 当前时间 +//var currentTime = DateTime.Now; +//if (currentTime - lastRefreshTime < refreshInterval) +//{ +// return; // 跳过过于频繁的刷新调用 +//} +//else +//{ +// lastRefreshTime = currentTime;// 记录这次的刷新时间 +//} +// + +/// +/// 从当前节点获取至父节点的路径,例如 "node1.node2.node3.node4" +/// +/// 目标节点 +/// 节点路径 +//private string GetNodeFullPath(TreeViewItem node) +//{ +// if (node == null) +// return string.Empty; + +// FlowDataDetails flowDataDetails = (FlowDataDetails)node.Tag; +// var parent = GetParentTreeViewItem(node); +// if (parent != null) +// { +// // 递归获取父节点的路径,并拼接当前节点的 Header +// return $"{GetNodeFullPath(parent)}.{flowDataDetails.Name}"; +// } +// else +// { +// // 没有父节点,则说明这是根节点,直接返回 Header +// return ""; +// } +//} + +/// +/// 获取指定节点的父级节点 +/// +/// 目标节点 +/// 父节点 +//private TreeViewItem GetParentTreeViewItem(TreeViewItem node) +//{ +// DependencyObject parent = VisualTreeHelper.GetParent(node); +// while (parent != null && !(parent is TreeViewItem)) +// { +// parent = VisualTreeHelper.GetParent(parent); +// } +// return parent as TreeViewItem; +//} + + + + + + +/// +/// 根据成员类别配置右键菜单 +/// +/// +/// +/// +/// +//private bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member, out ContextMenu? contextMenu) +//{ +// if (ConfigureTreeItemMenu(memberNode, member, out ContextMenu? contextMenu)) +// { +// memberNode.ContextMenu = contextMenu; // 设置子项节点的事件 +// } + +// bool isChange = false; +// if (member is PropertyInfo property) +// { +// isChange = true; +// contextMenu = new ContextMenu(); +// contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) => +// { +// string fullPath = GetNodeFullPath(memberNode); +// string copyValue = /*"@Get " + */fullPath; +// ExpressionTextBox.Text = copyValue; +// // Clipboard.SetDataObject(copyValue); + +// })); +// } +// else if (member is MethodInfo method) +// { +// //isChange = true; +// contextMenu = new ContextMenu(); +// } +// else if (member is FieldInfo field) +// { +// isChange = true; +// contextMenu = new ContextMenu(); +// contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) => +// { +// string fullPath = GetNodeFullPath(memberNode); +// string copyValue = /*"@Get " +*/ fullPath; +// ExpressionTextBox.Text = copyValue; +// // Clipboard.SetDataObject(copyValue); +// })); +// } +// else +// { +// contextMenu = new ContextMenu(); +// } +// return isChange; +//} + + + + + +///// +///// 刷新按钮的点击事件 +///// +//private void RefreshButton_Click(object sender, RoutedEventArgs e) +//{ +// RefreshObjectTree(); +//} + +//private bool IsTimerRefres = false; +//private void TimerRefreshButton_Click(object sender, RoutedEventArgs e) +//{ +// if (IsTimerRefres) +// { +// IsTimerRefres = false; +// TimerRefreshButton.Content = "定时刷新"; +// } +// else +// { +// IsTimerRefres = true; +// TimerRefreshButton.Content = "取消刷新"; + +// _ = Task.Run(async () => { +// while (true) +// { +// if (IsTimerRefres) +// { +// Application.Current.Dispatcher.Invoke(() => +// { +// RefreshObjectTree(); // 刷新UI +// }); +// await Task.Delay(100); +// } +// else +// { +// break; +// } +// } +// IsTimerRefres = false; +// }); +// } + +//} + diff --git a/WorkBench/Themes/TypeViewerWindow.xaml.cs b/WorkBench/Themes/TypeViewerWindow.xaml.cs index e89fac8..ceb0fcf 100644 --- a/WorkBench/Themes/TypeViewerWindow.xaml.cs +++ b/WorkBench/Themes/TypeViewerWindow.xaml.cs @@ -267,7 +267,9 @@ namespace Serein.WorkBench.Themes { Property, Method, - Field + Field, + IEnumerable, + Item, } diff --git a/WorkBench/Tool/Converters/TypeToColorConverter.cs b/WorkBench/Tool/Converters/TypeToColorConverter.cs new file mode 100644 index 0000000..f9d904b --- /dev/null +++ b/WorkBench/Tool/Converters/TypeToColorConverter.cs @@ -0,0 +1,23 @@ +using Serein.Library.Enums; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; + +namespace Serein.WorkBench.Tool.Converters +{ + public class TypeToColorConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + // 根据 ControlType 返回颜色 + return value switch + { + NodeControlType.Action => Brushes.Blue, + NodeControlType.Flipflop => Brushes.Green, + _ => Brushes.Black, + }; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException(); + } +}