diff --git a/FlowEdit/FlowEdit可视化流程编辑器.zip b/FlowEdit/FlowEdit可视化流程编辑器beta(基于WebSocket的远程控制).zip similarity index 61% rename from FlowEdit/FlowEdit可视化流程编辑器.zip rename to FlowEdit/FlowEdit可视化流程编辑器beta(基于WebSocket的远程控制).zip index 38ecc69..d866967 100644 Binary files a/FlowEdit/FlowEdit可视化流程编辑器.zip and b/FlowEdit/FlowEdit可视化流程编辑器beta(基于WebSocket的远程控制).zip differ diff --git a/FlowStartTool/Program.cs b/FlowStartTool/Program.cs index 3db3c81..8dd7e9c 100644 --- a/FlowStartTool/Program.cs +++ b/FlowStartTool/Program.cs @@ -1,9 +1,11 @@ using Newtonsoft.Json; using Serein.Library; using Serein.Library.Api; +using Serein.Library.Utils; using Serein.NodeFlow.Env; using System.Diagnostics; using System.Reflection; +using static System.Net.Mime.MediaTypeNames; namespace Serein.FlowStartTool { @@ -18,7 +20,7 @@ namespace Serein.FlowStartTool SereinProjectData? flowProjectData; string exeAssemblyDictPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - + if (args.Length == 1) { filePath = args[0]; @@ -26,6 +28,7 @@ namespace Serein.FlowStartTool } else if (args.Length == 0) { + Console.WriteLine("loading project file data..."); filePath = Process.GetCurrentProcess().ProcessName + ".dnf"; fileDataPath = exeAssemblyDictPath; @@ -66,10 +69,32 @@ namespace Serein.FlowStartTool public static bool IsRuning; public static async Task StartFlow(SereinProjectData flowProjectData, string fileDataPath) { - Env = new FlowEnvironment(); + SynchronizationContext? uiContext = SynchronizationContext.Current; // 在UI线程上获取UI线程上下文信息 + var uIContextOperation = new UIContextOperation(uiContext); // 封装一个调用UI线程的工具类 + + //if (OperatingSystem.IsLinux()) + //{ + + //} + + // if (uIContextOperation is null) + //{ + // throw new Exception("无法封装 UIContextOperation "); + //} + //else + //{ + // env = new FlowEnvironmentDecorator(uIContextOperation); + // this.window = window; + //} + + Env = new FlowEnvironmentDecorator(uIContextOperation); // Linux 环境下没有线程上下文(暂时没有写) Env.LoadProject(new FlowEnvInfo { Project = flowProjectData }, fileDataPath); // 加载项目 - await Env.StartAsync(); + await Env.StartRemoteServerAsync(7525); // 启动 web socket 监听远程请求 + + //await Env.StartAsync(); + + IsRuning = false; } diff --git a/FlowStartTool/Serein.FlowStartTool.csproj b/FlowStartTool/Serein.FlowStartTool.csproj index 71a1916..bb0d729 100644 --- a/FlowStartTool/Serein.FlowStartTool.csproj +++ b/FlowStartTool/Serein.FlowStartTool.csproj @@ -8,7 +8,7 @@ enable enable prompt - project + starter diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index b1b777b..7719fc4 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -1,4 +1,5 @@ -using Serein.Library.Utils; + +using Serein.Library.Utils; using System; using System.Collections.Generic; using System.Threading; @@ -228,10 +229,10 @@ namespace Serein.Library.Api /// /// - public delegate void NodeRemoteHandler(NodeRemoteEventArgs eventArgs); - public class NodeRemoteEventArgs : FlowEventArgs + public delegate void NodeRemoveHandler(NodeRemoveEventArgs eventArgs); + public class NodeRemoveEventArgs : FlowEventArgs { - public NodeRemoteEventArgs(string nodeGuid) + public NodeRemoveEventArgs(string nodeGuid) { this.NodeGuid = nodeGuid; } @@ -478,10 +479,15 @@ namespace Serein.Library.Api RunState FlipFlopState { get; set; } /// - /// 拓展功能时,如需订阅事件,则需要使用该属性 + /// 表示当前环境 /// IFlowEnvironment CurrentEnv { get; } + /// + /// 由运行环境提供的UI线程上下文操作,用于类库中需要在UI线程中操作视觉元素的场景 + /// + UIContextOperation UIContextOperation { get; } + #endregion #region 事件 @@ -509,7 +515,7 @@ namespace Serein.Library.Api /// /// 移除节点事件 /// - event NodeRemoteHandler OnNodeRemote; + event NodeRemoveHandler OnNodeRemove; /// /// 起始节点变化事件 @@ -674,13 +680,13 @@ namespace Serein.Library.Api /// 起始节点 /// 目标节点 /// 连接类型 - void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType); + Task RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType); /// /// 移除节点/区域/基础控件 /// /// 待移除的节点Guid - void RemoveNode(string nodeGuid); + Task RemoveNodeAsync(string nodeGuid); /// /// 激活未启动的全局触发器 diff --git a/Library/FlowNode/Attribute.cs b/Library/FlowNode/Attribute.cs index 435b632..22a16c7 100644 --- a/Library/FlowNode/Attribute.cs +++ b/Library/FlowNode/Attribute.cs @@ -20,6 +20,7 @@ namespace Serein.Library { public bool IsNotification = false; public bool IsPrint = false; + public bool IsProtection = false; } } diff --git a/Library/Entity/NodeDebugSetting.cs b/Library/FlowNode/NodeDebugSetting.cs similarity index 58% rename from Library/Entity/NodeDebugSetting.cs rename to Library/FlowNode/NodeDebugSetting.cs index 25ad76b..8c3dc45 100644 --- a/Library/Entity/NodeDebugSetting.cs +++ b/Library/FlowNode/NodeDebugSetting.cs @@ -9,27 +9,41 @@ namespace Serein.Library /// /// 节点调试设置,用于中断节点的运行 /// - public class NodeDebugSetting + [AutoProperty(ValuePath = nameof(NodeDebugSetting))] + public partial class NodeDebugSetting { + private readonly NodeModelBase nodeModel; + /// + /// 创建属于某个节点的调试设置 + /// + /// + public NodeDebugSetting(NodeModelBase nodeModel) + { + this.nodeModel = nodeModel; + } /// /// 是否使能 /// - public bool IsEnable { get; set; } = true; + [PropertyInfo(IsNotification = true)] + private bool _isEnable = true; /// /// 中断级别,暂时停止继续执行后继分支。 /// - public InterruptClass InterruptClass { get; set; } = InterruptClass.None; + [PropertyInfo] + private InterruptClass _interruptClass = InterruptClass.None; /// /// 取消中断的回调函数 /// - public Action CancelInterruptCallback { get; set; } + [PropertyInfo] + private Action _cancelInterruptCallback; /// - /// 中断Task(用来取消中断) + /// 中断Task(用来中断) /// - public Func> GetInterruptTask { get; set; } + [PropertyInfo] + private Func> _getInterruptTask; } @@ -53,3 +67,6 @@ namespace Serein.Library Global, } } + + + diff --git a/Library/FlowNode/NodeModelBaseData.cs b/Library/FlowNode/NodeModelBaseData.cs index f221326..e1f7798 100644 --- a/Library/FlowNode/NodeModelBaseData.cs +++ b/Library/FlowNode/NodeModelBaseData.cs @@ -1,17 +1,93 @@ using Serein.Library.Api; -using Serein.Library; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Threading; namespace Serein.Library { + /// /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域 /// + [AutoProperty(ValuePath = nameof(NodeModelBase))] // 是否更名为 NodeProperty? public abstract partial class NodeModelBase : IDynamicFlowNode { + + [PropertyInfo(IsProtection = true)] + private IFlowEnvironment _env; + + /// + /// 在画布中的位置 + /// + [PropertyInfo(IsProtection = true)] + private PositionOfUI _position ; + + /// + /// 附加的调试功能 + /// + [PropertyInfo(IsProtection = true)] + private NodeDebugSetting _debugSetting ; + + /// + /// 描述节点对应的控件类型 + /// + [PropertyInfo(IsProtection = true)] + private NodeControlType _controlType ; + + /// + /// 方法描述。不包含Method与委托,需要通过MethodName从环境中获取委托进行调用。 + /// + [PropertyInfo(IsProtection = true)] + private MethodDetails _methodDetails ; + + /// + /// 标识节点对象全局唯一 + /// + [PropertyInfo(IsProtection = true)] + private string _guid ; + + /// + /// 显示名称 + /// + [PropertyInfo] + private string _displayName ; + + /// + /// 是否为起点控件 + /// + [PropertyInfo] + private bool _isStart ; + + /// + /// 运行时的上一节点 + /// + [PropertyInfo] + private NodeModelBase _previousNode ; + + + + /// + /// 当前节点执行完毕后需要执行的下一个分支的类别 + /// + [PropertyInfo] + private ConnectionType _nextOrientation = ConnectionType.None; + + /// + /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值) + /// + [PropertyInfo] + private Exception _runingException ; + + } + + + + + + public abstract partial class NodeModelBase : IDynamicFlowNode + { public NodeModelBase(IFlowEnvironment environment) { PreviousNodes = new Dictionary>(); @@ -21,75 +97,19 @@ namespace Serein.Library PreviousNodes[ctType] = new List(); SuccessorNodes[ctType] = new List(); } - DebugSetting = new NodeDebugSetting(); + DebugSetting = new NodeDebugSetting(this); this.Env = environment; } - /// - /// 节点保留对环境的引用,因为需要在属性更改时通知 - /// - public IFlowEnvironment Env { get; } - - /// - /// 在画布中的位置 - /// - public PositionOfUI Position { get; set; } - - /// - /// 附加的调试功能 - /// - public NodeDebugSetting DebugSetting { get; set; } - - /// - /// 描述节点对应的控件类型 - /// - public NodeControlType ControlType { get; set; } - - /// - /// 方法描述但不包含Method与委托,需要通过MethodName从环境中获取委托进行调用。 - /// - public MethodDetails MethodDetails { get; set; } - - /// - /// 标识节点对象全局唯一 - /// - public string Guid { get; set; } - - /// - /// 显示名称 - /// - public string DisplayName { get; set; } = string.Empty; - - /// - /// 是否为起点控件 - /// - public bool IsStart { get; set; } - - /// - /// 运行时的上一节点 - /// - public NodeModelBase PreviousNode { get; set; } - /// /// 不同分支的父节点 /// - public Dictionary> PreviousNodes { get; } - + public Dictionary> PreviousNodes { get; } + /// /// 不同分支的子节点 /// - public Dictionary> SuccessorNodes { get; } - - /// - /// 当前节点执行完毕后需要执行的下一个分支的类别 - /// - public ConnectionType NextOrientation { get; set; } = ConnectionType.None; - - /// - /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值) - /// - public Exception RuningException { get; set; } = null; - + public Dictionary> SuccessorNodes { get; } /// /// 控制FlowData在同一时间只会被同一个线程更改。 @@ -126,11 +146,75 @@ namespace Serein.Library } } } - } + /* + /// + /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域 + /// + public abstract partial class NodeModelBase : IDynamicFlowNode + { + + /// + /// 节点保留对环境的引用,因为需要在属性更改时通知 + /// + public IFlowEnvironment Env { get; } + + /// + /// 在画布中的位置 + /// + public PositionOfUI Position { get; set; } + + /// + /// 附加的调试功能 + /// + public NodeDebugSetting DebugSetting { get; set; } + + /// + /// 描述节点对应的控件类型 + /// + public NodeControlType ControlType { get; set; } + + /// + /// 方法描述。不包含Method与委托,需要通过MethodName从环境中获取委托进行调用。 + /// + public MethodDetails MethodDetails { get; set; } + + /// + /// 标识节点对象全局唯一 + /// + public string Guid { get; set; } + + /// + /// 显示名称 + /// + public string DisplayName { get; set; } = string.Empty; + + /// + /// 是否为起点控件 + /// + public bool IsStart { get; set; } + + /// + /// 运行时的上一节点 + /// + public NodeModelBase PreviousNode { get; set; } + + /// + /// 当前节点执行完毕后需要执行的下一个分支的类别 + /// + public ConnectionType NextOrientation { get; set; } = ConnectionType.None; + + /// + /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值) + /// + public Exception RuningException { get; set; } = null; + + + }*/ + /// @@ -156,7 +240,7 @@ namespace Serein.Library // this.FlowData = builder.FlowData; // } - + // /// // /// 节点对应的控件类型 diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index 1895138..54f6fb8 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Serein.Library; using Serein.Library.Api; using Serein.Library.Utils; using Serein.Library.Utils.SereinExpression; diff --git a/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs b/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs index 10e888a..87adcbc 100644 --- a/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs +++ b/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs @@ -212,6 +212,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func> hasResultTask) { result = await hasResultTask(Instance, args); + //Console.WriteLine(result); + // why not data? } else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func task) { @@ -241,12 +243,23 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle //sw.Stop(); //Console.WriteLine($"Emit Invoke:{sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs"); - if(Module.IsReturnValue && result != null && result.GetType().IsClass) + if(result is null) { - //var reusltJsonText = JsonConvert.SerializeObject(result); - _ = SendAsync.Invoke(result); - //_ = SendAsync.Invoke($"{reusltJsonText}"); + return; } + else + { + if (Module.IsReturnValue) + { + _ = SendAsync.Invoke(result); + } + } + //if( && result != null && result.GetType().IsClass) + //{ + // //var reusltJsonText = JsonConvert.SerializeObject(result); + + // //_ = SendAsync.Invoke($"{reusltJsonText}"); + //} } diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index 7b419ee..a29c9f8 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -1,7 +1,7 @@  - 1.0.15 + 1.0.17 net8.0;net462 D:\Project\C#\DynamicControl\SereinFlow\.Output @@ -32,7 +32,7 @@ - + diff --git a/Library/Utils/ChannelFlowInterrupt.cs b/Library/Utils/ChannelFlowInterrupt.cs index c87fa40..9d3ba60 100644 --- a/Library/Utils/ChannelFlowInterrupt.cs +++ b/Library/Utils/ChannelFlowInterrupt.cs @@ -75,7 +75,6 @@ namespace Serein.Library.Utils /// 创建信号,直到手动触发(异步方法) /// /// 信号标识符 - /// 超时时间 /// 等待任务 public async Task GetOrCreateChannelAsync(string signal) { diff --git a/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs b/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs index 1b12021..e583501 100644 --- a/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs +++ b/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs @@ -48,8 +48,6 @@ namespace Serein.Library.Utils.SereinExpression /// public static object Evaluate(string expression, object targetObJ, out bool isChange) { - //var parts = expression.Split([' '], 2); - var parts = expression.Split(new[] { ' ' }, 2, StringSplitOptions.None); if (parts.Length != 2) { diff --git a/Library/Utils/UIContextOperation.cs b/Library/Utils/UIContextOperation.cs index 680bbca..f2a7700 100644 --- a/Library/Utils/UIContextOperation.cs +++ b/Library/Utils/UIContextOperation.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace Serein.Library.Utils { /// - /// 为类库提供了在UI线程上下文操作的方法 + /// 为类库提供了在UI线程上下文操作的方法,如果你在Windows平台上运行,不必手动实例化该类 /// public class UIContextOperation { diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index c6502a7..9a00c63 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -15,6 +15,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Net.Sockets; +using System.Numerics; using System.Reflection; using System.Security.Cryptography; using System.Threading; @@ -29,10 +30,6 @@ namespace Serein.NodeFlow.Env - - // ******************************************************88 - - /// /// 运行环境 /// @@ -54,8 +51,15 @@ namespace Serein.NodeFlow.Env this.ChannelFlowInterrupt = new ChannelFlowInterrupt(); this.IsGlobalInterrupt = false; this.flowStarter = null; - this.sereinIOC.OnIOCMembersChanged += e => this?.OnIOCMembersChanged?.Invoke(e); // 监听IOC容器的注册 - this.uiContextOperation = uiContextOperation; // 本地环境需要存放视图管理 + this.sereinIOC.OnIOCMembersChanged += e => + { + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => this?.OnIOCMembersChanged?.Invoke(e)); // 监听IOC容器的注册 + } + + }; + this.UIContextOperation = uiContextOperation; // 本地环境需要存放视图管理 } #region 远程管理 @@ -139,7 +143,7 @@ namespace Serein.NodeFlow.Env /// /// 移除节点事件 /// - public event NodeRemoteHandler? OnNodeRemote; + public event NodeRemoveHandler? OnNodeRemove; /// /// 起始节点变化事件 @@ -190,10 +194,15 @@ namespace Serein.NodeFlow.Env #region 属性 + /// + /// 当前环境 + /// public IFlowEnvironment CurrentEnv { get => this; } - - + /// + /// UI线程操作类 + /// + public UIContextOperation UIContextOperation { get; set; } /// @@ -245,11 +254,7 @@ namespace Serein.NodeFlow.Env /// public ConcurrentDictionary MethodDetailss { get; } = []; - /// - /// UI线程操作类 - /// - private readonly UIContextOperation uiContextOperation; - + /// /// 容器管理 /// @@ -335,7 +340,11 @@ namespace Serein.NodeFlow.Env public void WriteLineObjToJson(object obj) { var msg = JsonConvert.SerializeObject(obj); - OnEnvOut?.Invoke(msg + Environment.NewLine); + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg + Environment.NewLine)); + } + } @@ -370,8 +379,9 @@ namespace Serein.NodeFlow.Env IOC.Reset(); // 开始运行时清空ioc中注册的实例 - IOC.CustomRegisterInstance(typeof(UIContextOperation).FullName, this.uiContextOperation, false); IOC.CustomRegisterInstance(typeof(IFlowEnvironment).FullName, this); + if (this.UIContextOperation is not null) + IOC.CustomRegisterInstance(typeof(UIContextOperation).FullName, this.UIContextOperation, false); await flowStarter.RunAsync(this, nodes, AutoRegisterTypes, initMethods, loadMethods, exitMethods); @@ -401,6 +411,12 @@ namespace Serein.NodeFlow.Env { return; } + //var getExp = "@get .DebugSetting.IsEnable"; + //var getExpResult1 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel,out _); + //var setExp = "@set .DebugSetting.IsEnable = false"; + //SerinExpressionEvaluator.Evaluate(setExp, nodeModel,out _); + //var getExpResult2 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); + await flowStarter.StartFlowInSelectNodeAsync(this, nodeModel); } else @@ -424,9 +440,9 @@ namespace Serein.NodeFlow.Env node.ReleaseFlowData(); // 退出时释放对象计数 } } + UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs())); - - OnFlowRunComplete?.Invoke(new FlowEventArgs()); + GC.Collect(); } @@ -435,7 +451,7 @@ namespace Serein.NodeFlow.Env /// 激活全局触发器 /// /// - [AutoSocketHandle] + // [AutoSocketHandle] public void ActivateFlipflopNode(string nodeGuid) { var nodeModel = GuidToModel(nodeGuid); @@ -455,7 +471,7 @@ namespace Serein.NodeFlow.Env /// 关闭全局触发器 /// /// - [AutoSocketHandle] + // [AutoSocketHandle] public void TerminateFlipflopNode(string nodeGuid) { var nodeModel = GuidToModel(nodeGuid); @@ -470,7 +486,7 @@ namespace Serein.NodeFlow.Env /// 获取当前环境信息(远程连接) /// /// - [AutoSocketHandle] + // [AutoSocketHandle] public async Task GetEnvInfoAsync() { Dictionary> LibraryMds = []; @@ -535,7 +551,6 @@ namespace Serein.NodeFlow.Env foreach (var dllPath in dllPaths) { var dllFilePath = Path.GetFullPath(Path.Combine(filePath, dllPath)); - //var dllFilePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(filePath)!, dllPath)); LoadDllNodeInfo(dllFilePath); } @@ -564,7 +579,8 @@ namespace Serein.NodeFlow.Env if (nodeInfo.ChildNodeGuids?.Length > 0) { regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids)); - OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)); + + UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); } else { @@ -583,8 +599,9 @@ namespace Serein.NodeFlow.Env // 节点尚未加载 continue; } + UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid))); // 存在节点 - OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)); + } } // 加载节点 @@ -602,7 +619,8 @@ namespace Serein.NodeFlow.Env } } if (IsContinue) continue; - OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)); + UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position))); + } @@ -633,13 +651,14 @@ namespace Serein.NodeFlow.Env // 遍历当前类型分支的节点(确认连接关系) foreach (var toNode in item.toNodes) { - ConnectNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系 + ConnectNodeAsync(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系 } } } SetStartNode(projectData.StartNode); - OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); + UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs())); + } @@ -684,7 +703,7 @@ namespace Serein.NodeFlow.Env /// 序列化当前项目的依赖信息、节点信息 /// /// - public async Task GetProjectInfoAsync() + public Task GetProjectInfoAsync() { var projectData = new SereinProjectData() { @@ -692,7 +711,7 @@ namespace Serein.NodeFlow.Env Nodes = Nodes.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(), StartNode = Nodes.Values.FirstOrDefault(it => it.IsStart)?.Guid, }; - return projectData; + return Task.FromResult(projectData); } @@ -701,7 +720,7 @@ namespace Serein.NodeFlow.Env /// /// /// - [AutoSocketHandle] + // [AutoSocketHandle] public void LoadDll(string dllPath) { LoadDllNodeInfo(dllPath); @@ -766,11 +785,10 @@ namespace Serein.NodeFlow.Env /// /// /// 如果是表达式节点条件节点,该项为null - public async Task CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo? methodDetailsInfo = null) + public Task CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo? methodDetailsInfo = null) { - - NodeModelBase? nodeModel = null; + NodeModelBase? nodeModel; if (methodDetailsInfo is null) { nodeModel = FlowFunc.CreateNode(this, nodeControlType); // 加载基础节点 @@ -783,7 +801,7 @@ namespace Serein.NodeFlow.Env } else { - return null; + return Task.FromResult(null); } } @@ -792,15 +810,17 @@ namespace Serein.NodeFlow.Env nodeModel.Position = position; // 通知UI更改 - OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position)); + UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position))); + // 因为需要UI先布置了元素,才能通知UI变更特效 // 如果不存在流程起始控件,默认设置为流程起始控件 if (StartNode is null) { SetStartNode(nodeModel); } - return nodeModel.ToInfo(); - + var nodeInfo = nodeModel.ToInfo(); + return Task.FromResult(nodeInfo); +; } @@ -809,10 +829,12 @@ namespace Serein.NodeFlow.Env /// /// /// - public void RemoveNode(string nodeGuid) + public async Task RemoveNodeAsync(string nodeGuid) { var remoteNode = GuidToModel(nodeGuid); - if (remoteNode is null) return; + if (remoteNode is null) + return false; + //if (remoteNode.IsStart) //{ // return; @@ -832,10 +854,11 @@ namespace Serein.NodeFlow.Env NodeModelBase? pNode = pnc.Value[i]; pNode.SuccessorNodes[pCType].Remove(remoteNode); - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(pNode.Guid, + UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(pNode.Guid, remoteNode.Guid, pCType, - NodeConnectChangeEventArgs.ConnectChangeType.Remote)); // 通知UI + NodeConnectChangeEventArgs.ConnectChangeType.Remote))); // 通知UI + } } @@ -847,14 +870,15 @@ namespace Serein.NodeFlow.Env { NodeModelBase? toNode = snc.Value[i]; - RemoteConnect(remoteNode, toNode, connectionType); + await RemoteConnectAsync(remoteNode, toNode, connectionType); } } // 从集合中移除节点 Nodes.Remove(nodeGuid); - OnNodeRemote?.Invoke(new NodeRemoteEventArgs(nodeGuid)); + UIContextOperation?.Invoke(() => OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid))); + return true; } /// @@ -868,10 +892,10 @@ namespace Serein.NodeFlow.Env // 获取起始节点与目标节点 var fromNode = GuidToModel(fromNodeGuid); var toNode = GuidToModel(toNodeGuid); - if (fromNode is null) return false; - if (toNode is null) return false; + if (fromNode is null || toNode is null) return false; + // 开始连接 - return await ConnectNode(fromNode, toNode, connectionType); // 外部调用连接方法 + return await ConnectNodeAsync(fromNode, toNode, connectionType); // 外部调用连接方法 } @@ -882,15 +906,14 @@ namespace Serein.NodeFlow.Env /// 目标节点Guid /// 连接关系 /// - public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) + public async Task RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) { // 获取起始节点与目标节点 var fromNode = GuidToModel(fromNodeGuid); var toNode = GuidToModel(toNodeGuid); - if (fromNode is null) return; - if (toNode is null) return; - RemoteConnect(fromNode, toNode, connectionType); - + if (fromNode is null || toNode is null) return false; + var result = await RemoteConnectAsync(fromNode, toNode, connectionType); + return result; } @@ -958,7 +981,8 @@ namespace Serein.NodeFlow.Env if (nodeModel is null) return; nodeModel.Position.X = x; nodeModel.Position.Y = y; - OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y)); + UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y))); + } /// @@ -978,12 +1002,13 @@ namespace Serein.NodeFlow.Env /// 被中断的目标节点Guid /// 中断级别 /// 操作是否成功 - public async Task SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass) + public Task SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass) { var nodeModel = GuidToModel(nodeGuid); - if (nodeModel is null) return false; + if (nodeModel is null) + return Task.FromResult(false); if (interruptClass == InterruptClass.None) { nodeModel.CancelInterrupt(); @@ -991,10 +1016,11 @@ namespace Serein.NodeFlow.Env else if (interruptClass == InterruptClass.Branch) { nodeModel.DebugSetting.CancelInterruptCallback?.Invoke(); - nodeModel.DebugSetting.GetInterruptTask = () => + nodeModel.DebugSetting.GetInterruptTask = async () => { TriggerInterrupt(nodeGuid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor); - return ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid); + var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid); + return result; }; nodeModel.DebugSetting.CancelInterruptCallback = () => { @@ -1004,11 +1030,16 @@ namespace Serein.NodeFlow.Env } else if (interruptClass == InterruptClass.Global) // 全局……做不了omg { - return false; + return Task.FromResult(false); } - nodeModel.DebugSetting.InterruptClass = interruptClass; - OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass)); - return true; + nodeModel.DebugSetting.InterruptClass = interruptClass; + if (OperatingSystem.IsWindows()) + { + + UIContextOperation?.Invoke(() => OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass))); + } + + return Task.FromResult(true); } @@ -1018,22 +1049,21 @@ namespace Serein.NodeFlow.Env /// 如果是节点,传入Guid;如果是对象,传入类型FullName /// 合法的条件表达式 /// - public async Task AddInterruptExpressionAsync(string key, string expression) + public Task AddInterruptExpressionAsync(string key, string expression) { - if (string.IsNullOrEmpty(expression)) return false; + if (string.IsNullOrEmpty(expression)) return Task.FromResult(false); if (dictMonitorObjExpInterrupt.TryGetValue(key, out var condition)) { condition.Clear(); // 暂时 condition.Add(expression);// 暂时 - return true; } else { var exps = new List(); exps.Add(expression); dictMonitorObjExpInterrupt.TryAdd(key, exps); - return true; } + return Task.FromResult(true); } /// @@ -1073,19 +1103,41 @@ namespace Serein.NodeFlow.Env /// /// /// - public async Task<(bool, string[])> CheckObjMonitorStateAsync(string key) + public Task<(bool, string[])> CheckObjMonitorStateAsync(string key) { if (string.IsNullOrEmpty(key)) - return (false, Array.Empty()); - var isMonitor = dictMonitorObjExpInterrupt.TryGetValue(key, out var exps); - if (exps is null) { - return (isMonitor, Array.Empty()); + var data = (false, Array.Empty()); + return Task.FromResult(data); } else { - return (isMonitor, exps.ToArray()); + var isMonitor = dictMonitorObjExpInterrupt.TryGetValue(key, out var exps); + + if (exps is null) + { + var data = (isMonitor, Array.Empty()); + return Task.FromResult(data); + } + else + { + var data = (isMonitor, exps.ToArray()); + return Task.FromResult(data); + } + + } + + //if (exps is null) + //{ + // var data = (isMonitor, Array.Empty()); + // return Task.FromResult(data); + //} + //else + //{ + // var data = (isMonitor, exps.ToArray()); + // return Task.FromResult(data); + //} } @@ -1140,33 +1192,38 @@ namespace Serein.NodeFlow.Env { var nodeModel = GuidToModel(nodeGuid); if (nodeModel is null) return; - if(NodeValueChangeLogger.Remove((nodeGuid, path, value))) + if (NodeValueChangeLogger.Remove((nodeGuid, path, value))) { // 说明存在过重复的修改 return; } - - Console.WriteLine($"本地环境收到数据更改通知:{value}"); - - var getExp = $"@Get .{path}"; - //Console.WriteLine($"取值表达式:{getExp}"); - var getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); - Console.WriteLine($"原数据 :{getResult}"); - if (getResult.Equals(value)) - { - Console.WriteLine("无须修改" ); - return; - } - - NodeValueChangeLogger.Add((nodeGuid, path, value)); + var setExp = $"@Set .{path} = {value}"; // 生成 set 表达式 + SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); // 更改对应的数据 + + + + //Console.WriteLine($"本地环境收到数据更改通知:{value}"); + //var getExp = $"@Get .{path}"; + ////Console.WriteLine($"取值表达式:{getExp}"); + //var getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); + ////Console.WriteLine($"原数据 :{getResult}"); + //if (getResult.Equals(value)) + //{ + // Console.WriteLine("无须修改"); + // return; + //} + + + //NodeValueChangeLogger.Add((nodeGuid, path, value)); + + + //var setExp = $"@Set .{path} = {value}"; + ////Console.WriteLine($"设值表达式:{setExp}"); + //SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); + //getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); + //Console.WriteLine($"新数据 :{getResult}"); - - var setExp = $"@Set .{path} = {value}"; - //Console.WriteLine($"设值表达式:{setExp}"); - SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); - getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); - Console.WriteLine($"新数据 :{getResult}"); } @@ -1226,8 +1283,13 @@ namespace Serein.NodeFlow.Env } types.AddRange(kv.Value); } - var mdInfo = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息 - OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfo)); // 通知UI创建dll面板显示 + var mdInfos = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息 + + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos))); // 通知UI创建dll面板显示 + + } } } @@ -1239,16 +1301,25 @@ namespace Serein.NodeFlow.Env /// 目标节点Model /// 连接关系 /// - private void RemoteConnect(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType) + private async Task RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType) { fromNode.SuccessorNodes[connectionType].Remove(toNode); toNode.PreviousNodes[connectionType].Remove(fromNode); - // 通知UI - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, + + if (OperatingSystem.IsWindows()) + { + await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, toNode.Guid, connectionType, - NodeConnectChangeEventArgs.ConnectChangeType.Remote)); + NodeConnectChangeEventArgs.ConnectChangeType.Remote))); + } + //else if (OperatingSystem.IsLinux()) + //{ + + //} + + return true; } private (NodeLibrary?, Dictionary>, List) LoadAssembly(string dllPath) @@ -1377,11 +1448,11 @@ namespace Serein.NodeFlow.Env /// 起始节点 /// 目标节点 /// 连接关系 - private Task ConnectNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType) + private async Task ConnectNodeAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType) { if (fromNode is null || toNode is null || fromNode == toNode) { - return Task.FromResult(false); + return false; } var ToExistOnFrom = true; @@ -1428,23 +1499,26 @@ namespace Serein.NodeFlow.Env } } } - if (!isPass) - { - return Task.FromResult(false); - } - else + if (isPass) { fromNode.SuccessorNodes[connectionType].Add(toNode); // 添加到起始节点的子分支 toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支 - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, - toNode.Guid, - connectionType, - NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, + toNode.Guid, + connectionType, + NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI + } - return Task.FromResult(true); + return true; + } + else + { + return false; } - + } /// @@ -1456,7 +1530,11 @@ namespace Serein.NodeFlow.Env { var oldNodeGuid = StartNode?.Guid; StartNode = newStartNode; - OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid)); + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid))); + } + } /// @@ -1465,7 +1543,11 @@ namespace Serein.NodeFlow.Env /// private void Output(string msg) { - OnEnvOut?.Invoke(msg); + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg)); + } + } #endregion @@ -1478,7 +1560,11 @@ namespace Serein.NodeFlow.Env /// public void NodeLocated(string nodeGuid) { - OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)); + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid))); + } + } #endregion diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs index 3624612..05255ce 100644 --- a/NodeFlow/Env/FlowEnvironmentDecorator.cs +++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs @@ -67,6 +67,7 @@ namespace Serein.NodeFlow.Env /// public IFlowEnvironment CurrentEnv { get => currentFlowEnvironment; } + public UIContextOperation UIContextOperation => currentFlowEnvironment.UIContextOperation; public ISereinIOC IOC => (ISereinIOC)currentFlowEnvironment; @@ -103,10 +104,10 @@ namespace Serein.NodeFlow.Env remove { currentFlowEnvironment.OnNodeCreate -= value; } } - public event NodeRemoteHandler OnNodeRemote + public event NodeRemoveHandler OnNodeRemove { - add { currentFlowEnvironment.OnNodeRemote += value; } - remove { currentFlowEnvironment.OnNodeRemote -= value; } + add { currentFlowEnvironment.OnNodeRemove += value; } + remove { currentFlowEnvironment.OnNodeRemove -= value; } } public event StartNodeChangeHandler OnStartNodeChange @@ -198,7 +199,8 @@ namespace Serein.NodeFlow.Env (var isConnect, var remoteEnvControl) = await currentFlowEnvironment.ConnectRemoteEnv(addres, port, token); if (isConnect) { - remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteEnvControl); + + remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteEnvControl, this.UIContextOperation); currentFlowEnvironment = remoteFlowEnvironment; } return (isConnect, remoteEnvControl); @@ -277,14 +279,14 @@ namespace Serein.NodeFlow.Env return currentFlowEnvironment.RemoteDll(assemblyFullName); } - public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) + public async Task RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) { - currentFlowEnvironment.RemoveConnect(fromNodeGuid, toNodeGuid, connectionType); + return await currentFlowEnvironment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connectionType); } - public void RemoveNode(string nodeGuid) + public async Task RemoveNodeAsync(string nodeGuid) { - currentFlowEnvironment.RemoveNode(nodeGuid); + return await currentFlowEnvironment.RemoveNodeAsync(nodeGuid); } diff --git a/NodeFlow/Env/MsgControllerOfClient.cs b/NodeFlow/Env/MsgControllerOfClient.cs index b035cce..8eaeaec 100644 --- a/NodeFlow/Env/MsgControllerOfClient.cs +++ b/NodeFlow/Env/MsgControllerOfClient.cs @@ -1,4 +1,5 @@ -using Serein.Library; +using Newtonsoft.Json; +using Serein.Library; using Serein.Library.Network.WebSocketCommunication; using Serein.Library.Utils; using System; @@ -35,13 +36,14 @@ namespace Serein.NodeFlow.Env /// /// /// 超时触发 - public async Task SendAsync(string signal, object? senddata = null, int debounceTimeInMs = 100) + public async Task SendAsync(string signal, object? sendData = null, int overtimeInMs = 100) { - if (!DebounceHelper.CanExecute(signal, debounceTimeInMs)) + //Console.WriteLine($"指令[{signal}],value:{JsonConvert.SerializeObject(sendData)}"); + if (!DebounceHelper.CanExecute(signal, overtimeInMs)) { return; } - await SendCommandAsync.Invoke(signal, senddata); + await SendCommandAsync.Invoke(signal, sendData); } /// @@ -49,32 +51,32 @@ namespace Serein.NodeFlow.Env /// /// /// 超时触发 - public async Task SendAndWaitDataAsync(string signal, object? senddata = null, int debounceTimeInMs = 50) + public async Task SendAndWaitDataAsync(string signal, object? sendData = null, int overtimeInMs = 50) { - _ = SendCommandAsync.Invoke(signal, senddata); + //Console.WriteLine($"指令[{signal}],value:{JsonConvert.SerializeObject(sendData)}"); + _ = SendCommandAsync.Invoke(signal, sendData); return await remoteFlowEnvironment.WaitData(signal); -#if DEBUG - if (DebounceHelper.CanExecute(signal, debounceTimeInMs)) - { - _ = SendCommandAsync.Invoke(signal, senddata); - return await remoteFlowEnvironment.WaitData(signal); + //if (DebounceHelper.CanExecute(signal, overtimeInMs)) + //{ + // _ = SendCommandAsync.Invoke(signal, sendData); + // return await remoteFlowEnvironment.WaitData(signal); + + // //(var type, var result) = await remoteFlowEnvironment.WaitDataWithTimeoutAsync(signal, TimeSpan.FromSeconds(150)); + // //if (type == TriggerType.Overtime) + // //{ + // // throw new NotImplementedException("超时触发"); + // //} + // //else + // //{ + // // return result; + // //} + //} + //else + //{ + // return default; + //} - //(var type, var result) = await remoteFlowEnvironment.WaitDataWithTimeoutAsync(signal, TimeSpan.FromSeconds(150)); - //if (type == TriggerType.Overtime) - //{ - // throw new NotImplementedException("超时触发"); - //} - //else - //{ - // return result; - //} - } - else - { - return default; - } -#endif } @@ -92,11 +94,6 @@ namespace Serein.NodeFlow.Env } - [AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode)] - public void AddInterruptExpression([UseMsgData] NodeInfo nodeInfo) - { - remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.CreateNode, nodeInfo); - } /// @@ -122,6 +119,35 @@ namespace Serein.NodeFlow.Env } + + [AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode)] + public void CreateNode([UseMsgData] NodeInfo nodeInfo) + { + remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.CreateNode, nodeInfo); + } + + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode)] + public void RemoveNode(bool state) + { + remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.RemoveNode, state); + } + + + [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)] + public void ConnectNode(bool state) + { + remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.ConnectNode, state); + } + + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)] + public void RemoveConnect(bool state) + { + remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.RemoveConnect, state); + } + + + + #endregion } diff --git a/NodeFlow/Env/MsgControllerOfServer.cs b/NodeFlow/Env/MsgControllerOfServer.cs index 72c18e4..c838bf6 100644 --- a/NodeFlow/Env/MsgControllerOfServer.cs +++ b/NodeFlow/Env/MsgControllerOfServer.cs @@ -184,13 +184,14 @@ namespace Serein.NodeFlow.Env /// /// 从远程环境运行选定的节点 /// - /// + /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlowInSelectNode)] - private async Task StartAsyncInSelectNode(string startNodeGuid) + private async Task StartAsyncInSelectNode(string nodeGuid) { - await environment.StartAsyncInSelectNode(startNodeGuid); + await environment.StartAsyncInSelectNode(nodeGuid); } + /// /// 结束流程 /// @@ -321,9 +322,15 @@ namespace Serein.NodeFlow.Env /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode)] - public void RemoveNode(string nodeGuid) + public async Task RemoveNode(string nodeGuid) { - environment.RemoveNode(nodeGuid); + //var result = environment.RemoveNodeAsync(nodeGuid).GetAwaiter().GetResult(); + var result = await environment.RemoveNodeAsync(nodeGuid); + //return result; + return new + { + state = result + }; } @@ -334,9 +341,21 @@ namespace Serein.NodeFlow.Env /// 目标节点 /// 连接关系 [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)] - public void ConnectNode(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) + public async Task ConnectNode(string fromNodeGuid, string toNodeGuid, string connectionType) { - environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, connectionType); + if (!EnumHelper.TryConvertEnum(connectionType, out var tmpConnectionType)) + { + return new + { + state = false + }; + } + //environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType); + var result = await environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType); + return new + { + state = result + }; } /// @@ -347,9 +366,21 @@ namespace Serein.NodeFlow.Env /// 连接关系 /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)] - public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) + public async Task RemoveConnect(string fromNodeGuid, string toNodeGuid, string connectionType) { - environment.RemoveConnect(fromNodeGuid, toNodeGuid, connectionType); + if (!EnumHelper.TryConvertEnum(connectionType, out var tmpConnectionType)) + { + return new + { + state = false + }; + } + + var result = await environment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, tmpConnectionType); + return new + { + state = result + }; } /// diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs index 17d6581..a15ee0a 100644 --- a/NodeFlow/Env/RemoteFlowEnvironment.cs +++ b/NodeFlow/Env/RemoteFlowEnvironment.cs @@ -18,9 +18,11 @@ namespace Serein.NodeFlow.Env /// /// 连接到远程环境后切换到的环境接口实现 /// - /// 连接到远程环境的客户端 - public RemoteFlowEnvironment(RemoteEnvControl RemoteEnvControl) + /// 连接到远程环境后,本地环境自动切换到对应的环境实体 + /// 远程环境下需要操作UI线程时,所提供的线程上下文封装工具 + public RemoteFlowEnvironment(RemoteEnvControl RemoteEnvControl, UIContextOperation uIContextOperation) { + this.UIContextOperation = uIContextOperation; remoteEnvControl = RemoteEnvControl; msgClient = new MsgControllerOfClient(this, RemoteEnvControl.SendAsync); RemoteEnvControl.EnvClient.MsgHandleHelper.AddModule(msgClient, (ex, send) => @@ -34,9 +36,6 @@ namespace Serein.NodeFlow.Env private readonly MsgControllerOfClient msgClient; private readonly ConcurrentDictionary MethodDetailss = []; - - - /// /// 环境加载的节点集合 /// Node Guid - Node Model @@ -47,7 +46,7 @@ namespace Serein.NodeFlow.Env public event ProjectLoadedHandler OnProjectLoaded; public event NodeConnectChangeHandler OnNodeConnectChange; public event NodeCreateHandler OnNodeCreate; - public event NodeRemoteHandler OnNodeRemote; + public event NodeRemoveHandler OnNodeRemove; public event StartNodeChangeHandler OnStartNodeChange; public event FlowRunCompleteHandler OnFlowRunComplete; public event MonitorObjectChangeHandler OnMonitorObjectChange; @@ -72,7 +71,7 @@ namespace Serein.NodeFlow.Env public RunState FlipFlopState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public IFlowEnvironment CurrentEnv => this; - + public UIContextOperation UIContextOperation { get; } public void SetConsoleOut() { var logTextWriter = new LogTextWriter(msg => @@ -118,8 +117,8 @@ namespace Serein.NodeFlow.Env FilePath = "Remote", }; var mdInfos = lib.Mds.ToList(); - OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos)); // 通知UI创建dll面板显示 - + //OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos)); // 通知UI创建dll面板显示 + UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos))); // 通知UI创建dll面板显示 foreach (var mdInfo in mdInfos) { MethodDetailss.TryAdd(mdInfo.MethodName, new MethodDetails(mdInfo)); // 从DLL读取时生成元数据 @@ -160,7 +159,8 @@ namespace Serein.NodeFlow.Env if (nodeInfo.ChildNodeGuids?.Length > 0) { regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids)); - OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)); + UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); + //OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)); } else { @@ -181,7 +181,8 @@ namespace Serein.NodeFlow.Env continue; } // 存在节点 - OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)); + //OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)); + UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid))); } } @@ -200,7 +201,8 @@ namespace Serein.NodeFlow.Env } } if (IsContinue) continue; - OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)); + //OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)); + UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position))); } @@ -208,7 +210,7 @@ namespace Serein.NodeFlow.Env // 确定节点之间的连接关系 _ = Task.Run(async () => { - await Task.Delay(100); + await Task.Delay(250); foreach (var nodeInfo in projectData.Nodes) { if (!Nodes.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode)) @@ -234,12 +236,16 @@ namespace Serein.NodeFlow.Env // 遍历当前类型分支的节点(确认连接关系) foreach (var toNode in item.toNodes) { - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, - toNode.Guid, - item.connectionType, - NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI创建节点 - //ConnectNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系 + UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, + toNode.Guid, + item.connectionType, + NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点 + //OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, + // toNode.Guid, + // item.connectionType, + // NodeConnectChangeEventArgs.ConnectChangeType.Create)); // + } } } @@ -251,7 +257,7 @@ namespace Serein.NodeFlow.Env } private bool TryAddNode(NodeModelBase nodeModel) { - nodeModel.Guid ??= Guid.NewGuid().ToString(); + //nodeModel.Guid ??= Guid.NewGuid().ToString(); Nodes[nodeModel.Guid] = nodeModel; // 如果是触发器,则需要添加到专属集合中 @@ -375,7 +381,7 @@ namespace Serein.NodeFlow.Env public async Task StartAsyncInSelectNode(string startNodeGuid) { - await msgClient.SendAsync(EnvMsgTheme.StartFlowInSelectNode, new + _ = msgClient.SendAsync(EnvMsgTheme.StartFlowInSelectNode, new { nodeGuid = startNodeGuid }); @@ -404,22 +410,24 @@ namespace Serein.NodeFlow.Env { nodeGuid }); + // UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid))); } public async Task ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) { - //_ = RemoteEnv.SendAsync(EnvMsgTheme.ConnectNode, new - //{ - // fromNodeGuid, - // toNodeGuid, - // connectionType = connectionType.ToString(), - //}); var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.ConnectNode, new { fromNodeGuid, toNodeGuid, connectionType = connectionType.ToString(), }); + if (result) + { + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + toNodeGuid, + connectionType, + NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI + } return result; } @@ -434,30 +442,47 @@ namespace Serein.NodeFlow.Env MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息 var nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 远程环境下加载节点 - TryAddNode(nodeModel); nodeModel.LoadInfo(nodeInfo); + TryAddNode(nodeModel); // 通知UI更改 OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position)); return nodeInfo; } - public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) + public async Task RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) { - _ = msgClient.SendAsync(EnvMsgTheme.RemoveConnect, new + var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveConnect, new { fromNodeGuid, toNodeGuid, connectionType = connectionType.ToString(), }); + if (result) + { + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + toNodeGuid, + connectionType, + NodeConnectChangeEventArgs.ConnectChangeType.Remote)); + } + return result; } - public void RemoveNode(string nodeGuid) + public async Task RemoveNodeAsync(string nodeGuid) { - _ = msgClient.SendAsync(EnvMsgTheme.RemoveNode, new + var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveNode, new { nodeGuid }); + if (result) + { + OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid)); + } + else + { + Console.WriteLine("删除失败"); + } + return result; } public void ActivateFlipflopNode(string nodeGuid) @@ -559,14 +584,14 @@ namespace Serein.NodeFlow.Env public void NodeLocated(string nodeGuid) { - Console.WriteLine("远程环境尚未实现的接口:NodeLocated"); + //Console.WriteLine("远程环境尚未实现的接口:NodeLocated"); + UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid))); } public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) { - //Console.WriteLine($"通知远程环境修改节点数据:{nodeGuid},name:{path},value:{value}"); - _ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new + _ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new { nodeGuid = nodeGuid, path = path, diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index 98e37f7..d74a63c 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -3,7 +3,6 @@ using Serein.Library.Api; using Serein.Library.Core.NodeFlow; using Serein.Library.Network.WebSocketCommunication; using Serein.Library.Web; -using Serein.Library; using Serein.NodeFlow.Env; using Serein.NodeFlow.Model; using System.Collections.Concurrent; @@ -284,6 +283,7 @@ namespace Serein.NodeFlow finally { env.FlowState = RunState.Completion; + Console.WriteLine($"流程运行完毕{Environment.NewLine}");; } #endregion } diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 5614056..881f2d1 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -1,7 +1,7 @@  - 1.0.14 + 1.0.16 net8.0 enable enable diff --git a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs index 887212c..0e4cbbf 100644 --- a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs +++ b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs @@ -10,7 +10,7 @@ using System.Text; using System.Threading; -namespace Serein.Library.MyGenerator +namespace Serein.Library.NodeGenerator { @@ -20,6 +20,7 @@ namespace Serein.Library.MyGenerator [Generator] public class MyPropertyGenerator : IIncrementalGenerator { + /// /// 初始化生成器,定义需要执行的生成逻辑。 /// @@ -83,6 +84,17 @@ namespace Serein.Library.MyGenerator } }); } + private int myProperty; + + public int MyProperty { get => myProperty; set + { + if(myProperty == null) + { + + myProperty = value; + } + } } + @@ -103,20 +115,28 @@ namespace Serein.Library.MyGenerator // 生成命名空间和类的开始部分 sb.AppendLine($"using System;"); + sb.AppendLine($"using System.Threading;"); + sb.AppendLine($"using System.Threading.Tasks;"); + sb.AppendLine($"using System.Collections.Concurrent;"); sb.AppendLine($"using Serein.Library;"); sb.AppendLine($"using Serein.Library.Api;"); + sb.AppendLine($"using static Serein.Library.Utils.ChannelFlowInterrupt;"); sb.AppendLine($""); sb.AppendLine($"namespace {namespaceName}"); sb.AppendLine("{"); - sb.AppendLine($" public partial class {className}"); + sb.AppendLine($" public partial class {className} : System.ComponentModel.INotifyPropertyChanged"); sb.AppendLine(" {"); var path = classInfo["AutoPropertyAttribute"]["ValuePath"]; + + // + // + // "ParameterDetails"; // "MethodDetails"; - + try @@ -130,35 +150,36 @@ namespace Serein.Library.MyGenerator continue; } - var leadingTrivia = field.GetLeadingTrivia().InsertSummaryComment("(此属性由源生成器生成)").ToString(); // 获取注释 + var leadingTrivia = field.GetLeadingTrivia().InsertSummaryComment("(此属性为自动生成)").ToString(); // 获取注释 var fieldName = field.Declaration.Variables.First().Identifier.Text; // 获取字段名称 var fieldType = field.Declaration.Type.ToString(); // 获取字段类型 var propertyName = field.ToPropertyName(); // 转为合适的属性名称 var attributeInfo = fieldKV.Value; // 缓存的特性信息 - //if (!attributeInfo.TryGetValue("PropertyInfo",out var tmp) || tmp.Count == 0) - //{ - // continue; - //} - + var isProtection = attributeInfo.Search("PropertyInfo", "IsProtection", "true"); // 是否为保护字段 + // 生成 getter / setter sb.AppendLine(leadingTrivia); sb.AppendLine($" public {fieldType} {propertyName}"); sb.AppendLine(" {"); - sb.AppendLine($" get => {fieldName};"); + sb.AppendLine($" get => {fieldName};"); // getter方法 sb.AppendLine(" set"); sb.AppendLine(" {"); - sb.AppendLine($" if ({fieldName} != value)"); - sb.AppendLine(" {"); + sb.AppendLine($" if ({fieldName} {(isProtection ? "== default" : "!= value")})"); // 非保护的Setter + sb.AppendLine(" {"); if (attributeInfo.Search("PropertyInfo", "IsPrint", "true")) // 是否打印 { sb.AddCode(5, $"Console.WriteLine({fieldName});"); } if (attributeInfo.Search("PropertyInfo", "IsNotification", "true")) // 是否通知 { - if (classInfo.ExitsPath("MethodDetails")) + if (classInfo.ExitsPath("NodeModelBase")) + { + sb.AddCode(5, $"this.env?.NotificationNodeValueChangeAsync(this.Guid, .nameof({propertyName}), value);"); + } + else if (classInfo.ExitsPath("MethodDetails")) { sb.AddCode(5, $"nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"MethodDetails.\"+nameof({propertyName}), value);"); } @@ -166,26 +187,53 @@ namespace Serein.Library.MyGenerator { sb.AddCode(5, "nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"MethodDetails.ParameterDetailss[\"+$\"{Index}\"+\"]." + $"\"+nameof({propertyName}),value);"); } - + else if (classInfo.ExitsPath("NodeDebugSetting")) + { + sb.AddCode(5, $"nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"DebugSetting.\"+nameof({propertyName}), value);"); + } } - sb.AppendLine($" {fieldName} = value;"); + sb.AppendLine($" {fieldName} = value;"); + sb.AppendLine($" OnPropertyChanged(); // 先更改属性,然后通知属性发生改变了"); sb.AppendLine(" }"); sb.AppendLine(" }"); - sb.AppendLine(" }"); + sb.AppendLine(" }"); // 属性的结尾大括号 } + + + sb.AppendLine(" /// "); + sb.AppendLine(" /// 略"); + sb.AppendLine(" /// 此事件为自动生成"); + sb.AppendLine(" /// "); + sb.AppendLine(" public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;"); + + sb.AppendLine(" /// "); + sb.AppendLine(" /// 略"); + sb.AppendLine(" /// 此方法为自动生成"); + sb.AppendLine(" /// "); + sb.AppendLine(" /// "); + sb.AppendLine(" "); + sb.AppendLine(" protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)"); + sb.AppendLine(" {"); + //sb.AppendLine(" Console.WriteLine(\"测试:\"+ propertyName);"); + sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));"); + sb.AppendLine(" }"); + + } finally { // 生成类的结束部分 - sb.AppendLine(" }"); - sb.AppendLine("}"); + sb.AppendLine(" }"); // 类的结尾大括号 + sb.AppendLine("}"); // 命名空间的结尾大括号 } - - return sb.ToString(); // 返回生成的代码 } - + + + + + /// /// 获取类所在的命名空间。 /// @@ -198,7 +246,19 @@ namespace Serein.Library.MyGenerator return namespaceDeclaration?.Name.ToString() ?? "GlobalNamespace"; } - + + + private void SetterIsProtection() + { + + } + private void SetterNotIsProtection() + { + + } + + + } diff --git a/Serein.Library.MyGenerator/Serein.Library.MyGenerator.csproj b/Serein.Library.MyGenerator/Serein.Library.MyGenerator.csproj deleted file mode 100644 index f9f1199..0000000 --- a/Serein.Library.MyGenerator/Serein.Library.MyGenerator.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netstandard2.0 - true - - - - - - - - diff --git a/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj new file mode 100644 index 0000000..d648318 --- /dev/null +++ b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj @@ -0,0 +1,38 @@ + + + + netstandard2.0 + true + D:\Project\C#\DynamicControl\SereinFlow\.Output + True + SereinFow + 基础依赖必须添加项,用于生成NodeModel部分的依赖实体代码 + README.md + https://github.com/fhhyyp/serein-flow + MIT + True + true + + + + + + + + + + True + \ + + + True + \ + + + diff --git a/SereinFlow.sln b/SereinFlow.sln index 7cc108a..92ac60f 100644 --- a/SereinFlow.sln +++ b/SereinFlow.sln @@ -24,7 +24,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Extend.RemoteControl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.FlowStartTool", "FlowStartTool\Serein.FlowStartTool.csproj", "{38D0FA92-5139-4616-A41E-8186AA4C1532}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.MyGenerator", "Serein.Library.MyGenerator\Serein.Library.MyGenerator.csproj", "{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.NodeGenerator", "Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj", "{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/WorkBench/MainWindow.xaml b/WorkBench/MainWindow.xaml index c59145c..25379ba 100644 --- a/WorkBench/MainWindow.xaml +++ b/WorkBench/MainWindow.xaml @@ -43,7 +43,7 @@ - + diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index 2599311..e956b7a 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -186,7 +186,7 @@ namespace Serein.Workbench EnvDecorator.OnStartNodeChange += FlowEnvironment_StartNodeChangeEvent; EnvDecorator.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt; EnvDecorator.OnNodeCreate += FlowEnvironment_NodeCreateEvent; - EnvDecorator.OnNodeRemote += FlowEnvironment_NodeRemoteEvent; + EnvDecorator.OnNodeRemove += FlowEnvironment_NodeRemoteEvent; EnvDecorator.OnFlowRunComplete += FlowEnvironment_OnFlowRunComplete; @@ -213,7 +213,7 @@ namespace Serein.Workbench EnvDecorator.OnStartNodeChange -= FlowEnvironment_StartNodeChangeEvent; EnvDecorator.OnNodeConnectChange -= FlowEnvironment_NodeConnectChangeEvemt; EnvDecorator.OnNodeCreate -= FlowEnvironment_NodeCreateEvent; - EnvDecorator.OnNodeRemote -= FlowEnvironment_NodeRemoteEvent; + EnvDecorator.OnNodeRemove -= FlowEnvironment_NodeRemoteEvent; EnvDecorator.OnFlowRunComplete -= FlowEnvironment_OnFlowRunComplete; @@ -312,47 +312,45 @@ namespace Serein.Workbench /// private void FlowEnvironment_DllLoadEvent(LoadDllEventArgs eventArgs) { - this.Dispatcher.Invoke(() => { - NodeLibrary nodeLibrary = eventArgs.NodeLibrary; - List methodDetailss = eventArgs.MethodDetailss; + NodeLibrary nodeLibrary = eventArgs.NodeLibrary; + List methodDetailss = eventArgs.MethodDetailss; - var dllControl = new DllControl(nodeLibrary); + var dllControl = new DllControl(nodeLibrary); - foreach (var methodDetailsInfo in methodDetailss) + foreach (var methodDetailsInfo in methodDetailss) + { + if (!EnumHelper.TryConvertEnum(methodDetailsInfo.NodeType, out var nodeType)) { - if(!EnumHelper.TryConvertEnum(methodDetailsInfo.NodeType, out var nodeType)) - { - continue; - } - switch (nodeType) - { - case Library.NodeType.Action: - dllControl.AddAction(methodDetailsInfo); // 添加动作类型到控件 - break; - case Library.NodeType.Flipflop: - dllControl.AddFlipflop(methodDetailsInfo); // 添加触发器方法到控件 - break; - } - + continue; } - var menu = new ContextMenu(); - menu.Items.Add(CreateMenuItem("卸载", (s,e) => + switch (nodeType) { - if (this.EnvDecorator.RemoteDll(nodeLibrary.FullName)) - { - DllStackPanel.Children.Remove(dllControl); - } - else - { - Console.WriteLine("卸载失败"); - } - })); + case Library.NodeType.Action: + dllControl.AddAction(methodDetailsInfo); // 添加动作类型到控件 + break; + case Library.NodeType.Flipflop: + dllControl.AddFlipflop(methodDetailsInfo); // 添加触发器方法到控件 + break; + } - dllControl.ContextMenu = menu; + } + var menu = new ContextMenu(); + menu.Items.Add(CreateMenuItem("卸载", (s, e) => + { + if (this.EnvDecorator.RemoteDll(nodeLibrary.FullName)) + { + DllStackPanel.Children.Remove(dllControl); + } + else + { + Console.WriteLine("卸载失败"); + } + })); + + dllControl.ContextMenu = menu; + + DllStackPanel.Children.Add(dllControl); // 将控件添加到界面上显示 - DllStackPanel.Children.Add(dllControl); // 将控件添加到界面上显示 - }); - } /// @@ -370,7 +368,6 @@ namespace Serein.Workbench } ConnectionType connectionType = eventArgs.ConnectionType; - Action? action = null; if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接 { // 添加连接 @@ -381,59 +378,48 @@ namespace Serein.Workbench Type = connectionType }; if (toNode is FlipflopNodeControl flipflopControl - && flipflopControl?.ViewModel?.Node is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器 + && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器 { NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除 } - action = () => { - BsControl.Draw(FlowChartCanvas, connection); // 添加贝塞尔曲线显示 - ConfigureLineContextMenu(connection); // 设置连接右键事件 - Connections.Add(connection); - EndConnection(); - connection.Refresh(); - //UpdateConnections(fromNode); - // connection.ArrowPath?.InvalidateVisual(); - // connection.BezierPath?.InvalidateVisual(); - }; - + BsControl.Draw(FlowChartCanvas, connection); // 添加贝塞尔曲线显示 + ConfigureLineContextMenu(connection); // 设置连接右键事件 + Connections.Add(connection); + EndConnection(); + connection.Refresh(); + //UpdateConnections(fromNode); + // connection.ArrowPath?.InvalidateVisual(); + // connection.BezierPath?.InvalidateVisual(); } else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remote) // 移除连接 { // 需要移除连接 - var removeConnections = Connections.Where(c => c.Start.ViewModel.Node.Guid.Equals(fromNodeGuid) - && c.End.ViewModel.Node.Guid.Equals(toNodeGuid)) + var removeConnections = Connections.Where(c => c.Start.ViewModel.NodeModel.Guid.Equals(fromNodeGuid) + && c.End.ViewModel.NodeModel.Guid.Equals(toNodeGuid)) .ToList(); - - action = () => + + foreach (var connection in removeConnections) { - foreach (var connection in removeConnections) - { - connection.RemoveFromCanvas(); - Connections.Remove(connection); - JudgmentFlipFlopNode(connection.End); // 连接关系变更时判断 - } - }; + connection.RemoveFromCanvas(); + Connections.Remove(connection); + JudgmentFlipFlopNode(connection.End); // 连接关系变更时判断 + } } - this.Dispatcher.Invoke(() => - { - action?.Invoke(); - }); - - + } /// /// 节点移除事件 /// /// - private void FlowEnvironment_NodeRemoteEvent(NodeRemoteEventArgs eventArgs) + private void FlowEnvironment_NodeRemoteEvent(NodeRemoveEventArgs eventArgs) { var nodeGuid = eventArgs.NodeGuid; if (!TryGetControl(nodeGuid, out var nodeControl)) @@ -449,24 +435,18 @@ namespace Serein.Workbench selectNodeControls.Remove(nodeControl); } } - #region 节点树视图 - - #endregion - this.Dispatcher.Invoke(() => + if (nodeControl is FlipflopNodeControl flipflopControl) // 判断是否为触发器 { - if (nodeControl is FlipflopNodeControl flipflopControl) // 判断是否为触发器 + var node = flipflopControl?.ViewModel?.NodeModel; + if (node is not null) { - var node = flipflopControl?.ViewModel?.Node; - if (node is not null) - { - NodeTreeViewer.RemoteGlobalFlipFlop(node); // 从全局触发器树树视图中移除 - } + NodeTreeViewer.RemoteGlobalFlipFlop(node); // 从全局触发器树树视图中移除 } + } - FlowChartCanvas.Children.Remove(nodeControl); - NodeControls.Remove(nodeControl.ViewModel.Node.Guid); - }); + FlowChartCanvas.Children.Remove(nodeControl); + NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid); } /// @@ -476,61 +456,57 @@ namespace Serein.Workbench /// private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs) { - this.Dispatcher.Invoke(() => + if (eventArgs.NodeModel is not NodeModelBase nodeModelBase) { - if (eventArgs.NodeModel is not NodeModelBase nodeModelBase) + return; + } + + // MethodDetails methodDetailss = eventArgs.MethodDetailss; + PositionOfUI position = eventArgs.Position; + + // 创建对应控件 + NodeControlBase? nodeControl = nodeModelBase.ControlType switch + { + NodeControlType.Action => CreateNodeControl(nodeModelBase), //typeof(ActionNodeControl), + NodeControlType.Flipflop => CreateNodeControl(nodeModelBase), + NodeControlType.ExpCondition => CreateNodeControl(nodeModelBase), + NodeControlType.ExpOp => CreateNodeControl(nodeModelBase), + NodeControlType.ConditionRegion => CreateNodeControl(nodeModelBase), + _ => null, + }; + if (nodeControl is null) + { + return; + } + NodeControls.TryAdd(nodeModelBase.Guid, nodeControl); + if (eventArgs.IsAddInRegion && NodeControls.TryGetValue(eventArgs.RegeionGuid, out NodeControlBase? regionControl)) + { + if (regionControl is not null) { - return; + TryPlaceNodeInRegion(regionControl, nodeControl); } - - // MethodDetails methodDetailss = eventArgs.MethodDetailss; - PositionOfUI position = eventArgs.Position; - - // 创建对应控件 - NodeControlBase? nodeControl = nodeModelBase.ControlType switch + return; + } + else + { + if (!TryPlaceNodeInRegion(nodeControl, position)) // 将节点放置在区域中 { - NodeControlType.Action => CreateNodeControl(nodeModelBase), //typeof(ActionNodeControl), - NodeControlType.Flipflop => CreateNodeControl(nodeModelBase), - NodeControlType.ExpCondition => CreateNodeControl(nodeModelBase), - NodeControlType.ExpOp => CreateNodeControl(nodeModelBase), - NodeControlType.ConditionRegion => CreateNodeControl(nodeModelBase), - _ => null, - }; - if (nodeControl is null) - { - return; + PlaceNodeOnCanvas(nodeControl, position.X, position.Y); // 将节点放置在画布上 } - NodeControls.TryAdd(nodeModelBase.Guid, nodeControl); - if (eventArgs.IsAddInRegion && NodeControls.TryGetValue(eventArgs.RegeionGuid, out NodeControlBase? regionControl)) + } + + + #region 节点树视图 + if (nodeModelBase.ControlType == NodeControlType.Flipflop) + { + var node = nodeControl?.ViewModel?.NodeModel; + if (node is not null) { - if (regionControl is not null) - { - TryPlaceNodeInRegion(regionControl, nodeControl); - } - return; - } - else - { - if (!TryPlaceNodeInRegion(nodeControl, position)) // 将节点放置在区域中 - { - PlaceNodeOnCanvas(nodeControl, position.X, position.Y); // 将节点放置在画布上 - } + NodeTreeViewer.AddGlobalFlipFlop(EnvDecorator, node); // 新增的触发器节点添加到全局触发器 } + } + #endregion - - #region 节点树视图 - if (nodeModelBase.ControlType == NodeControlType.Flipflop) - { - var node = nodeControl?.ViewModel?.Node; - if(node is not null) - { - NodeTreeViewer.AddGlobalFlipFlop(EnvDecorator, node); // 新增的触发器节点添加到全局触发器 - } - } - #endregion - - - }); } /// @@ -540,26 +516,23 @@ namespace Serein.Workbench /// private void FlowEnvironment_StartNodeChangeEvent(StartNodeChangeEventArgs eventArgs) { - this.Dispatcher.Invoke(() => + string oldNodeGuid = eventArgs.OldNodeGuid; + string newNodeGuid = eventArgs.NewNodeGuid; + if (!TryGetControl(newNodeGuid, out var newStartNodeControl)) return; + if (!string.IsNullOrEmpty(oldNodeGuid)) { - string oldNodeGuid = eventArgs.OldNodeGuid; - string newNodeGuid = eventArgs.NewNodeGuid; - if (!TryGetControl(newNodeGuid, out var newStartNodeControl)) return; - if (!string.IsNullOrEmpty(oldNodeGuid)) - { - if (!TryGetControl(oldNodeGuid, out var oldStartNodeControl)) return; - oldStartNodeControl.BorderBrush = Brushes.Black; - oldStartNodeControl.BorderThickness = new Thickness(0); - } + if (!TryGetControl(oldNodeGuid, out var oldStartNodeControl)) return; + oldStartNodeControl.BorderBrush = Brushes.Black; + oldStartNodeControl.BorderThickness = new Thickness(0); + } - newStartNodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")); - newStartNodeControl.BorderThickness = new Thickness(2); - var node = newStartNodeControl?.ViewModel?.Node; - if (node is not null) - { - NodeTreeViewer.LoadNodeTreeOfStartNode(EnvDecorator, node); - } - }); + newStartNodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")); + newStartNodeControl.BorderThickness = new Thickness(2); + var node = newStartNodeControl?.ViewModel?.NodeModel; + if (node is not null) + { + NodeTreeViewer.LoadNodeTreeOfStartNode(EnvDecorator, node); + } } @@ -578,24 +551,21 @@ namespace Serein.Workbench }; //NodeControlBase nodeControl = GuidToControl(nodeGuid); - ViewObjectViewer.Dispatcher.Invoke(() => { - if (ViewObjectViewer.MonitorObj is null) // 如果没有加载过对象 + if (ViewObjectViewer.MonitorObj is null) // 如果没有加载过对象 + { + ViewObjectViewer.LoadObjectInformation(monitorKey, eventArgs.NewData); // 加载对象 ViewObjectViewerControl.MonitorType.Obj + } + else + { + if (monitorKey.Equals(ViewObjectViewer.MonitorKey)) // 相同对象 { - ViewObjectViewer.LoadObjectInformation(monitorKey, eventArgs.NewData); // 加载对象 ViewObjectViewerControl.MonitorType.Obj - } + ViewObjectViewer.RefreshObjectTree(eventArgs.NewData); // 刷新 + } else { - if (monitorKey.Equals(ViewObjectViewer.MonitorKey)) // 相同对象 - { - ViewObjectViewer.RefreshObjectTree(eventArgs.NewData); // 刷新 - } - else - { - ViewObjectViewer.LoadObjectInformation(monitorKey, eventArgs.NewData); // 加载对象 - } + ViewObjectViewer.LoadObjectInformation(monitorKey, eventArgs.NewData); // 加载对象 } - }); - + } } @@ -607,35 +577,31 @@ namespace Serein.Workbench { string nodeGuid = eventArgs.NodeGuid; if (!TryGetControl(nodeGuid, out var nodeControl)) return; - - this.Dispatcher.Invoke(() => + if (eventArgs.Class == InterruptClass.None) { - if (eventArgs.Class == InterruptClass.None) - { - nodeControl.ViewModel.IsInterrupt = false; + nodeControl.ViewModel.IsInterrupt = false; - } - else - { - nodeControl.ViewModel.IsInterrupt = true; - } + } + else + { + nodeControl.ViewModel.IsInterrupt = true; + } - foreach (var menuItem in nodeControl.ContextMenu.Items) + foreach (var menuItem in nodeControl.ContextMenu.Items) + { + if (menuItem is MenuItem menu) { - if (menuItem is MenuItem menu) + if ("取消中断".Equals(menu.Header)) { - if ("取消中断".Equals(menu.Header)) - { - menu.Header = "在此中断"; - } - else if ("在此中断".Equals(menu.Header)) - { - menu.Header = "取消中断"; - } - + menu.Header = "在此中断"; } + else if ("在此中断".Equals(menu.Header)) + { + menu.Header = "取消中断"; + } + } - }); + } } @@ -665,10 +631,8 @@ namespace Serein.Workbench /// private void FlowEnvironment_OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs) { - this.Dispatcher.Invoke(() => - { - IOCObjectViewer.AddDependenciesInstance(eventArgs.Key, eventArgs.Instance); - }); + IOCObjectViewer.AddDependenciesInstance(eventArgs.Key, eventArgs.Instance); + } /// @@ -678,56 +642,52 @@ namespace Serein.Workbench /// private void FlowEnvironment_OnNodeLocate(NodeLocatedEventArgs eventArgs) { - this.Dispatcher.Invoke(() => - { - if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl)) return; - //scaleTransform.ScaleX = 1; - //scaleTransform.ScaleY = 1; - // 获取控件在 FlowChartCanvas 上的相对位置 - Rect controlBounds = VisualTreeHelper.GetDescendantBounds(nodeControl); - Point controlPosition = nodeControl.TransformToAncestor(FlowChartCanvas).Transform(new Point(0, 0)); + if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl)) return; + //scaleTransform.ScaleX = 1; + //scaleTransform.ScaleY = 1; + // 获取控件在 FlowChartCanvas 上的相对位置 + Rect controlBounds = VisualTreeHelper.GetDescendantBounds(nodeControl); + Point controlPosition = nodeControl.TransformToAncestor(FlowChartCanvas).Transform(new Point(0, 0)); - // 获取控件在画布上的中心点 - double controlCenterX = controlPosition.X + controlBounds.Width / 2; - double controlCenterY = controlPosition.Y + controlBounds.Height / 2; + // 获取控件在画布上的中心点 + double controlCenterX = controlPosition.X + controlBounds.Width / 2; + double controlCenterY = controlPosition.Y + controlBounds.Height / 2; - // 考虑缩放因素计算目标位置的中心点 - double scaledCenterX = controlCenterX * scaleTransform.ScaleX; - double scaledCenterY = controlCenterY * scaleTransform.ScaleY; + // 考虑缩放因素计算目标位置的中心点 + double scaledCenterX = controlCenterX * scaleTransform.ScaleX; + double scaledCenterY = controlCenterY * scaleTransform.ScaleY; - //// 计算画布的可视区域大小 - //double visibleAreaLeft = scaledCenterX; - //double visibleAreaTop = scaledCenterY; - //double visibleAreaRight = scaledCenterX + FlowChartStackGrid.ActualWidth; - //double visibleAreaBottom = scaledCenterY + FlowChartStackGrid.ActualHeight; - //// 检查控件中心点是否在可视区域内 - //bool isInView = scaledCenterX >= visibleAreaLeft && scaledCenterX <= visibleAreaRight && - // scaledCenterY >= visibleAreaTop && scaledCenterY <= visibleAreaBottom; + //// 计算画布的可视区域大小 + //double visibleAreaLeft = scaledCenterX; + //double visibleAreaTop = scaledCenterY; + //double visibleAreaRight = scaledCenterX + FlowChartStackGrid.ActualWidth; + //double visibleAreaBottom = scaledCenterY + FlowChartStackGrid.ActualHeight; + //// 检查控件中心点是否在可视区域内 + //bool isInView = scaledCenterX >= visibleAreaLeft && scaledCenterX <= visibleAreaRight && + // scaledCenterY >= visibleAreaTop && scaledCenterY <= visibleAreaBottom; - //Console.WriteLine($"isInView :{isInView}"); + //Console.WriteLine($"isInView :{isInView}"); - //if (!isInView) - //{ - //} - // 计算平移偏移量,使得控件在可视区域的中心 - double translateX = scaledCenterX - FlowChartStackGrid.ActualWidth / 2; - double translateY = scaledCenterY - FlowChartStackGrid.ActualHeight / 2; + //if (!isInView) + //{ + //} + // 计算平移偏移量,使得控件在可视区域的中心 + double translateX = scaledCenterX - FlowChartStackGrid.ActualWidth / 2; + double translateY = scaledCenterY - FlowChartStackGrid.ActualHeight / 2; - var translate = this.translateTransform; - // 应用平移变换 - translate.X = 0; - translate.Y = 0; - translate.X -= translateX; - translate.Y -= translateY; + var translate = this.translateTransform; + // 应用平移变换 + translate.X = 0; + translate.Y = 0; + translate.X -= translateX; + translate.Y -= translateY; - // 设置RenderTransform以实现移动效果 - TranslateTransform translateTransform = new TranslateTransform(); - nodeControl.RenderTransform = translateTransform; - ElasticAnimation(nodeControl, translateTransform, 4, 1, 0.5); + // 设置RenderTransform以实现移动效果 + TranslateTransform translateTransform = new TranslateTransform(); + nodeControl.RenderTransform = translateTransform; + ElasticAnimation(nodeControl, translateTransform, 4, 1, 0.5); - }); - } /// @@ -775,23 +735,20 @@ namespace Serein.Workbench var newLeft = eventArgs.X; var newTop = eventArgs.Y; - this.Dispatcher.Invoke(() => { - // 限制控件不超出FlowChartCanvas的边界 - if (newLeft >= 0 && newLeft + nodeControl.ActualWidth <= FlowChartCanvas.ActualWidth) - { - Canvas.SetLeft(nodeControl, newLeft); + // 限制控件不超出FlowChartCanvas的边界 + if (newLeft >= 0 && newLeft + nodeControl.ActualWidth <= FlowChartCanvas.ActualWidth) + { + Canvas.SetLeft(nodeControl, newLeft); - } - if (newTop >= 0 && newTop + nodeControl.ActualHeight <= FlowChartCanvas.ActualHeight) - { - Canvas.SetTop(nodeControl, newTop); + } + if (newTop >= 0 && newTop + nodeControl.ActualHeight <= FlowChartCanvas.ActualHeight) + { + Canvas.SetTop(nodeControl, newTop); - } - UpdateConnections(nodeControl); - }); + } + UpdateConnections(nodeControl); - //Canvas.SetLeft(nodeControl,); //Canvas.SetTop(nodeControl, ); } @@ -892,16 +849,13 @@ namespace Serein.Workbench /// private void PlaceNodeOnCanvas(NodeControlBase nodeControl, double x, double y) { - FlowChartCanvas.Dispatcher.Invoke(() => - { - // 添加控件到画布 - FlowChartCanvas.Children.Add(nodeControl); - Canvas.SetLeft(nodeControl, x); - Canvas.SetTop(nodeControl, y); - - ConfigureContextMenu(nodeControl); // 配置节点右键菜单 - ConfigureNodeEvents(nodeControl); // 配置节点事件 - }); + // 添加控件到画布 + FlowChartCanvas.Children.Add(nodeControl); + Canvas.SetLeft(nodeControl, x); + Canvas.SetTop(nodeControl, y); + + ConfigureContextMenu(nodeControl); // 配置节点右键菜单 + ConfigureNodeEvents(nodeControl); // 配置节点事件 } /// @@ -957,10 +911,10 @@ namespace Serein.Workbench { var contextMenu = new ContextMenu(); - var nodeGuid = nodeControl.ViewModel?.Node?.Guid; + var nodeGuid = nodeControl.ViewModel?.NodeModel?.Guid; #region 触发器节点 - if(nodeControl.ViewModel?.Node.ControlType == NodeControlType.Flipflop) + if(nodeControl.ViewModel?.NodeModel.ControlType == NodeControlType.Flipflop) { contextMenu.Items.Add(CreateMenuItem("启动触发器", (s, e) => { @@ -984,7 +938,7 @@ namespace Serein.Workbench #endregion - if (nodeControl.ViewModel?.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void)) + if (nodeControl.ViewModel?.NodeModel?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void)) { contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) => { @@ -998,7 +952,7 @@ namespace Serein.Workbench { if ((s is MenuItem menuItem) && menuItem is not null) { - if (nodeControl?.ViewModel?.Node?.DebugSetting?.InterruptClass == InterruptClass.None) + if (nodeControl?.ViewModel?.NodeModel?.DebugSetting?.InterruptClass == InterruptClass.None) { await EnvDecorator.SetNodeInterruptAsync(nodeGuid, InterruptClass.Branch); @@ -1017,7 +971,7 @@ namespace Serein.Workbench contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => EnvDecorator.SetStartNode(nodeGuid))); - contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => EnvDecorator.RemoveNode(nodeGuid))); + contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => EnvDecorator.RemoveNodeAsync(nodeGuid))); contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsSucceed))); contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsFail))); @@ -1092,9 +1046,9 @@ namespace Serein.Workbench return; } // 获取起始节点与终止节点,消除映射关系 - var fromNodeGuid = connectionToRemove.Start.ViewModel.Node.Guid; - var toNodeGuid = connectionToRemove.End.ViewModel.Node.Guid; - EnvDecorator.RemoveConnect(fromNodeGuid, toNodeGuid, connection.Type); + var fromNodeGuid = connectionToRemove.Start.ViewModel.NodeModel.Guid; + var toNodeGuid = connectionToRemove.End.ViewModel.NodeModel.Guid; + EnvDecorator.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connection.Type); } /// @@ -1293,7 +1247,7 @@ namespace Serein.Workbench if (hitTestResult != null && hitTestResult.VisualHit is UIElement hitElement) { // 准备放置条件表达式控件 - if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition) + if (nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.ExpCondition) { ConditionRegionControl? conditionRegion = GetParentOfType(hitElement); if (conditionRegion is not null) @@ -1317,7 +1271,7 @@ namespace Serein.Workbench private void TryPlaceNodeInRegion(NodeControlBase regionControl, NodeControlBase nodeControl) { // 准备放置条件表达式控件 - if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition) + if (nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.ExpCondition) { ConditionRegionControl? conditionRegion = regionControl as ConditionRegionControl; if (conditionRegion is not null) @@ -1355,7 +1309,7 @@ namespace Serein.Workbench if(sender is NodeControlBase nodeControl) { ChangeViewerObjOfNode(nodeControl); - if (nodeControl?.ViewModel?.Node?.MethodDetails?.IsProtectionParameter == true) return; + if (nodeControl?.ViewModel?.NodeModel?.MethodDetails?.IsProtectionParameter == true) return; IsControlDragging = true; startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置 ((UIElement)sender).CaptureMouse(); // 捕获鼠标 @@ -1395,7 +1349,7 @@ namespace Serein.Workbench var newLeft = oldLeft + deltaX; var newTop = oldTop + deltaY; - this.EnvDecorator.MoveNode(nodeControlMain.ViewModel.Node.Guid, newLeft, newTop); // 移动节点 + this.EnvDecorator.MoveNode(nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点 // 计算控件实际移动的距离 var actualDeltaX = newLeft - oldLeft; @@ -1408,7 +1362,7 @@ namespace Serein.Workbench { var otherNewLeft = Canvas.GetLeft(nodeControl) + actualDeltaX; var otherNewTop = Canvas.GetTop(nodeControl) + actualDeltaY; - this.EnvDecorator.MoveNode(nodeControl.ViewModel.Node.Guid, otherNewLeft, otherNewTop); // 移动节点 + this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点 } } @@ -1428,7 +1382,7 @@ namespace Serein.Workbench double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量 double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距 double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距 - this.EnvDecorator.MoveNode(nodeControl.ViewModel.Node.Guid, newLeft, newTop); // 移动节点 + this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点 UpdateConnections(nodeControl); } startControlDragPoint = currentPosition; // 更新起始点位置 @@ -1440,7 +1394,7 @@ namespace Serein.Workbench private void ChangeViewerObjOfNode(NodeControlBase nodeControl) { - var node = nodeControl.ViewModel.Node; + var node = nodeControl.ViewModel.NodeModel; //if (node is not null && (node.MethodDetails is null || node.MethodDetails.ReturnType != typeof(void)) if (node is not null && node.MethodDetails?.ReturnType != typeof(void)) { @@ -1493,8 +1447,8 @@ namespace Serein.Workbench if (IsConnecting) { - var formNodeGuid = startConnectNodeControl?.ViewModel.Node.Guid; - var toNodeGuid = (sender as NodeControlBase)?.ViewModel.Node.Guid; + var formNodeGuid = startConnectNodeControl?.ViewModel.NodeModel.Guid; + var toNodeGuid = (sender as NodeControlBase)?.ViewModel.NodeModel.Guid; if (string.IsNullOrEmpty(formNodeGuid) || string.IsNullOrEmpty(toNodeGuid)) { return; @@ -1914,10 +1868,10 @@ namespace Serein.Workbench { foreach (var node in selectNodeControls.ToArray()) { - var guid = node?.ViewModel?.Node?.Guid; + var guid = node?.ViewModel?.NodeModel?.Guid; if (!string.IsNullOrEmpty(guid)) { - EnvDecorator.RemoveNode(guid); + EnvDecorator.RemoveNodeAsync(guid); } } } @@ -1943,7 +1897,7 @@ namespace Serein.Workbench //Console.WriteLine($"一共选取了{selectNodeControls.Count}个控件"); foreach (var node in selectNodeControls) { - node.ViewModel.Selected(); + node.ViewModel.IsSelect =true; // node.ViewModel.CancelSelect(); node.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFC700")); node.BorderThickness = new Thickness(4); @@ -1954,10 +1908,10 @@ namespace Serein.Workbench IsSelectControl = false; foreach (var nodeControl in selectNodeControls) { - nodeControl.ViewModel.CancelSelect(); + nodeControl.ViewModel.IsSelect = false; nodeControl.BorderBrush = Brushes.Black; nodeControl.BorderThickness = new Thickness(0); - if (nodeControl.ViewModel.Node.IsStart) + if (nodeControl.ViewModel.NodeModel.IsStart) { nodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")); nodeControl.BorderThickness = new Thickness(2); @@ -2364,7 +2318,7 @@ namespace Serein.Workbench private void JudgmentFlipFlopNode(NodeControlBase nodeControl) { if (nodeControl is FlipflopNodeControl flipflopControl - && flipflopControl?.ViewModel?.Node is NodeModelBase nodeModel) // 判断是否为触发器 + && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 判断是否为触发器 { int count = 0; foreach (var ct in NodeStaticConfig.ConnectionTypes) @@ -2449,7 +2403,7 @@ namespace Serein.Workbench } else { - await this.EnvDecorator.StartAsyncInSelectNode(selectNodeControls[0].ViewModel.Node.Guid); + await this.EnvDecorator.StartAsyncInSelectNode(selectNodeControls[0].ViewModel.NodeModel.Guid); } } diff --git a/WorkBench/Node/NodeControlViewModelBase.cs b/WorkBench/Node/NodeControlViewModelBase.cs index 4c61e17..00890e3 100644 --- a/WorkBench/Node/NodeControlViewModelBase.cs +++ b/WorkBench/Node/NodeControlViewModelBase.cs @@ -6,16 +6,18 @@ namespace Serein.Workbench.Node.ViewModel { public abstract class NodeControlViewModelBase : INotifyPropertyChanged { - public NodeControlViewModelBase(NodeModelBase node) + public NodeControlViewModelBase(NodeModelBase nodeModel) { - Node = node; - MethodDetails = Node.MethodDetails; + NodeModel = nodeModel; + MethodDetails = NodeModel.MethodDetails; + + // 订阅来自 NodeModel 的通知事件 } /// /// 对应的节点实体类 /// - internal NodeModelBase Node { get; } + internal NodeModelBase NodeModel { get; } private bool isSelect; @@ -37,12 +39,12 @@ namespace Serein.Workbench.Node.ViewModel /// public NodeDebugSetting DebugSetting { - get => Node.DebugSetting; + get => NodeModel.DebugSetting; set { if (value != null) { - Node.DebugSetting = value; + NodeModel.DebugSetting = value; OnPropertyChanged(); } } @@ -53,12 +55,12 @@ namespace Serein.Workbench.Node.ViewModel /// public MethodDetails MethodDetails { - get => Node.MethodDetails; + get => NodeModel.MethodDetails; set { if(value != null) { - Node.MethodDetails = value; + NodeModel.MethodDetails = value; OnPropertyChanged(); } } @@ -78,35 +80,12 @@ namespace Serein.Workbench.Node.ViewModel } } - /// - /// - /// public event PropertyChangedEventHandler? PropertyChanged; - /// - /// - /// - /// protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - /// - /// - /// - public void Selected() - { - IsSelect = true; - } - /// - /// - /// - public void CancelSelect() - { - IsSelect = false; - } - - } } diff --git a/WorkBench/Node/View/ConditionRegionControl.xaml.cs b/WorkBench/Node/View/ConditionRegionControl.xaml.cs index 24f8e03..d31c69b 100644 --- a/WorkBench/Node/View/ConditionRegionControl.xaml.cs +++ b/WorkBench/Node/View/ConditionRegionControl.xaml.cs @@ -34,7 +34,7 @@ namespace Serein.Workbench.Node.View /// public void AddCondition(NodeControlBase node) { - ((CompositeConditionNode)ViewModel.Node).AddNode((SingleConditionNode)node.ViewModel.Node); + ((CompositeConditionNode)ViewModel.NodeModel).AddNode((SingleConditionNode)node.ViewModel.NodeModel); this.Width += node.Width; this.Height += node.Height;