diff --git a/Extend.FlowRemoteManagement/SereinFlowRemoteControl.cs b/Extend.FlowRemoteManagement/SereinFlowRemoteControl.cs index 90071a3..7a7722a 100644 --- a/Extend.FlowRemoteManagement/SereinFlowRemoteControl.cs +++ b/Extend.FlowRemoteManagement/SereinFlowRemoteControl.cs @@ -1,13 +1,10 @@  using Serein.Library; using Serein.Library.Api; -using Serein.Library.Attributes; -using Serein.Library.Enums; using Serein.Library.Network.WebSocketCommunication; using System.Security.Cryptography.X509Certificates; using Serein.NodeFlow; using Serein.Library.Core.NodeFlow; -using Serein.Library.NodeFlow.Tool; using Serein.Library.Utils; using Serein.FlowRemoteManagement.Model; using System.Reflection; diff --git a/FlowStartTool/Program.cs b/FlowStartTool/Program.cs index 8dd7e9c..7994ae7 100644 --- a/FlowStartTool/Program.cs +++ b/FlowStartTool/Program.cs @@ -13,6 +13,11 @@ namespace Serein.FlowStartTool { public static void Main(string[] args) { +#if true + args = [@"F:\临时\project\linux\project.dnf"]; +#endif + + Console.WriteLine("Hello :) "); Console.WriteLine($"args : {string.Join(" , ", args)}"); string filePath; @@ -57,10 +62,10 @@ namespace Serein.FlowStartTool } IsRuning = true; - StartFlow(flowProjectData, fileDataPath).GetAwaiter().GetResult(); + _ = StartFlow(flowProjectData, fileDataPath); while (IsRuning) { - + Console.ReadKey(); } } @@ -88,13 +93,12 @@ namespace Serein.FlowStartTool // this.window = window; //} - Env = new FlowEnvironmentDecorator(uIContextOperation); // Linux 环境下没有线程上下文(暂时没有写) + Env = new FlowEnvironmentDecorator(uIContextOperation); Env.LoadProject(new FlowEnvInfo { Project = flowProjectData }, fileDataPath); // 加载项目 await Env.StartRemoteServerAsync(7525); // 启动 web socket 监听远程请求 //await Env.StartAsync(); - IsRuning = false; } diff --git a/FlowStartTool/Properties/launchSettings.json b/FlowStartTool/Properties/launchSettings.json index a742007..28541c4 100644 --- a/FlowStartTool/Properties/launchSettings.json +++ b/FlowStartTool/Properties/launchSettings.json @@ -1,10 +1,10 @@ { "profiles": { - "Serein.FlowStartTool": { - "commandName": "Project" - }, "配置文件 1": { "commandName": "DebugRoslynComponent" + }, + "Serein.FlowStartTool": { + "commandName": "Project" } } } \ No newline at end of file diff --git a/FlowStartTool/Serein.FlowStartTool.csproj b/FlowStartTool/Serein.FlowStartTool.csproj index 1d48b61..8a1227c 100644 --- a/FlowStartTool/Serein.FlowStartTool.csproj +++ b/FlowStartTool/Serein.FlowStartTool.csproj @@ -9,34 +9,34 @@ enable prompt starter + AnyCPU + False + True + + + + False + True + + + + + + + + + - - - - - - - - - - + --> diff --git a/Library.Core/NodeFlow/DynamicContext.cs b/Library.Core/NodeFlow/DynamicContext.cs index 7315912..436e9f3 100644 --- a/Library.Core/NodeFlow/DynamicContext.cs +++ b/Library.Core/NodeFlow/DynamicContext.cs @@ -47,6 +47,10 @@ namespace Serein.Library.Core.NodeFlow /// public object? GetFlowData(string nodeGuid) { + if (string.IsNullOrEmpty(nodeGuid)) + { + return null; + } if(dictNodeFlowData.TryGetValue(nodeGuid,out var data)) { return data; @@ -70,8 +74,18 @@ namespace Serein.Library.Core.NodeFlow /// /// 结束流程 /// - public void EndCurrentBranch() + public void Exit() { + foreach (var nodeObj in dictNodeFlowData.Values) + { + if (nodeObj is not null) + { + if (typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) && nodeObj is IDisposable disposable) + { + disposable?.Dispose(); + } + } + } this.dictNodeFlowData?.Clear(); RunState = RunState.Completion; } diff --git a/Library.Core/Serein.Library.Core.csproj b/Library.Core/Serein.Library.Core.csproj index 8c80e4b..e4daf39 100644 --- a/Library.Core/Serein.Library.Core.csproj +++ b/Library.Core/Serein.Library.Core.csproj @@ -1,7 +1,7 @@  - 1.0.14 + 1.0.15 net8.0 enable enable diff --git a/Library.Framework/NodeFlow/DynamicContext.cs b/Library.Framework/NodeFlow/DynamicContext.cs index 711a87b..2e1ad96 100644 --- a/Library.Framework/NodeFlow/DynamicContext.cs +++ b/Library.Framework/NodeFlow/DynamicContext.cs @@ -1,4 +1,5 @@ using Serein.Library.Api; +using System; using System.Collections.Concurrent; namespace Serein.Library.Framework.NodeFlow @@ -46,8 +47,6 @@ namespace Serein.Library.Framework.NodeFlow /// public object GetFlowData(string nodeGuid) { - - if (dictNodeFlowData.TryGetValue(nodeGuid, out var data)) { return data; @@ -71,8 +70,18 @@ namespace Serein.Library.Framework.NodeFlow /// /// 结束流程 /// - public void EndCurrentBranch() + public void Exit() { + foreach (var nodeObj in dictNodeFlowData.Values) + { + if (nodeObj != null) + { + if (typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) && nodeObj is IDisposable disposable) + { + disposable?.Dispose(); + } + } + } this.dictNodeFlowData?.Clear(); RunState = RunState.Completion; } diff --git a/Library.Framework/Properties/AssemblyInfo.cs b/Library.Framework/Properties/AssemblyInfo.cs index 67817af..c3ce276 100644 --- a/Library.Framework/Properties/AssemblyInfo.cs +++ b/Library.Framework/Properties/AssemblyInfo.cs @@ -34,5 +34,5 @@ using System.Runtime.InteropServices; //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.1.4")] -[assembly: AssemblyFileVersion("1.0.1.4")] +[assembly: AssemblyFileVersion("1.0.1.5")] [assembly: NeutralResourcesLanguage("")] diff --git a/Library/Api/IDynamicContext.cs b/Library/Api/IDynamicContext.cs index 925356a..f097dc4 100644 --- a/Library/Api/IDynamicContext.cs +++ b/Library/Api/IDynamicContext.cs @@ -42,7 +42,8 @@ namespace Serein.Library.Api /// /// 用以提前结束分支运行 /// - void EndCurrentBranch(); + void Exit(); + /*/// /// 定时循环触发 diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index c89a419..64017da 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -1,5 +1,6 @@  +using Serein.Library.FlowNode; using Serein.Library.Utils; using System; using System.Collections.Generic; @@ -176,6 +177,15 @@ namespace Serein.Library.Api /// Remote, } + + /// + /// 更改方法调用关系 + /// + /// + /// + /// + /// + /// public NodeConnectChangeEventArgs(string fromNodeGuid, string toNodeGuid, JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线 @@ -189,6 +199,15 @@ namespace Serein.Library.Api this.JunctionOfConnectionType = junctionOfConnectionType; } + /// + /// 更改参数传递关系 + /// + /// + /// + /// + /// + /// + /// public NodeConnectChangeEventArgs(string fromNodeGuid, string toNodeGuid, JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线 @@ -228,6 +247,9 @@ namespace Serein.Library.Api /// 节点对应的方法入参所需参数来源 /// public ConnectionArgSourceType ConnectionArgSourceType { get; protected set; } + /// + /// 第几个参数 + /// public int ArgIndex { get; protected set; } @@ -350,17 +372,19 @@ namespace Serein.Library.Api /// public class NodeInterruptStateChangeEventArgs : FlowEventArgs { - public NodeInterruptStateChangeEventArgs(string nodeGuid, InterruptClass @class) + public NodeInterruptStateChangeEventArgs(string nodeGuid,bool isInterrupt) { NodeGuid = nodeGuid; - Class = @class; + // Class = @class; + IsInterrupt = isInterrupt; } /// /// 中断的节点Guid /// public string NodeGuid { get; protected set; } - public InterruptClass Class { get; protected set; } + public bool IsInterrupt { get; protected set; } + // public InterruptClass Class { get; protected set; } } /// /// 节点触发了中断事件参数 @@ -638,7 +662,7 @@ namespace Serein.Library.Api /// 远程环境地址 /// 远程环境端口 /// 密码 - Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres,int port, string token); + Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres,int port, string token); /// /// 退出远程环境 @@ -705,30 +729,53 @@ namespace Serein.Library.Api /// 目标节点Guid /// 起始节点控制点 /// 目标节点控制点 - /// 决定了方法执行后的后继行为 - /// 决定了方法入参来源 - Task ConnectNodeAsync(string fromNodeGuid, + /// 决定了方法执行后的后继行为 + Task ConnectInvokeNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, - ConnectionInvokeType connectionType, - int argIndex); + ConnectionInvokeType invokeType); /// + /// 在两个节点之间创建连接关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 起始节点控制点 + /// 目标节点控制点 + /// 决定了方法参数来源 + /// 设置第几个参数 + Task ConnectArgSourceNodeAsync(string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionArgSourceType argSourceType, + int argIndex); + /// /// 创建节点/区域/基础控件 /// /// 节点/区域/基础控件类型 /// 节点在画布上的位置( - /// 节点绑定的方法说明( + /// 节点绑定的方法说明 Task CreateNodeAsync(NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null); /// - /// 移除两个节点之间的连接关系 + /// 移除两个节点之间的方法调用关系 /// /// 起始节点 /// 目标节点 /// 连接类型 - Task RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType); + Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType); + + /// + /// 移除连接节点之间参数传递的关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 连接到第几个参数 + /// 参数来源类型 + Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex); + /// /// 移除节点/区域/基础控件 @@ -750,12 +797,12 @@ namespace Serein.Library.Api /// - /// 设置节点中断级别 + /// 设置节点中断 /// - /// 被中断的节点Guid - /// 新的中断级别 + /// 更改中断状态的节点Guid + /// 是否中断 /// - Task SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass); + Task SetNodeInterruptAsync(string nodeGuid,bool isInterrup); /// /// 添加作用于某个对象的中断表达式 diff --git a/Library/Extension/SereinExtension.cs b/Library/Extension/SereinExtension.cs new file mode 100644 index 0000000..84e1c95 --- /dev/null +++ b/Library/Extension/SereinExtension.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library +{ + /// + /// 拓展方法 + /// + public static partial class SereinExtension + { + /// + /// 判断连接类型 + /// + /// + /// + public static JunctionOfConnectionType ToConnectyionType(this JunctionType start) + { + if (start == JunctionType.Execute + || start == JunctionType.NextStep) + { + return JunctionOfConnectionType.Invoke; + } + else + { + return JunctionOfConnectionType.Arg; + } + } + /// + /// 判断是否运行连接 + /// + /// + /// + /// + public static bool IsCanConnection(this JunctionType start,JunctionType end) + { + if(start == end) + { + return false; + } + + var startType = start.ToConnectyionType(); + if (startType == JunctionOfConnectionType.Invoke) + { + return (end == JunctionType.Execute && start == JunctionType.NextStep) + || (start == JunctionType.Execute && end == JunctionType.NextStep); + } + else // if (startType == JunctionOfConnectionType.Arg) + { + return (end == JunctionType.ArgData && start == JunctionType.ReturnData) + || (start == JunctionType.ArgData && end == JunctionType.ReturnData); + } + + //var endType = end.ToConnectyionType(); + //if (startType != endType + // || startType == JunctionOfConnectionType.None + // || endType == JunctionOfConnectionType.None) + //{ + // return false; + //} + //else + //{ + // if (startType == JunctionOfConnectionType.Invoke) + // { + + // return end == JunctionType.NextStep; + // } + // else // if (startType == JunctionOfConnectionType.Arg) + // { + // return end == JunctionType.ReturnData; + // } + //} + } + + } +} diff --git a/Library/Entity/DelegateDetails.cs b/Library/FlowNode/DelegateDetails.cs similarity index 86% rename from Library/Entity/DelegateDetails.cs rename to Library/FlowNode/DelegateDetails.cs index edd7d9e..8da348a 100644 --- a/Library/Entity/DelegateDetails.cs +++ b/Library/FlowNode/DelegateDetails.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using static Serein.Library.Utils.EmitHelper; @@ -14,6 +15,17 @@ namespace Serein.Library /// public class DelegateDetails { + /// + /// 根据方法信息构建Emit委托 + /// + /// + public DelegateDetails(MethodInfo methodInfo) + { + var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var emitDelegate); + _emitMethodType = emitMethodType; + _emitDelegate = emitDelegate; + } + /// /// 记录Emit委托 /// @@ -21,8 +33,8 @@ namespace Serein.Library /// public DelegateDetails(EmitMethodType EmitMethodType, Delegate EmitDelegate) { - this._emitMethodType = EmitMethodType; - this._emitDelegate = EmitDelegate; + _emitMethodType = EmitMethodType; + _emitDelegate = EmitDelegate; } /// /// 更新委托方法 @@ -56,7 +68,7 @@ namespace Serein.Library /// void方法自动返回null public async Task InvokeAsync(object instance, object[] args) { - if(args is null) + if (args is null) { args = Array.Empty(); } diff --git a/Library/FlowNode/NodeDebugSetting.cs b/Library/FlowNode/NodeDebugSetting.cs index f4dd378..2395651 100644 --- a/Library/FlowNode/NodeDebugSetting.cs +++ b/Library/FlowNode/NodeDebugSetting.cs @@ -35,15 +35,16 @@ namespace Serein.Library /// /// 中断级别,暂时停止继续执行后继分支。 /// - [PropertyInfo] - private InterruptClass _interruptClass = InterruptClass.None; + //[PropertyInfo] + //private InterruptClass _interruptClass = InterruptClass.None; /// /// 中断级别,暂时停止继续执行后继分支。 /// - [PropertyInfo(IsNotification = true)] + [PropertyInfo(IsNotification = true, CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);")] private bool _isInterrupt = false; + //private const string MyInteruptCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);"; // 添加到中断的自定义代码 /// /// 取消中断的回调函数 @@ -56,29 +57,30 @@ namespace Serein.Library /// [PropertyInfo] private Func> _getInterruptTask; + + } + + + + /// + /// 中断级别,暂时停止继续执行后继分支。 + /// + //public enum InterruptClass + //{ + // /// + // /// 不中断 + // /// + // None, + // /// + // /// 分支中断,中断进入当前节点的分支。 + // /// + // Branch, + // /// + // /// 全局中断,中断全局所有节点的运行。(暂未实现相关) + // /// + // Global, + //} } - /// - /// 中断级别,暂时停止继续执行后继分支。 - /// - public enum InterruptClass - { - /// - /// 不中断 - /// - None, - /// - /// 分支中断,中断进入当前节点的分支。 - /// - Branch, - /// - /// 全局中断,中断全局所有节点的运行。(暂未实现相关) - /// - Global, - } -} - - - diff --git a/Library/FlowNode/NodeModelBaseData.cs b/Library/FlowNode/NodeModelBaseData.cs index 061d4dd..7d51e8e 100644 --- a/Library/FlowNode/NodeModelBaseData.cs +++ b/Library/FlowNode/NodeModelBaseData.cs @@ -85,9 +85,6 @@ namespace Serein.Library } - - - public abstract partial class NodeModelBase : IDynamicFlowNode { public NodeModelBase(IFlowEnvironment environment) @@ -119,39 +116,40 @@ namespace Serein.Library /// /// 控制FlowData在同一时间只会被同一个线程更改。 /// - private readonly ReaderWriterLockSlim _flowDataLock = new ReaderWriterLockSlim(); - private object _flowData; - /// - /// 当前传递数据(执行了节点对应的方法,才会存在值)。 - /// - protected object FlowData - { - get - { - _flowDataLock.EnterReadLock(); - try - { - return _flowData; - } - finally - { - _flowDataLock.ExitReadLock(); - } - } - set - { - _flowDataLock.EnterWriteLock(); - try - { - _flowData = value; - } - finally - { - _flowDataLock.ExitWriteLock(); - } - } - } + //private readonly ReaderWriterLockSlim _flowDataLock = new ReaderWriterLockSlim(); + //private object _flowData; + ///// + ///// 当前传递数据(执行了节点对应的方法,才会存在值)。 + ///// + //protected object FlowData + //{ + // get + // { + // _flowDataLock.EnterReadLock(); + // try + // { + // return _flowData; + // } + // finally + // { + // _flowDataLock.ExitReadLock(); + // } + // } + // set + // { + // _flowDataLock.EnterWriteLock(); + // try + // { + // _flowData = value; + // } + // finally + // { + // _flowDataLock.ExitWriteLock(); + // } + // } + //} } + } @@ -319,5 +317,5 @@ namespace Serein.Library //} -} + diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index 9893174..5232b71 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -31,7 +31,7 @@ namespace Serein.Library /// 获取节点参数 /// /// - public abstract Parameterdata[] GetParameterdatas(); + public abstract ParameterData[] GetParameterdatas(); /// /// 导出为节点信息 @@ -47,7 +47,7 @@ namespace Serein.Library var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支 // 生成参数列表 - Parameterdata[] parameterData = GetParameterdatas(); + ParameterData[] parameterData = GetParameterdatas(); return new NodeInfo { @@ -82,9 +82,13 @@ namespace Serein.Library { for (int i = 0; i < nodeInfo.ParameterData.Length; i++) { - Parameterdata pd = nodeInfo.ParameterData[i]; - this.MethodDetails.ParameterDetailss[i].IsExplicitData = pd.State; - this.MethodDetails.ParameterDetailss[i].DataValue = pd.Value; + var mdPd = this.MethodDetails.ParameterDetailss[i]; + ParameterData pd = nodeInfo.ParameterData[i]; + mdPd.IsExplicitData = pd.State; + mdPd.DataValue = pd.Value; + mdPd.ArgDataSourceType = EnumHelper.ConvertEnum(pd.SourceType); + mdPd.ArgDataSourceNodeGuid = pd.SourceNodeGuid; + } } return this; @@ -93,13 +97,12 @@ namespace Serein.Library #region 调试中断 - /// /// 不再中断 /// public void CancelInterrupt() { - this.DebugSetting.InterruptClass = InterruptClass.None; + this.DebugSetting.IsInterrupt = false; DebugSetting.CancelInterruptCallback?.Invoke(); } @@ -165,7 +168,7 @@ namespace Serein.Library NodeModelBase upstreamNode = upstreamNodes[index]; if (!(upstreamNode is null) && upstreamNode.DebugSetting.IsEnable) { - if (upstreamNode.DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前 + if (upstreamNode.DebugSetting.IsInterrupt) // 执行触发前 { var cancelType = await upstreamNode.DebugSetting.GetInterruptTask(); await Console.Out.WriteLineAsync($"[{upstreamNode.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支"); @@ -220,7 +223,7 @@ namespace Serein.Library { #region 调试中断 - if (DebugSetting.InterruptClass != InterruptClass.None) // 执行触发检查是否需要中断 + if (DebugSetting.IsInterrupt) // 执行触发检查是否需要中断 { var cancelType = await this.DebugSetting.GetInterruptTask(); // 等待中断结束 await Console.Out.WriteLineAsync($"[{this.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支"); @@ -312,8 +315,9 @@ namespace Serein.Library } object[] parameters = new object[md.ParameterDetailss.Length]; - var previousFlowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据 - var previousDataType = previousFlowData?.GetType(); // 当前传递数据的类型 + + //var previousFlowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据 + for (int i = 0; i < parameters.Length; i++) { @@ -336,41 +340,48 @@ namespace Serein.Library object inputParameter; // 存放解析的临时参数 if (ed.IsExplicitData) // 判断是否使用显示的输入参数 { - if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && !(previousFlowData is null)) + if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase)) { + var previousFlowData = context.GetFlowData(nodeModel?.PreviousNode?.Guid); // 当前传递的数据 // 执行表达式从上一节点获取对象 inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, previousFlowData, out _); } else { // 使用输入的固定值 - inputParameter = ed.DataValue; + inputParameter = ed.DataValue; } } else { if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData) { - inputParameter = previousFlowData; // 使用运行时上一节点的返回值 + inputParameter = context.GetFlowData(nodeModel?.PreviousNode?.Guid); // 当前传递的数据 } - else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData) + else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) { // 获取指定节点的数据 // 如果指定节点没有被执行,会返回null // 如果执行过,会获取上一次执行结果作为预入参数据 - inputParameter = ed.ArgDataSourceNodeMoels[i].FlowData; + inputParameter = context.GetFlowData(ed.ArgDataSourceNodeGuid); } else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) { // 立刻调用对应节点获取数据。 - var result = await ed.ArgDataSourceNodeMoels[i].InvokeAsync(nodeModel.Env); + + var result = await context.Env.InvokeNodeAsync(ed.ArgDataSourceNodeGuid); inputParameter = result; } else { throw new Exception("节点执行方法获取入参参数时,ConnectionArgSourceType枚举是意外的枚举值"); } - } + } + if (inputParameter is null) + { + throw new Exception($"[arg{ed.Index}][{ed.Name}][{ed.DataType}]参数不能为null"); + } + #endregion #region 入参存在取值转换器,调用对应的转换器获取入参数据 @@ -418,7 +429,7 @@ namespace Serein.Library #endregion #region 对入参数据尝试进行转换 - + if (inputParameter.GetType() == ed.DataType) { parameters[i] = inputParameter; // 类型一致无需转换,直接装入入参数组 @@ -506,8 +517,8 @@ namespace Serein.Library { await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象 await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点 - nodeModel.FlowData = newData; // 替换数据 - context.AddOrUpdate(guid, nodeModel); // 上下文中更新数据 + //nodeModel.FlowData = newData; // 替换数据 + context.AddOrUpdate(guid, newData); // 上下文中更新数据 } } @@ -548,12 +559,13 @@ namespace Serein.Library if (isExpInterrupt) // 触发中断 { - InterruptClass interruptClass = InterruptClass.Branch; // 分支中断 - if (await context.Env.SetNodeInterruptAsync(nodeModel.Guid, interruptClass)) + nodeModel.DebugSetting.IsInterrupt = true; + if (await context.Env.SetNodeInterruptAsync(nodeModel.Guid,true)) { context.Env.TriggerInterrupt(nodeModel.Guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp); var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); await Console.Out.WriteLineAsync($"[{data}]中断已{cancelType},开始执行后继分支"); + nodeModel.DebugSetting.IsInterrupt = false; } } } @@ -561,26 +573,26 @@ namespace Serein.Library } } - /// - /// 释放对象 - /// - public void ReleaseFlowData() - { - if (typeof(IDisposable).IsAssignableFrom(FlowData?.GetType()) && FlowData is IDisposable disposable) - { - disposable?.Dispose(); - } - this.FlowData = null; - } + ///// + ///// 释放对象 + ///// + //public void ReleaseFlowData() + //{ + // if (typeof(IDisposable).IsAssignableFrom(FlowData?.GetType()) && FlowData is IDisposable disposable) + // { + // disposable?.Dispose(); + // } + // this.FlowData = null; + //} - /// - /// 获取节点数据 - /// - /// - public object GetFlowData() - { - return this.FlowData; - } + ///// + ///// 获取节点数据 + ///// + ///// + //public object GetFlowData() + //{ + // return this.FlowData; + //} #endregion } diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index b5dbce2..683edc5 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -1,4 +1,5 @@ using Serein.Library.Api; +using Serein.Library.Utils; using System; using System.Linq; @@ -62,11 +63,10 @@ namespace Serein.Library /// /// 当 ArgDataSourceType 不为 GetPreviousNodeData 时(从运行时上一节点获取数据)。 - /// 则通过该集合对应的节点,获取其 FlowData 作为预处理的入参参数。 + /// 则通过当前上下文,获取该Guid对应的数据作为预处理的入参参数。 /// - [PropertyInfo(IsProtection = true)] - public NodeModelBase[] _argDataSourceNodeMoels; - + [PropertyInfo] + private string _argDataSourceNodeGuid; /// @@ -97,6 +97,16 @@ namespace Serein.Library public partial class ParameterDetails { + + /// + /// 用于创建元数据 + /// + public ParameterDetails() + { + + } + + /// /// 为节点实例化新的入参描述 /// @@ -112,27 +122,15 @@ namespace Serein.Library /// 参数信息 public ParameterDetails(ParameterDetailsInfo info) { - //this.env = env; Index = info.Index; Name = info.Name; DataType = Type.GetType(info.DataTypeFullName); ExplicitType = Type.GetType(info.ExplicitTypeFullName); ExplicitTypeName = info.ExplicitTypeName; Items = info.Items; - } - - /// - /// 用于创建元数据 - /// - public ParameterDetails() - { } - - - - /// /// 转为描述 /// @@ -141,12 +139,12 @@ namespace Serein.Library { return new ParameterDetailsInfo { - Index = Index, - DataTypeFullName = DataType.FullName, - Name = Name, - ExplicitTypeFullName = ExplicitType.FullName, - ExplicitTypeName = ExplicitTypeName, - Items = Items, + Index = this.Index, + DataTypeFullName = this.DataType.FullName, + Name = this.Name, + ExplicitTypeFullName = this.ExplicitType.FullName, + ExplicitTypeName = this.ExplicitTypeName, + Items = this.Items.Select(it => it).ToArray(), }; } @@ -154,7 +152,7 @@ namespace Serein.Library /// 为某个节点拷贝方法描述的入参描述 /// /// 运行环境 - /// 运行环境 + /// 对应的节点 /// public ParameterDetails CloneOfClone(IFlowEnvironment env, NodeModelBase nodeModel) { diff --git a/Library/FlowNode/ParameterDetailsInfo.cs b/Library/FlowNode/ParameterDetailsInfo.cs index 3f8380f..c1feb54 100644 --- a/Library/FlowNode/ParameterDetailsInfo.cs +++ b/Library/FlowNode/ParameterDetailsInfo.cs @@ -8,7 +8,7 @@ namespace Serein.Library { /// - /// 方法入参描述 + /// 方法入参描述(远程用) /// public class ParameterDetailsInfo { @@ -26,6 +26,7 @@ namespace Serein.Library /// 方法入参参数名称 /// public string Name { get; set; } + /// /// 显式类型 /// diff --git a/Library/FlowNode/SereinProjectData.cs b/Library/FlowNode/SereinProjectData.cs index 708dd41..84d1f78 100644 --- a/Library/FlowNode/SereinProjectData.cs +++ b/Library/FlowNode/SereinProjectData.cs @@ -202,14 +202,13 @@ namespace Serein.Library /// /// 参数 /// - public Parameterdata[] ParameterData { get; set; } + public ParameterData[] ParameterData { get; set; } /// /// 如果是区域控件,则会存在子项。 /// public string[] ChildNodeGuids { get; set; } - /// /// 于画布中的位置 /// @@ -223,14 +222,26 @@ namespace Serein.Library } /// - /// 显示参数,项目文件相关 + /// 参数信息,项目文件相关 /// - public class Parameterdata + public class ParameterData { /// /// 参数类型,true时使用自定义的入参,false时由运行环境自动传参 /// public bool State { get; set; } + + /// + /// 参数来源节点 + /// + public string SourceNodeGuid { get; set; } + + /// + /// 来源类型 + /// + public string SourceType { get; set; } + + /// /// 自定义入参 /// diff --git a/Library/Network/Http/Router.cs b/Library/Network/Http/Router.cs index d99d594..a2967ee 100644 --- a/Library/Network/Http/Router.cs +++ b/Library/Network/Http/Router.cs @@ -17,28 +17,58 @@ using Type = System.Type; namespace Serein.Library.Web { + /// + /// 路由接口 + /// public interface IRouter { + /// + /// 添加处理模块 + /// + /// void AddHandle(Type controllerType); + /// + /// 路由解析开始处理 + /// + /// + /// Task ProcessingAsync(HttpListenerContext context); } - + /// + /// api请求处理模块 + /// public class ApiHandleConfig { - private readonly Delegate EmitDelegate; - private readonly EmitHelper.EmitMethodType EmitMethodType; + private readonly DelegateDetails delegateDetails; + /// + /// Post请求处理方法中,入参参数类型 + /// public enum PostArgType { + /// + /// 不做处理 + /// None, + /// + /// 使用Url参数 + /// IsUrlData, + /// + /// 使用整体的Boby参数 + /// IsBobyData, } + + /// + /// 添加处理配置 + /// + /// public ApiHandleConfig(MethodInfo methodInfo) { - EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out EmitDelegate); + delegateDetails = new DelegateDetails(methodInfo); var parameterInfos = methodInfo.GetParameters(); ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray(); ParameterName = parameterInfos.Select(t => t.Name.ToLower()).ToArray(); @@ -68,7 +98,12 @@ namespace Serein.Library.Web private readonly string[] ParameterName; private readonly Type[] ParameterType; - + /// + /// 处理Get请求 + /// + /// + /// + /// public async Task HandleGet(object instance, Dictionary routeData) { object[] args = new object[ParameterType.Length]; @@ -93,40 +128,20 @@ namespace Serein.Library.Web } } - - object result; + object result = null; try { - if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func> hasResultTask) - { - result = await hasResultTask(instance, args); - } - else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func task) - { - await task.Invoke(instance, args); - result = null; - } - else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func func) - { - result = func.Invoke(instance, args); - } - else - { - result = null; - } + result = await delegateDetails.InvokeAsync(instance, args); } catch (Exception ex) { result = null; await Console.Out.WriteLineAsync(ex.Message); - } return result; - } - - + /// public async Task HandlePost(object instance, JObject jsonObject, Dictionary routeData) { object[] args = new object[ParameterType.Length]; @@ -173,26 +188,10 @@ namespace Serein.Library.Web } - object result; + object result = null; try { - if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func> hasResultTask) - { - result = await hasResultTask(instance, args); - } - else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func task) - { - await task.Invoke(instance, args); - result = null; - } - else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func func) - { - result = func.Invoke(instance, args); - } - else - { - result = null; - } + result = await delegateDetails.InvokeAsync(instance, args); } catch (Exception ex) { @@ -567,107 +566,6 @@ namespace Serein.Library.Web } - - internal static class WebFunc - { - public static bool ToBool(this JToken token, bool defult = false) - { - var value = token?.ToString(); - if (string.IsNullOrWhiteSpace(value)) - { - return defult; - } - if (!bool.TryParse(value, out bool result)) - { - return defult; - } - else - { - return result; - } - } - public static int ToInt(this JToken token, int defult = 0) - { - var value = token?.ToString(); - if (string.IsNullOrWhiteSpace(value)) - { - return defult; - } - if (!int.TryParse(value, out int result)) - { - return defult; - } - else - { - return result; - } - } - public static double ToDouble(this JToken token, double defult = 0) - { - var value = token?.ToString(); - if (string.IsNullOrWhiteSpace(value)) - { - return defult; - } - if (!int.TryParse(value, out int result)) - { - return defult; - } - else - { - return result; - } - } - } - - #region 已经注释 - - // private readonly ConcurrentDictionary _controllerAutoHosting; // 存储是否实例化 - // private readonly ConcurrentDictionary _controllerInstances; - - //public void CollectRoutes(Type controllerType) - //{ - // string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写 - // foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 - // { - // var routeAttribute = method.GetCustomAttribute(); // 获取方法上的 WebAPIAttribute 自定义属性 - // if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性 - // { - // var customUrl = routeAttribute.Url; // 获取自定义 URL - // string url; - // if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空 - // { - // url = $"/api/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL - // } - // else - // { - // customUrl = CleanUrl(customUrl); - // url = $"/api/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL - // } - // var httpMethod = routeAttribute.Http; // 获取 HTTP 方法 - // _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中 - // } - // } - //} - - //public void RegisterRoute(T controllerInstance) // 方法声明,用于动态注册路由 - //{ - // Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型 - // var autoHostingAttribute = controllerType.GetCustomAttribute(); - // foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 - // { - // var webAttribute = method.GetCustomAttribute(); // 获取方法上的 WebAPIAttribute 自定义属性 - // if (webAttribute != null) // 如果存在 WebAPIAttribute 属性 - // { - // var url = AddRoutesUrl(autoHostingAttribute, webAttribute, controllerType, method); - // if (url == null) continue; - // _controllerInstances[url] = controllerInstance; - // _controllerAutoHosting[url] = false; - // } - - // } - //} - - #endregion + } diff --git a/Library/Network/Http/SereinExtension.cs b/Library/Network/Http/SereinExtension.cs new file mode 100644 index 0000000..cafaa6f --- /dev/null +++ b/Library/Network/Http/SereinExtension.cs @@ -0,0 +1,64 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Network.Http +{ + internal static partial class SereinExtension + { + #region JSON相关 + + public static bool ToBool(this JToken token, bool defult = false) + { + var value = token?.ToString(); + if (string.IsNullOrWhiteSpace(value)) + { + return defult; + } + if (!bool.TryParse(value, out bool result)) + { + return defult; + } + else + { + return result; + } + } + public static int ToInt(this JToken token, int defult = 0) + { + var value = token?.ToString(); + if (string.IsNullOrWhiteSpace(value)) + { + return defult; + } + if (!int.TryParse(value, out int result)) + { + return defult; + } + else + { + return result; + } + } + public static double ToDouble(this JToken token, double defult = 0) + { + var value = token?.ToString(); + if (string.IsNullOrWhiteSpace(value)) + { + return defult; + } + if (!int.TryParse(value, out int result)) + { + return defult; + } + else + { + return result; + } + } + #endregion + } +} diff --git a/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs b/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs index 5bbc321..7ffdd2e 100644 --- a/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs +++ b/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs @@ -31,7 +31,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle Action> onExceptionTracking, bool ArgNotNull) { - EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo,out EmitDelegate); + DelegateDetails = new DelegateDetails(methodInfo); this.Module = model; Instance = instance; var parameterInfos = methodInfo.GetParameters(); @@ -72,15 +72,10 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// 参数不能为空 /// private bool ArgNotNull; - /// /// Emit委托 /// - private readonly Delegate EmitDelegate; - /// - /// Emit委托类型 - /// - private readonly EmitHelper.EmitMethodType EmitMethodType; + private readonly DelegateDetails DelegateDetails; /// /// 未捕获的异常跟踪 /// @@ -114,29 +109,26 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// private readonly bool[] IsCheckArgNotNull; - //private object ConvertArg(Type type, string argName ) - //{ - - //} - public async void Handle(Func SendAsync,string msgId, JObject jsonObject) { object[] args = new object[ParameterType.Length]; bool isCanInvoke = true;; // 表示是否可以调用方法 for (int i = 0; i < ParameterType.Length; i++) { - var type = ParameterType[i]; - var argName = ParameterName[i]; - #region DATA JSON数据 - if (useData[i]) - { - args[i] = jsonObject.ToObject(type); - } - #endregion - else if (useMsgId[i]) + var type = ParameterType[i]; // 入参变量类型 + var argName = ParameterName[i]; // 入参参数名称 + #region 传递消息ID + if (useMsgId[i]) { args[i] = msgId; } + #endregion + #region DATA JSON数据 + else if (useData[i]) + { + args[i] = jsonObject.ToObject(type); + } + #endregion #region 值类型参数 else if (type.IsValueType) { @@ -229,23 +221,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle object result; try { - if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func> hasResultTask) - { - result = await hasResultTask(Instance, args); - } - else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func task) - { - await task.Invoke(Instance, args); - result = null; - } - else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func func) - { - result = func.Invoke(Instance, args); - } - else - { - result = null; - } + result = await DelegateDetails.InvokeAsync(Instance, args); } catch (Exception ex) { @@ -256,25 +232,11 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle await SendAsync.Invoke(exData); })); } - //sw.Stop(); - //Console.WriteLine($"Emit Invoke:{sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs"); - - + if (Module.IsReturnValue) { - if (result is null) - { - result = "null"; - } _ = SendAsync.Invoke(result); } - //if( && result != null && result.GetType().IsClass) - //{ - // //var reusltJsonText = JsonConvert.SerializeObject(result); - - // //_ = SendAsync.Invoke($"{reusltJsonText}"); - //} - } diff --git a/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs b/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs index 4db7922..ea92151 100644 --- a/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs +++ b/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs @@ -91,6 +91,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle MyHandleConfigs.Clear(); } + private HashSet _myMsgIdHash = new HashSet(); + /// /// 处理JSON数据 /// @@ -106,11 +108,15 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle return; } string msgId = jsonObject.GetValue(MsgIdJsonKey)?.ToString(); - + if (_myMsgIdHash.Contains(msgId)) + { + Console.WriteLine($"[{msgId}]{theme} 消息重复"); + return; + } + _myMsgIdHash.Add(msgId); try { - JObject dataObj = jsonObject.GetValue(DataJsonKey)?.ToObject(); handldConfig.Handle(async (data) => { @@ -170,9 +176,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle } var msg = jsonData.ToString(); - //Console.WriteLine(msg); - //Console.WriteLine(); - + + await sendAsync.Invoke(msg); } diff --git a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs index 1a4bc78..24b2c63 100644 --- a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs +++ b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs @@ -155,21 +155,16 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// 异步处理消息 /// - /// + /// /// /// - public async Task HandleMsgAsync(Func SendAsync, string message) + public void HandleMsg(Func sendAsync, string message) { - //Console.WriteLine(message); JObject json = JObject.Parse(message); - await Task.Run(() => + foreach (var module in MyHandleModuleDict.Values) { - foreach (var module in MyHandleModuleDict.Values) - { - module.HandleSocketMsg(SendAsync, json); - - } - }); + module.HandleSocketMsg(sendAsync, json); + } } diff --git a/Library/Network/WebSocket/SocketControlBase.cs b/Library/Network/WebSocket/SocketControlBase.cs index a736bbf..e194fae 100644 --- a/Library/Network/WebSocket/SocketControlBase.cs +++ b/Library/Network/WebSocket/SocketControlBase.cs @@ -8,28 +8,4 @@ namespace Serein.Library.Network.WebSocketCommunication } - //[AutoRegister(RegisterSequence.FlowLoading)] - //[AutoSocketModule(JsonThemeField = "theme", JsonDataField = "data")] - //public class UserService : ISocketControlBase - //{ - // public Guid HandleGuid { get; } = new Guid(); - - // // Action 类型是特殊的,会用一个委托代替,这个委托可以将文本信息发送到客户端 - // // Action 类型是特殊的,会用一个委托代替,这个委托可以将对象转成json发送到客户端 - - // [AutoSocketHandle] - // public void AddUser(User user,Action Recover) - // { - // Console.WriteLine(user.ToString()); - // Recover("ok"); - // } - - // [AutoSocketHandle(ThemeValue = "Remote")] - // public void DeleteUser(User user, Action Recover) - // { - // Console.WriteLine(user.ToString()); - // } - - //} - } diff --git a/Library/Network/WebSocket/TestExtension.cs b/Library/Network/WebSocket/TestExtension.cs new file mode 100644 index 0000000..02acb67 --- /dev/null +++ b/Library/Network/WebSocket/TestExtension.cs @@ -0,0 +1,73 @@ +using Serein.Library.Utils; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; + +namespace Serein.Library.Network.WebSocketCommunication +{ + public class MsgQueueUtil + { + public ConcurrentQueue Msgs = new ConcurrentQueue(); + + private readonly Channel _msgChannel; + public MsgQueueUtil() + { + _msgChannel = CreateChannel(); + } + + private Channel CreateChannel() + { + return Channel.CreateBounded(new BoundedChannelOptions(100) + { + FullMode = BoundedChannelFullMode.Wait + }); + } + + /// + /// 等待消息 + /// + /// + public async Task WaitMsgAsync() + { + var state = await _msgChannel.Reader.ReadAsync(); + return state; + } + + public void WriteMsg(string msg) + { + //Msgs.Enqueue(msg); + Console.WriteLine($"{DateTime.Now}{msg}{Environment.NewLine}"); + _ = _msgChannel.Writer.WriteAsync(msg); + } + + public bool TryGetMsg(out string msg) + { + return Msgs.TryDequeue(out msg); + } + + + } + + + + public class SocketExtension + { + /// + /// 发送消息 + /// + /// + /// + /// + public static async Task SendAsync(WebSocket webSocket, string message) + { + var buffer = Encoding.UTF8.GetBytes(message); + await webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None); + } + } +} diff --git a/Library/Network/WebSocket/WebSocketClient.cs b/Library/Network/WebSocket/WebSocketClient.cs index 23f1af5..ba72748 100644 --- a/Library/Network/WebSocket/WebSocketClient.cs +++ b/Library/Network/WebSocket/WebSocketClient.cs @@ -1,10 +1,13 @@ using Serein.Library.Network.WebSocketCommunication.Handle; using System; using System.Diagnostics; +using System.IO.Compression; +using System.IO; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using static System.Net.Mime.MediaTypeNames; namespace Serein.Library.Network.WebSocketCommunication { @@ -38,7 +41,6 @@ namespace Serein.Library.Network.WebSocketCommunication { try { - await _client.ConnectAsync(new Uri(uri), CancellationToken.None); _ = ReceiveAsync(); return true; @@ -58,8 +60,12 @@ namespace Serein.Library.Network.WebSocketCommunication /// public async Task SendAsync(string message) { - var buffer = Encoding.UTF8.GetBytes(message); - await _client.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None); + Console.WriteLine("发送消息"); + await Task.Delay(2000); + await SocketExtension.SendAsync(this._client, message); // 回复客户端 + Console.WriteLine(); + //var buffer = Encoding.UTF8.GetBytes(message); + //await _client.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None); } /// @@ -68,13 +74,21 @@ namespace Serein.Library.Network.WebSocketCommunication /// private async Task ReceiveAsync() { - var buffer = new byte[1024]; + + var msgQueueUtil = new MsgQueueUtil(); + _ = Task.Run(async () => + { + await HandleMsgAsync(_client, msgQueueUtil); + }); + + var receivedMessage = new StringBuilder(); // 用于拼接长消息 while (_client.State == WebSocketState.Open) { try { + var buffer = new byte[1024]; WebSocketReceiveResult result; do @@ -86,21 +100,22 @@ namespace Serein.Library.Network.WebSocketCommunication receivedMessage.Append(partialMessage); } while (!result.EndOfMessage); // 判断是否已经收到完整消息 - + var message = receivedMessage.ToString(); + msgQueueUtil.WriteMsg(message); + receivedMessage.Clear(); // 清空 StringBuilder 为下一条消息做准备 // 处理收到的完整消息 if (result.MessageType == WebSocketMessageType.Close) { await _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); } - else - { - var completeMessage = receivedMessage.ToString(); - _ = MsgHandleHelper.HandleMsgAsync(SendAsync, completeMessage); // 处理消息 - //Debug.WriteLine($"Received: {completeMessage}"); - } - - // 清空 StringBuilder 为下一条消息做准备 - receivedMessage.Clear(); + //else + //{ + // var completeMessage = receivedMessage.ToString(); + // MsgHandleHelper.HandleMsg(SendAsync, completeMessage); // 处理消息,如果方法入参是需要发送消息委托时,将 SendAsync 作为委托参数提供 + // //Debug.WriteLine($"Received: {completeMessage}"); + //} + + } catch (Exception ex) { @@ -110,65 +125,86 @@ namespace Serein.Library.Network.WebSocketCommunication } - /* #region 消息处理 - private readonly string ThemeField; - private readonly ConcurrentDictionary ThemeConfigs = new ConcurrentDictionary(); - - public async Task HandleSocketMsg(string jsonStr) + public async Task HandleMsgAsync(WebSocket webSocket, + MsgQueueUtil msgQueueUtil) { - JObject json; - try - { - json = JObject.Parse(jsonStr); - } - catch (Exception ex) - { - await SendAsync(_client, ex.Message); - return; - } - // 获取到消息 - string themeName = json[ThemeField]?.ToString(); - if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig)) - { - return; - } - object dataValue; - if (string.IsNullOrEmpty(handldConfig.DataField)) + while (true) { - dataValue = json.ToObject(handldConfig.DataType); - } - else - { - dataValue = json[handldConfig.DataField].ToObject(handldConfig.DataType); - } - await handldConfig.Invoke(dataValue, SendAsync); - } - - public void AddConfig(string themeName, Type dataType, MsgHandler msgHandler) - { - if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig)) - { - handldConfig = new HandldConfig + var message = await msgQueueUtil.WaitMsgAsync(); // 有消息时通知 + //if (!msgQueueUtil.TryGetMsg(out var message)) // 获取消息 + //{ + // return; + //} + // 消息处理 + MsgHandleHelper.HandleMsg(async (text) => { - DataField = themeName, - DataType = dataType - }; - ThemeConfigs.TryAdd(themeName, handldConfig); + await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 + }, message); // 处理消息 + } - handldConfig.HandldAsync += msgHandler; - } - public void RemoteConfig(string themeName, MsgHandler msgHandler) - { - if (ThemeConfigs.TryGetValue(themeName, out var handldConfig)) + + + /* #region 消息处理 + private readonly string ThemeField; + private readonly ConcurrentDictionary ThemeConfigs = new ConcurrentDictionary(); + + public async Task HandleSocketMsg(string jsonStr) { - handldConfig.HandldAsync -= msgHandler; - if (!handldConfig.HasSubscribers) + JObject json; + try { - ThemeConfigs.TryRemove(themeName, out _); + json = JObject.Parse(jsonStr); + } + catch (Exception ex) + { + await SendAsync(_client, ex.Message); + return; + } + // 获取到消息 + string themeName = json[ThemeField]?.ToString(); + if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig)) + { + return; + } + + object dataValue; + if (string.IsNullOrEmpty(handldConfig.DataField)) + { + dataValue = json.ToObject(handldConfig.DataType); + } + else + { + dataValue = json[handldConfig.DataField].ToObject(handldConfig.DataType); + } + await handldConfig.Invoke(dataValue, SendAsync); + } + + public void AddConfig(string themeName, Type dataType, MsgHandler msgHandler) + { + if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig)) + { + handldConfig = new HandldConfig + { + DataField = themeName, + DataType = dataType + }; + ThemeConfigs.TryAdd(themeName, handldConfig); + } + handldConfig.HandldAsync += msgHandler; + } + public void RemoteConfig(string themeName, MsgHandler msgHandler) + { + if (ThemeConfigs.TryGetValue(themeName, out var handldConfig)) + { + handldConfig.HandldAsync -= msgHandler; + if (!handldConfig.HasSubscribers) + { + ThemeConfigs.TryRemove(themeName, out _); + } } } + #endregion*/ } - #endregion*/ } } diff --git a/Library/Network/WebSocket/WebSocketServer.cs b/Library/Network/WebSocket/WebSocketServer.cs index e65c58f..6c1da9c 100644 --- a/Library/Network/WebSocket/WebSocketServer.cs +++ b/Library/Network/WebSocket/WebSocketServer.cs @@ -180,67 +180,46 @@ namespace Serein.Library.Network.WebSocketCommunication return; } + var msgQueueUtil = new MsgQueueUtil(); + _ = Task.Run(async () => + { + await HandleMsgAsync(webSocket,msgQueueUtil, authorizedHelper); + }); + //Func SendAsync = async (text) => //{ // await WebSocketServer.SendAsync(webSocket, text); //}; - var buffer = new byte[1024]; var receivedMessage = new StringBuilder(); // 用于拼接长消息 - while (webSocket.State == WebSocketState.Open) + while ( webSocket.State == WebSocketState.Open) { - WebSocketReceiveResult result; - + try { + WebSocketReceiveResult result; + var buffer = new byte[1024]; do { result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - + if (result.MessageType == WebSocketMessageType.Close) + { + await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); + if (IsCheckToken) + { + AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _); + } + } // 将接收到的部分消息解码并拼接 var partialMessage = Encoding.UTF8.GetString(buffer, 0, result.Count); receivedMessage.Append(partialMessage); } while (!result.EndOfMessage); // 循环直到接收到完整的消息 - // 完整消息已经接收到,准备处理 - var message = receivedMessage.ToString(); - - if (result.MessageType == WebSocketMessageType.Close) - { - //SendAsync = null; - await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); - if (IsCheckToken) - { - AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _); - } - } - else - { - if (IsCheckToken) - { - var authorizedResult = await authorizedHelper.HandleAuthorized(message); // 尝试检测授权 - if (!authorizedResult) // 授权失败 - { - await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); - if (IsCheckToken) - { - AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _); - } - continue; - } - } - - // 消息处理 - _ = MsgHandleHelper.HandleMsgAsync(async (text) => - { - await WebSocketServer.SendAsync(webSocket, text); - }, message); // 处理消息 - } - - // 清空 StringBuilder 为下一条消息做准备 - receivedMessage.Clear(); + var message = receivedMessage.ToString(); // 获取消息文本 + receivedMessage.Clear(); // 清空 StringBuilder 为下一条消息做准备 + msgQueueUtil.WriteMsg(message); // 处理消息 } catch (Exception ex) { @@ -249,17 +228,45 @@ namespace Serein.Library.Network.WebSocketCommunication } } } - /// - /// 发送消息 - /// - /// - /// - /// - public static async Task SendAsync(WebSocket webSocket, string message) + + + public async Task HandleMsgAsync(WebSocket webSocket, + MsgQueueUtil msgQueueUtil, + WebSocketAuthorizedHelper authorizedHelper) { - var buffer = Encoding.UTF8.GetBytes(message); - await webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None); + + while (true) + { + var message = await msgQueueUtil.WaitMsgAsync(); // 有消息时通知 + //if (!msgQueueUtil.TryGetMsg(out var message)) // 获取消息 + //{ + // return; + //} + if (IsCheckToken) + { + var authorizedResult = await authorizedHelper.HandleAuthorized(message); // 尝试检测授权 + if (!authorizedResult) // 授权失败 + { + await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); + if (IsCheckToken) + { + AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _); + } + return; + } + } + // 消息处理 + MsgHandleHelper.HandleMsg(async (text) => + { + await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 + }, message); // 处理消息 + + } + } } + + + } diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index b62df21..be41dc9 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -1,7 +1,7 @@  - 1.0.17 + 1.0.18 net8.0;net462 D:\Project\C#\DynamicControl\SereinFlow\.Output @@ -37,7 +37,6 @@ - diff --git a/Library/Utils/ChannelFlowTrigger.cs b/Library/Utils/ChannelFlowTrigger.cs index fe63503..34a5d5e 100644 --- a/Library/Utils/ChannelFlowTrigger.cs +++ b/Library/Utils/ChannelFlowTrigger.cs @@ -100,4 +100,14 @@ namespace Serein.Library.Utils return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<(TriggerType, object)>()); } } + + + + + + + + + + } diff --git a/Library/Utils/RemoteEnvControl.cs b/Library/Utils/RemoteMsgUtil.cs similarity index 91% rename from Library/Utils/RemoteEnvControl.cs rename to Library/Utils/RemoteMsgUtil.cs index 15e8cab..deb7b06 100644 --- a/Library/Utils/RemoteEnvControl.cs +++ b/Library/Utils/RemoteMsgUtil.cs @@ -13,7 +13,7 @@ namespace Serein.Library.Utils /// /// 管理远程环境,具备连接、发送消息、停止的功能 /// - public class RemoteEnvControl + public class RemoteMsgUtil { /// /// 远程环境配置 @@ -52,7 +52,7 @@ namespace Serein.Library.Utils /// /// 配置远程连接IP端口 /// - public RemoteEnvControl(ControlConfiguration controlConfiguration) + public RemoteMsgUtil(ControlConfiguration controlConfiguration) { Config = controlConfiguration; } @@ -120,13 +120,6 @@ namespace Serein.Library.Utils /// public async Task SendAsync(string msgId , string theme, object data) { - //var sendMsg = new - //{ - // theme = theme, - // token = this.Token, - // data = data, - //}; - //var msg = JsonConvert.SerializeObject(sendMsg); JObject jsonData; if (data is null) @@ -156,11 +149,8 @@ namespace Serein.Library.Utils [Config.DataJsonKey] = dataToken }; } - var msg = jsonData.ToString(); - //Console.WriteLine(msg); - //Console.WriteLine(); - + Console.WriteLine($"[{msgId}] => {theme}"); await EnvClient.SendAsync(msg); } diff --git a/NodeFlow/Env/EnvMsgTheme.cs b/NodeFlow/Env/EnvMsgTheme.cs index 04fe42a..c1f8afc 100644 --- a/NodeFlow/Env/EnvMsgTheme.cs +++ b/NodeFlow/Env/EnvMsgTheme.cs @@ -30,21 +30,31 @@ /// public const string SetStartNode = nameof(SetStartNode); /// - /// 尝试连接两个节点 - /// - public const string ConnectNode = nameof(ConnectNode); - /// /// 尝试创建节点 /// public const string CreateNode = nameof(CreateNode); /// - /// 尝试移除节点之间的连接关系 - /// - public const string RemoveConnect = nameof(RemoveConnect); - /// /// 尝试移除节点 /// public const string RemoveNode = nameof(RemoveNode); + /// + /// 尝试连接两个节点的方法调用关系 + /// + public const string ConnectInvokeNode = nameof(ConnectInvokeNode); + /// + /// 尝试移除节点之间的方法调用关系 + /// + public const string RemoveInvokeConnect = nameof(RemoveInvokeConnect); + + /// + /// 尝试连接两个节点的参数传递关系 + /// + public const string ConnectArgSourceNode = nameof(ConnectArgSourceNode); + /// + /// 尝试移除节点之间的参数传递关系 + /// + public const string RemoveArgSourceConnect = nameof(RemoveArgSourceConnect); + /// /// 激活一个触发器 /// diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 9309e25..10f3f94 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Serein.Library; using Serein.Library.Api; +using Serein.Library.FlowNode; using Serein.Library.Utils; using Serein.Library.Utils.SereinExpression; using Serein.NodeFlow.Model; @@ -9,6 +10,7 @@ using Serein.NodeFlow.Tool; using System.Collections.Concurrent; using System.Numerics; using System.Reflection; +using System.Reflection.Metadata.Ecma335; using System.Xml.Linq; using static Serein.Library.Utils.ChannelFlowInterrupt; @@ -78,9 +80,8 @@ namespace Serein.NodeFlow.Env if (clientMsgManage is null) { clientMsgManage = new MsgControllerOfServer(this); - //clientMsgManage = new MsgControllerOfServer(this, "token"); } - await clientMsgManage.StartRemoteServerAsync(port); + _ = clientMsgManage.StartRemoteServerAsync(port); } /// @@ -346,18 +347,11 @@ namespace Serein.NodeFlow.Env List initMethods = []; List loadMethods = []; List exitMethods = []; - //foreach(var mds in MethodDetailss.Values) - //{ - // var initMds = mds.Where(it => it.MethodDynamicType == NodeType.Init); - // var loadMds = mds.Where(it => it.MethodDynamicType == NodeType.Loading); - // var exitMds = mds.Where(it => it.MethodDynamicType == NodeType.Exit); - // initMethods.AddRange(initMds); - // loadMethods.AddRange(loadMds); - // exitMethods.AddRange(exitMds); - //} + var initMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Init); var loadMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Loading); var exitMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Exit); + initMethods.AddRange(initMds); loadMethods.AddRange(loadMds); exitMethods.AddRange(exitMds); @@ -418,9 +412,11 @@ namespace Serein.NodeFlow.Env /// public async Task InvokeNodeAsync(string nodeGuid) { + + if(this.NodeModels.TryGetValue(nodeGuid, out var model)) { - return await model.ExecutingAsync(null); + return await model.InvokeAsync(this); } else { @@ -435,18 +431,7 @@ namespace Serein.NodeFlow.Env { ChannelFlowInterrupt?.CancelAllTasks(); flowStarter?.Exit(); - - foreach (var node in NodeModels.Values) - { - if (node is not null) - { - node.ReleaseFlowData(); // 退出时释放对象计数 - } - } UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs())); - - - GC.Collect(); } @@ -489,7 +474,6 @@ namespace Serein.NodeFlow.Env /// 获取当前环境信息(远程连接) /// /// - // [AutoSocketHandle] public async Task GetEnvInfoAsync() { Dictionary> LibraryMds = []; @@ -574,12 +558,6 @@ namespace Serein.NodeFlow.Env { MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载项目时尝试获取方法信息 } - else - { - - } - - var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点 nodeModel.LoadInfo(nodeInfo); // 创建节点model if (nodeModel is null) @@ -588,7 +566,6 @@ namespace Serein.NodeFlow.Env continue; } - TryAddNode(nodeModel); // 加载项目时将节点加载到环境中 if (nodeInfo.ChildNodeGuids?.Length > 0) { @@ -643,6 +620,7 @@ namespace Serein.NodeFlow.Env Task.Run(async () => { await Task.Delay(777); + #region 方法调用关系 foreach (var nodeInfo in projectData.Nodes) { if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode)) @@ -650,8 +628,6 @@ namespace Serein.NodeFlow.Env // 不存在对应的起始节点 continue; } - - List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes), @@ -670,8 +646,24 @@ namespace Serein.NodeFlow.Env { _ = ConnectInvokeOfNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系 } + } + } + #endregion + #region 参数调用关系 + foreach (var toNode in NodeModels.Values) + { + for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++) + { + var pd = toNode.MethodDetails.ParameterDetailss[i]; + if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid) + && NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode)) + { + + ConnectGerResultOfNode(fromNode, toNode, pd.ArgDataSourceType, pd.Index); + } } } + #endregion }); SetStartNode(projectData.StartNode); @@ -686,7 +678,7 @@ namespace Serein.NodeFlow.Env /// 远程环境地址 /// 远程环境端口 /// 密码 - public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token) + public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) { if (IsLcR) { @@ -695,7 +687,7 @@ namespace Serein.NodeFlow.Env } // 没有连接远程环境,可以重新连接 - var controlConfiguration = new RemoteEnvControl.ControlConfiguration + var controlConfiguration = new RemoteMsgUtil.ControlConfiguration { Addres = addres, Port = port, @@ -704,8 +696,8 @@ namespace Serein.NodeFlow.Env MsgIdJsonKey = FlowEnvironment.MsgIdKey, DataJsonKey = FlowEnvironment.DataKey, }; - var remoteEnvControl = new RemoteEnvControl(controlConfiguration); - var result = await remoteEnvControl.ConnectAsync(); + var remoteMsgUtil = new RemoteMsgUtil(controlConfiguration); + var result = await remoteMsgUtil.ConnectAsync(); if (!result) { await Console.Out.WriteLineAsync("连接失败,请检查地址与端口是否正确"); @@ -713,7 +705,7 @@ namespace Serein.NodeFlow.Env } await Console.Out.WriteLineAsync("连接成功,开始验证Token"); IsLcR = true; - return (true, remoteEnvControl); + return (true, remoteMsgUtil); } /// @@ -908,19 +900,18 @@ namespace Serein.NodeFlow.Env } /// - /// 连接节点 + /// 连接节点,创建方法调用关系 /// /// 起始节点 /// 目标节点 /// 起始节点控制点 /// 目标节点控制点 - /// 连接关系 - public async Task ConnectNodeAsync(string fromNodeGuid, + /// 连接关系 + public async Task ConnectInvokeNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, - ConnectionInvokeType connectionType, - int argIndex) + ConnectionInvokeType invokeType) { // 获取起始节点与目标节点 @@ -943,22 +934,43 @@ namespace Serein.NodeFlow.Env (fromNode, toNode) = (toNode, fromNode); } // 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点 - state = ConnectInvokeOfNode(fromNode, toNode, connectionType); // 本地环境进行连接 + state = ConnectInvokeOfNode(fromNode, toNode, invokeType); // 本地环境进行连接 } - else if (type == JunctionOfConnectionType.Arg) + return state; + + } + + /// + /// 创建节点之间的参数来源关系 + /// + /// 起始节点 + /// 目标节点 + /// 起始节点控制点(result控制点) + /// 目标节点控制点(argData控制点) + /// 目标节点的第几个参数 + /// 调用目标节点对应方法时,对应参数来源类型 + /// + public async Task ConnectArgSourceNodeAsync(string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionArgSourceType connectionArgSourceType, + int argIndex) + { + + // 获取起始节点与目标节点 + var fromNode = GuidToModel(fromNodeGuid); + var toNode = GuidToModel(toNodeGuid); + if (fromNode is null || toNode is null) return false; + (var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType); + if (!state) { - ConnectionArgSourceType connectionArgSourceType; + Console.WriteLine("出现非预期的连接行为"); + return false; // 出现不符预期的连接行为,忽略此次连接行为 + } - if (fromNode.Guid.Equals(toNode.Guid)) - { - connectionArgSourceType = ConnectionArgSourceType.GetPreviousNodeData; - } - else - { - connectionArgSourceType = ConnectionArgSourceType.GetOtherNodeData; - } - - // (连接自身的情况下)从上一个节点“返回值”控制点,连接到目标节点“方法入参”控制点 + if (type == JunctionOfConnectionType.Arg) + { // 从起始节点“返回值”控制点,连接到目标节点“方法入参”控制点 if (fromNodeJunctionType == JunctionType.ArgData) { @@ -966,32 +978,48 @@ namespace Serein.NodeFlow.Env (fromNode, toNode) = (toNode, fromNode); } - // 确定方法入参关系 state = ConnectGerResultOfNode(fromNode, toNode, connectionArgSourceType, argIndex); // 本地环境进行连接 } - return state; } /// - /// 移除连接关系 + /// 移除连接节点之间方法调用的关系 /// /// 起始节点Guid /// 目标节点Guid /// 连接关系 /// - public async Task RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) + public async Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) { // 获取起始节点与目标节点 var fromNode = GuidToModel(fromNodeGuid); var toNode = GuidToModel(toNodeGuid); if (fromNode is null || toNode is null) return false; + var result = await RemoteConnectAsync(fromNode, toNode, connectionType); return result; } + /// + /// 移除连接节点之间参数传递的关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 连接到第几个参数 + public async Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex) + { + // 获取起始节点与目标节点 + var fromNode = GuidToModel(fromNodeGuid); + var toNode = GuidToModel(toNodeGuid); + if (fromNode is null || toNode is null) return false; + var result = await RemoteConnectAsync(fromNode, toNode, argIndex); + return result; + } + + /// /// 获取方法描述 /// @@ -1077,41 +1105,39 @@ namespace Serein.NodeFlow.Env /// 被中断的目标节点Guid /// 中断级别 /// 操作是否成功 - public Task SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass) + public Task SetNodeInterruptAsync(string nodeGuid, bool isInterrupt) { var nodeModel = GuidToModel(nodeGuid); if (nodeModel is null) return Task.FromResult(false); - if (interruptClass == InterruptClass.None) + if (!isInterrupt) { nodeModel.CancelInterrupt(); } - else if (interruptClass == InterruptClass.Branch) + else if (isInterrupt) { nodeModel.DebugSetting.CancelInterruptCallback?.Invoke(); nodeModel.DebugSetting.GetInterruptTask = async () => { TriggerInterrupt(nodeGuid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor); - var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid); + var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid); return result; }; nodeModel.DebugSetting.CancelInterruptCallback = () => { + //nodeModel.DebugSetting.IsInterrupt = false; ChannelFlowInterrupt.TriggerSignal(nodeGuid); }; } - else if (interruptClass == InterruptClass.Global) // 全局……做不了omg - { - return Task.FromResult(false); - } - nodeModel.DebugSetting.InterruptClass = interruptClass; + + //nodeModel.DebugSetting.IsInterrupt = true; if (OperatingSystem.IsWindows()) { - UIContextOperation?.Invoke(() => OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass))); + UIContextOperation?.Invoke(() => OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, isInterrupt))); } return Task.FromResult(true); @@ -1311,10 +1337,6 @@ namespace Serein.NodeFlow.Env #region 私有方法 - - - - /// /// 加载指定路径的DLL文件 /// @@ -1375,11 +1397,31 @@ namespace Serein.NodeFlow.Env connectionType, NodeConnectChangeEventArgs.ConnectChangeType.Remote))); } - //else if (OperatingSystem.IsLinux()) - //{ - - //} + return true; + } + /// + /// 移除连接关系 + /// + /// 起始节点Model + /// 目标节点Model + /// 连接关系 + /// + private async Task RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, int argIndex) + { + + toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = null; + toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值 + if (OperatingSystem.IsWindows()) + { + await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs( + fromNode.Guid, + toNode.Guid, + JunctionOfConnectionType.Arg, + argIndex, + ConnectionArgSourceType.GetPreviousNodeData, + NodeConnectChangeEventArgs.ConnectChangeType.Remote))); + } return true; } @@ -1479,13 +1521,10 @@ namespace Serein.NodeFlow.Env } } - - /// /// 创建节点 /// /// - private bool TryAddNode(NodeModelBase nodeModel) { nodeModel.Guid ??= Guid.NewGuid().ToString(); @@ -1503,10 +1542,6 @@ namespace Serein.NodeFlow.Env return true; } - - - - /// /// 检查连接 /// @@ -1530,12 +1565,12 @@ namespace Serein.NodeFlow.Env type = JunctionOfConnectionType.Invoke; state = true; } - else if (toNodeJunctionType == JunctionType.ArgData && fromNode.Guid.Equals(toNode.Guid)) - { - // “方法执行”控制点拖拽到“方法入参”控制点,且是同一个节点,则添加获取参数关系,表示生成入参参数时自动从该节点的上一节点获取flowdata - type = JunctionOfConnectionType.Arg; - state = true; - } + //else if (toNodeJunctionType == JunctionType.ArgData && fromNode.Guid.Equals(toNode.Guid)) + //{ + // // “方法执行”控制点拖拽到“方法入参”控制点,且是同一个节点,则添加获取参数关系,表示生成入参参数时自动从该节点的上一节点获取flowdata + // type = JunctionOfConnectionType.Arg; + // state = true; + //} } else if (fromNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid)) { @@ -1548,12 +1583,12 @@ namespace Serein.NodeFlow.Env } else if (fromNodeJunctionType == JunctionType.ArgData) { - if (toNodeJunctionType == JunctionType.Execute && fromNode.Guid.Equals(toNode.Guid)) // 添加获取参数关系 - { - // “方法入参”控制点拖拽到“方法执行”控制点,且是同一个节点,则添加获取参数关系,生成入参参数时自动从该节点的上一节点获取flowdata - type = JunctionOfConnectionType.Arg; - state = true; - } + //if (toNodeJunctionType == JunctionType.Execute && fromNode.Guid.Equals(toNode.Guid)) // 添加获取参数关系 + //{ + // // “方法入参”控制点拖拽到“方法执行”控制点,且是同一个节点,则添加获取参数关系,生成入参参数时自动从该节点的上一节点获取flowdata + // type = JunctionOfConnectionType.Arg; + // state = true; + //} if(toNodeJunctionType == JunctionType.ReturnData && !fromNode.Guid.Equals(toNode.Guid)) { // “”控制点拖拽到“方法返回值”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata @@ -1574,14 +1609,13 @@ namespace Serein.NodeFlow.Env return (type,state); } - /// /// 连接节点 /// /// 起始节点 /// 目标节点 - /// 连接关系 - private bool ConnectInvokeOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType connectionType) + /// 连接关系 + private bool ConnectInvokeOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType invokeType) { if (fromNode is null || toNode is null || fromNode == toNode) { @@ -1634,17 +1668,19 @@ namespace Serein.NodeFlow.Env } if (isPass) { - fromNode.SuccessorNodes[connectionType].Add(toNode); // 添加到起始节点的子分支 - toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支 + + fromNode.SuccessorNodes[invokeType].Add(toNode); // 添加到起始节点的子分支 + toNode.PreviousNodes[invokeType].Add(fromNode); // 添加到目标节点的父分支 if (OperatingSystem.IsWindows()) { + UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke( new NodeConnectChangeEventArgs( fromNode.Guid, // 从哪个节点开始 toNode.Guid, // 连接到那个节点 JunctionOfConnectionType.Invoke, - connectionType, // 连接线的样式类型 + invokeType, // 连接线的样式类型 NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 ))); // 通知UI } @@ -1668,8 +1704,13 @@ namespace Serein.NodeFlow.Env /// /// /// - private bool ConnectGerResultOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionArgSourceType connectionArgSourceType,int argIndex) + private bool ConnectGerResultOfNode(NodeModelBase fromNode, + NodeModelBase toNode, + ConnectionArgSourceType connectionArgSourceType, + int argIndex) { + toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = fromNode.Guid; + toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = connectionArgSourceType; UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke( new NodeConnectChangeEventArgs( @@ -1680,7 +1721,7 @@ namespace Serein.NodeFlow.Env connectionArgSourceType, NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 ))); // 通知UI - return false; + return true; } diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs index b8577d9..9d4164c 100644 --- a/NodeFlow/Env/FlowEnvironmentDecorator.cs +++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs @@ -1,5 +1,6 @@ using Serein.Library; using Serein.Library.Api; +using Serein.Library.FlowNode; using Serein.Library.Utils; namespace Serein.NodeFlow.Env @@ -33,22 +34,22 @@ namespace Serein.NodeFlow.Env private IFlowEnvironment currentFlowEnvironment; - private int _flag = 0; // 使用原子自增代替锁 + private int _loadingProjectFlag = 0; // 使用原子自增代替锁 /// /// 传入false时,将停止数据通知。传入true时, /// /// - public void SetFlag(bool value) + public void SetProjectLoadingFlag(bool value) { - Interlocked.Exchange(ref _flag, value ? 1 : 0); + Interlocked.Exchange(ref _loadingProjectFlag, value ? 1 : 0); } /// - /// + /// 判断是否正在加载项目 /// /// - public bool IsFlagSet() + public bool IsLoadingProject() { - return Interlocked.CompareExchange(ref _flag, 1, 1) == 1; + return Interlocked.CompareExchange(ref _loadingProjectFlag, 1, 1) == 1; } @@ -180,34 +181,69 @@ namespace Serein.NodeFlow.Env currentFlowEnvironment.ClearAll(); } - public async Task ConnectNodeAsync(string fromNodeGuid, + /// + /// 在两个节点之间创建连接关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 起始节点控制点 + /// 目标节点控制点 + /// 决定了方法执行后的后继行为 + public async Task ConnectInvokeNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, - ConnectionInvokeType connectionType, - int argIndex) + ConnectionInvokeType invokeType) { - return await currentFlowEnvironment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, connectionType, argIndex); + return await currentFlowEnvironment.ConnectInvokeNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, invokeType); } - public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token) + + /// + /// 在两个节点之间创建连接关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 起始节点控制点 + /// 目标节点控制点 + /// 决定了方法参数来源 + /// 设置第几个参数 + public async Task ConnectArgSourceNodeAsync(string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionArgSourceType argSourceType, + int argIndex) + { + return await currentFlowEnvironment.ConnectArgSourceNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, argSourceType, argIndex); + } + + + /// + /// 连接远程环境并自动切换环境 + /// + /// + /// + /// + /// + public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) { // 连接成功,切换远程环境 - (var isConnect, var remoteEnvControl) = await currentFlowEnvironment.ConnectRemoteEnv(addres, port, token); + (var isConnect, var remoteMsgUtil) = await currentFlowEnvironment.ConnectRemoteEnv(addres, port, token); if (isConnect) { - remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteEnvControl, this.UIContextOperation); + remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteMsgUtil, this.UIContextOperation); currentFlowEnvironment = remoteFlowEnvironment; } - return (isConnect, remoteEnvControl); + return (isConnect, remoteMsgUtil); } public async Task CreateNodeAsync(NodeControlType nodeBase, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null) { - SetFlag(false); + SetProjectLoadingFlag(false); var result = await currentFlowEnvironment.CreateNodeAsync(nodeBase, position, methodDetailsInfo); // 装饰器调用 - SetFlag(true); + SetProjectLoadingFlag(true); return result; } @@ -249,9 +285,9 @@ namespace Serein.NodeFlow.Env public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) { if (flowEnvInfo is null) return; - SetFlag(false); + SetProjectLoadingFlag(false); currentFlowEnvironment.LoadProject(flowEnvInfo, filePath); - SetFlag(true); + SetProjectLoadingFlag(true); } public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) @@ -275,9 +311,21 @@ namespace Serein.NodeFlow.Env return currentFlowEnvironment.RemoteDll(assemblyFullName); } - public async Task RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) + public async Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) { - return await currentFlowEnvironment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connectionType); + return await currentFlowEnvironment.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, connectionType); + } + + /// + /// 移除连接节点之间参数传递的关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 连接到第几个参数 + /// 参数来源类型 + public async Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex) + { + return await currentFlowEnvironment.RemoveConnectArgSourceAsync(fromNodeGuid, toNodeGuid, argIndex); } public async Task RemoveNodeAsync(string nodeGuid) @@ -296,9 +344,9 @@ namespace Serein.NodeFlow.Env currentFlowEnvironment.SetMonitorObjState(key, isMonitor); } - public async Task SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass) + public async Task SetNodeInterruptAsync(string nodeGuid, bool isInterrupt) { - return await currentFlowEnvironment.SetNodeInterruptAsync(nodeGuid, interruptClass); + return await currentFlowEnvironment.SetNodeInterruptAsync(nodeGuid, isInterrupt); } public void SetStartNode(string nodeGuid) @@ -359,11 +407,11 @@ namespace Serein.NodeFlow.Env public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) { - if (!IsFlagSet()) + if (!IsLoadingProject()) { return; } - await currentFlowEnvironment.NotificationNodeValueChangeAsync(nodeGuid, path, value); + await currentFlowEnvironment.NotificationNodeValueChangeAsync(nodeGuid, path, value); } diff --git a/NodeFlow/Env/MsgControllerOfClient.cs b/NodeFlow/Env/MsgControllerOfClient.cs index 53548c8..9e16a41 100644 --- a/NodeFlow/Env/MsgControllerOfClient.cs +++ b/NodeFlow/Env/MsgControllerOfClient.cs @@ -1,13 +1,11 @@ -using Serein.Library; +using Newtonsoft.Json; +using Serein.Library; using Serein.Library.Network.WebSocketCommunication; using Serein.Library.Network.WebSocketCommunication.Handle; using Serein.Library.Utils; namespace Serein.NodeFlow.Env { - - - /// /// 客户端的消息管理(用于处理服务端的响应) /// @@ -28,6 +26,14 @@ namespace Serein.NodeFlow.Env this.remoteFlowEnvironment = remoteFlowEnvironment; SendCommandFunc = func; } + + /// + /// 处理需要返回的消息 + /// + /// + /// + /// + /// private async Task SendCommandAsync(string msgId, string theme, object? data) { await SendCommandFunc.Invoke(msgId, theme, data); @@ -40,15 +46,12 @@ namespace Serein.NodeFlow.Env /// /// /// 超时触发 - public async Task SendAsync(string signal, object? data = null, int overtimeInMs = 100) + public async Task SendAsync(string theme, object? data = null, int overtimeInMs = 100) { - //Console.WriteLine($"指令[{signal}],value:{JsonConvert.SerializeObject(sendData)}"); - if (!DebounceHelper.CanExecute(signal, overtimeInMs)) - { - return; - } + var msgId = MsgIdHelper.GenerateId().ToString(); - await SendCommandAsync(msgId, signal, data); + Console.WriteLine($"[{msgId}] => {theme}"); + await SendCommandAsync(msgId, theme, data); // 客户端发送消息 } /// @@ -58,33 +61,13 @@ namespace Serein.NodeFlow.Env /// 超时触发 public async Task SendAndWaitDataAsync(string theme, object? data = null, int overtimeInMs = 50) { - //Console.WriteLine($"指令[{signal}],value:{JsonConvert.SerializeObject(sendData)}"); - var msgId = MsgIdHelper.GenerateId().ToString(); - _ = SendCommandAsync(msgId, theme, data); + //_ = Task.Run(async () => + //{ + // await Task.Delay(500); + //}); + await SendCommandAsync(msgId, theme, data); // 客户端发送消息 return await remoteFlowEnvironment.WaitData(msgId); - - //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; - //} - - } @@ -139,15 +122,26 @@ namespace Serein.NodeFlow.Env remoteFlowEnvironment.TriggerSignal(msgId, state); } - - [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)] - public void ConnectNode([UseMsgId] string msgId, bool state) + [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectInvokeNode)] + public void ConnectInvokeNode([UseMsgId] string msgId, bool state) { remoteFlowEnvironment.TriggerSignal(msgId, state); } - [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)] - public void RemoveConnect([UseMsgId] string msgId, bool state) + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveInvokeConnect)] + public void RemoveInvokeConnect([UseMsgId] string msgId, bool state) + { + remoteFlowEnvironment.TriggerSignal(msgId, state); + } + + [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectArgSourceNode)] + public void ConnectArgSourceNode([UseMsgId] string msgId, bool state) + { + remoteFlowEnvironment.TriggerSignal(msgId, state); + } + + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveArgSourceConnect)] + public void RemoveArgSourceConnect([UseMsgId] string msgId, bool state) { remoteFlowEnvironment.TriggerSignal(msgId, state); } diff --git a/NodeFlow/Env/MsgControllerOfServer.cs b/NodeFlow/Env/MsgControllerOfServer.cs index a5ee4fc..064d6b0 100644 --- a/NodeFlow/Env/MsgControllerOfServer.cs +++ b/NodeFlow/Env/MsgControllerOfServer.cs @@ -227,7 +227,8 @@ namespace Serein.NodeFlow.Env [AutoSocketHandle(ThemeValue = EnvMsgTheme.GetEnvInfo)] private async Task GetEnvInfoAsync() { - return await environment.GetEnvInfoAsync(); + var envInfo = await environment.GetEnvInfoAsync(); + return envInfo; } /// @@ -248,7 +249,7 @@ namespace Serein.NodeFlow.Env /// 远程环境端口 /// 密码 // [AutoSocketHandle] - public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token) + public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) { return await environment.ConnectRemoteEnv(addres, port, token); } @@ -314,7 +315,7 @@ namespace Serein.NodeFlow.Env } /// - /// 从远程环境移除节点 + /// 远程从远程环境移除节点 /// /// /// @@ -324,56 +325,178 @@ namespace Serein.NodeFlow.Env //var result = environment.RemoveNodeAsync(nodeGuid).GetAwaiter().GetResult(); var result = await environment.RemoveNodeAsync(nodeGuid); //return result; - return new - { - state = result - }; + return new { state = result }; } /// - /// 连接节点 + /// 远程连接节点的方法调用关系 /// /// 起始节点 /// 目标节点 - /// 连接关系 - [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)] - public async Task ConnectNode(string fromNodeGuid, string toNodeGuid, string connectionType) + /// 起始节点控制点 + /// 目标节点控制点 + /// 连接关系 + [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectInvokeNode)] + public async Task ConnectInvokeNode(string fromNodeGuid, + string toNodeGuid, + string fromJunctionType, + string toJunctionType, + string invokeType) { - if (!EnumHelper.TryConvertEnum(connectionType, out var tmpConnectionType)) + if (!EnumHelper.TryConvertEnum(invokeType, out var tmpConnectionType)) { - return new - { - state = false - }; + return new{ state = false}; } - //environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType); - var result = await environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid,0,0, tmpConnectionType,0); - return new + if (!EnumHelper.TryConvertEnum(fromJunctionType, out var tmpFromJunctionType)) { - state = result - }; + return new{ state = false}; + } + if (!EnumHelper.TryConvertEnum(toJunctionType, out var tmpToJunctionType)) + { + return new{ state = false}; + } + + // 检查控制点类别,判断此次连接请求是否符合预期 + if (tmpFromJunctionType == JunctionType.Execute) + { + if (tmpToJunctionType == JunctionType.NextStep) + { + (fromNodeGuid, toNodeGuid) = (toNodeGuid, fromNodeGuid); // 需要反转 + } + else + { + return new { state = false }; // 非预期的控制点连接 + } + } + else if (tmpFromJunctionType == JunctionType.NextStep) + { + if (tmpToJunctionType == JunctionType.Execute) + { + // 顺序正确无须反转 + } + else + { + return new { state = false }; // 非预期的控制点连接 + } + } + else // 其它类型的控制点,排除 + { + return new { state = false }; // 非预期的控制点连接 + } + Console.WriteLine(); + Console.WriteLine($"起始节点:{fromNodeGuid}"); + Console.WriteLine($"目标节点:{toNodeGuid}"); + Console.WriteLine($"链接请求:{(tmpFromJunctionType, tmpToJunctionType)}"); + + var result = await environment.ConnectInvokeNodeAsync(fromNodeGuid, toNodeGuid, tmpFromJunctionType, tmpToJunctionType, tmpConnectionType); + return new { state = result }; } /// - /// 移除连接关系 + /// 远程移除节点的方法调用关系 /// /// 起始节点Guid /// 目标节点Guid - /// 连接关系 - /// - [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)] - public async Task RemoveConnect(string fromNodeGuid, string toNodeGuid, string connectionType) + /// 连接关系 + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveInvokeConnect)] + public async Task RemoveInvokeConnect(string fromNodeGuid, string toNodeGuid, string invokeType) { - if (!EnumHelper.TryConvertEnum(connectionType, out var tmpConnectionType)) + if (!EnumHelper.TryConvertEnum(invokeType, out var tmpConnectionType)) { return new { state = false }; } + var result = await environment.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType); + return new { state = result }; + } - var result = await environment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, tmpConnectionType); + + /// + /// 远程连接节点的参数传递关系 + /// + /// 起始节点 + /// 目标节点 + /// 起始节点控制点 + /// 目标节点控制点 + /// 入参参数来源类型 + /// 第几个参数 + [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectArgSourceNode)] + public async Task ConnectArgSourceNode(string fromNodeGuid, + string toNodeGuid, + string fromJunctionType, + string toJunctionType, + string argSourceType, + int argIndex) + { + if (argIndex < 0 || argIndex > 65535) // 下标不合法 + { + return new { state = false }; + } + // 检查字面量是否可转换枚举类型 + if (!EnumHelper.TryConvertEnum(argSourceType, out var tmpArgSourceType)) + { + return new { state = false }; + } + if (!EnumHelper.TryConvertEnum(fromJunctionType, out var tmpFromJunctionType)) + { + return new { state = false }; + } + if (!EnumHelper.TryConvertEnum(toJunctionType, out var tmpToJunctionType)) + { + return new { state = false }; + } + + // 检查控制点类别,判断此次连接请求是否符合预期 + if (tmpFromJunctionType == JunctionType.ArgData) + { + if (tmpToJunctionType == JunctionType.ReturnData) + { + (fromNodeGuid, toNodeGuid) = (toNodeGuid, fromNodeGuid);// 需要反转 + } + else + { + return new { state = false }; // 非预期的控制点连接 + } + } + else if (tmpFromJunctionType == JunctionType.ReturnData) + { + if (tmpToJunctionType == JunctionType.ArgData) + { + // 顺序正确无须反转 + } + else + { + return new { state = false }; // 非预期的控制点连接 + } + } + else // 其它类型的控制点,排除 + { + return new { state = false }; // 非预期的控制点连接 + } + //Console.WriteLine(); + //Console.WriteLine($"起始节点:{fromNodeGuid}"); + //Console.WriteLine($"目标节点:{toNodeGuid}"); + //Console.WriteLine($"链接请求:{(tmpFromJunctionType, tmpToJunctionType)}"); + // 调用环境接口进行连接 + var result = await environment.ConnectArgSourceNodeAsync(fromNodeGuid, toNodeGuid, tmpFromJunctionType, tmpToJunctionType, tmpArgSourceType, argIndex); + return new { state = result }; + } + + /// + /// 远程移除节点的参数传递关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 目标节点的第几个参数 + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveArgSourceConnect)] + public async Task RemoveArgSourceConnect(string fromNodeGuid, string toNodeGuid, int argIndex) + { + + + var result = await environment.RemoveConnectArgSourceAsync(fromNodeGuid, toNodeGuid, argIndex); return new { state = result @@ -408,18 +531,14 @@ namespace Serein.NodeFlow.Env /// 中断指定节点,并指定中断等级。 /// /// 被中断的目标节点Guid - /// 中断级别 + /// 是否中断 /// 操作是否成功 [AutoSocketHandle(ThemeValue = EnvMsgTheme.SetNodeInterrupt)] - public async Task SetNodeInterruptAsync(string nodeGuid, string interruptClass) + public async Task SetNodeInterruptAsync(string nodeGuid, bool isInterrupt) { - if (!EnumHelper.TryConvertEnum(interruptClass, out var @class)) - { - return false; - } - - return await this.environment.SetNodeInterruptAsync(nodeGuid, @class); + + return await this.environment.SetNodeInterruptAsync(nodeGuid, isInterrupt); } diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs index a7e075d..601029f 100644 --- a/NodeFlow/Env/RemoteFlowEnvironment.cs +++ b/NodeFlow/Env/RemoteFlowEnvironment.cs @@ -1,8 +1,11 @@ using Serein.Library; using Serein.Library.Api; +using Serein.Library.FlowNode; using Serein.Library.Utils; using Serein.NodeFlow.Tool; using System.Collections.Concurrent; +using System.Security.AccessControl; +using System.Threading.Channels; namespace Serein.NodeFlow.Env { @@ -16,21 +19,21 @@ namespace Serein.NodeFlow.Env /// /// 连接到远程环境后切换到的环境接口实现 /// - /// 连接到远程环境后,本地环境自动切换到对应的环境实体 + /// 连接到远程环境后,本地环境自动切换到对应的环境实体 /// 远程环境下需要操作UI线程时,所提供的线程上下文封装工具 - public RemoteFlowEnvironment(RemoteEnvControl RemoteEnvControl, UIContextOperation uIContextOperation) + public RemoteFlowEnvironment(RemoteMsgUtil remoteMsgUtil, UIContextOperation uIContextOperation) { this.UIContextOperation = uIContextOperation; - remoteEnvControl = RemoteEnvControl; - msgClient = new MsgControllerOfClient(this, RemoteEnvControl.SendAsync); - RemoteEnvControl.EnvClient.MsgHandleHelper.AddModule(msgClient, (ex, send) => + RemoteMsgUtil = remoteMsgUtil; + msgClient = new MsgControllerOfClient(this, remoteMsgUtil.SendAsync); // 这里提供的是主动发送消息的方法 + remoteMsgUtil.EnvClient.MsgHandleHelper.AddModule(msgClient, (ex, send) => { Console.WriteLine(ex); }); } //private readonly Func SendCommandAsync; - private readonly RemoteEnvControl remoteEnvControl; + private readonly RemoteMsgUtil RemoteMsgUtil; private readonly MsgControllerOfClient msgClient; private readonly ConcurrentDictionary MethodDetailss = []; @@ -70,6 +73,12 @@ namespace Serein.NodeFlow.Env public IFlowEnvironment CurrentEnv => this; public UIContextOperation UIContextOperation { get; } + + /// + /// 标示是否正在加载项目 + /// + private bool IsLoadingProject = false; + public void SetConsoleOut() { var logTextWriter = new LogTextWriter(msg => @@ -102,9 +111,9 @@ namespace Serein.NodeFlow.Env public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) { - //Console.WriteLine("远程环境尚未实现的接口:LoadProject"); - - // dll面板 + Console.WriteLine("加载远程环境"); + IsLoadingProject = true; + #region DLL功能区创建 var libmds = flowEnvInfo.LibraryMds; foreach (var lib in libmds) { @@ -114,21 +123,18 @@ namespace Serein.NodeFlow.Env FilePath = "Remote", }; var mdInfos = lib.Mds.ToList(); - //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读取时生成元数据 } } - //flowSemaphore. + #endregion + #region 加载节点数据,如果是区域控件,提前加载区域 var projectData = flowEnvInfo.Project; - - List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>(); List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>(); - // 加载节点 foreach (var nodeInfo in projectData.Nodes) { @@ -139,7 +145,6 @@ namespace Serein.NodeFlow.Env } else { - MethodDetails? methodDetails = null; if (!string.IsNullOrEmpty(nodeInfo.MethodName)) { @@ -167,7 +172,9 @@ namespace Serein.NodeFlow.Env } } } + #endregion + #region 加载区域中的节点 // 加载区域子项 foreach ((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes) { @@ -183,7 +190,9 @@ namespace Serein.NodeFlow.Env UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid))); } } + #endregion + #region 加载普通的节点 // 加载节点 foreach ((NodeModelBase nodeModel, PositionOfUI position) item in ordinaryNodes) { @@ -202,60 +211,84 @@ namespace Serein.NodeFlow.Env //OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)); UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position))); } + #endregion - - - // 确定节点之间的连接关系 + #region 确定节点之间的连接关系 _ = Task.Run(async () => - { - await Task.Delay(250); - foreach (var nodeInfo in projectData.Nodes) { - if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode)) + await Task.Delay(500); + #region 连接节点的调用关系 + foreach (var nodeInfo in projectData.Nodes) { - // 不存在对应的起始节点 - continue; - } + if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode)) + { + // 不存在对应的起始节点 + continue; + } - List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), + List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes), (ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)]; - List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0) - .Select(info => (info.connectionType, - info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid]) - .ToArray())) - .ToList(); - // 遍历每种类型的节点分支(四种) - foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in fromNodes) - { - // 遍历当前类型分支的节点(确认连接关系) - foreach (var toNode in item.toNodes) + List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0) + .Select(info => (info.connectionType, + info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid]) + .ToArray())) + .ToList(); + // 遍历每种类型的节点分支(四种) + foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in fromNodes) { - - UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, - toNode.Guid, - JunctionOfConnectionType.Invoke, - item.connectionType, - NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点 - //OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, - // toNode.Guid, - // item.connectionType, - // NodeConnectChangeEventArgs.ConnectChangeType.Create)); // - + // 遍历当前类型分支的节点(确认连接关系) + foreach (var toNode in item.toNodes) + { + UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, + toNode.Guid, + JunctionOfConnectionType.Invoke, + item.connectionType, + NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点 + } } } - } - }); + #endregion - SetStartNode(projectData.StartNode); + #region 连接节点的传参关系 + foreach (var toNode in NodeModels.Values) + { + if(toNode.MethodDetails.ParameterDetailss is null) + { + continue; + } + for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++) + { + var pd = toNode.MethodDetails.ParameterDetailss[i]; + if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid) + && NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode)) + { + UIContextOperation?.Invoke(() => + OnNodeConnectChange?.Invoke( + new NodeConnectChangeEventArgs( + fromNode.Guid, // 从哪个节点开始 + toNode.Guid, // 连接到那个节点 + JunctionOfConnectionType.Arg, + (int)pd.Index, // 连接线的样式类型 + pd.ArgDataSourceType, + NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 + ))); // 通知UI + } + } + } + #endregion + }); + #endregion + + SetStartNode(projectData.StartNode); // 设置流程起点 UIContextOperation?.Invoke(() => { - OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); + OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); // 加载完成 }); - + IsLoadingProject = false; } private bool TryAddNode(NodeModelBase nodeModel) { @@ -339,15 +372,12 @@ namespace Serein.NodeFlow.Env public async Task GetEnvInfoAsync() { - var envInfo = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.GetEnvInfo); - return envInfo; } - - public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token) + public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) { await Console.Out.WriteLineAsync("远程环境尚未实现的接口:ConnectRemoteEnv"); return (false, null); @@ -436,32 +466,204 @@ namespace Serein.NodeFlow.Env return null; } - public async Task ConnectNodeAsync(string fromNodeGuid, - string toNodeGuid, - JunctionType fromNodeJunctionType, - JunctionType toNodeJunctionType, - ConnectionInvokeType connectionType, - int argIndex = 0) + /// + /// 在两个节点之间创建方法调用关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 起始节点控制点 + /// 目标节点控制点 + /// 决定了方法执行后的后继行为 + public async Task ConnectInvokeNodeAsync(string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionInvokeType invokeType) { - var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.ConnectNode, new + if (fromNodeJunctionType == JunctionType.Execute) { - fromNodeGuid, - toNodeGuid, - fromNodeJunctionType = fromNodeJunctionType.ToString(), - toNodeJunctionType = toNodeJunctionType.ToString(), - connectionType = connectionType.ToString(), - }); + if (toNodeJunctionType == JunctionType.NextStep) + { + (fromNodeGuid, toNodeGuid) = (toNodeGuid, fromNodeGuid);// 需要反转 + } + else + { + return false; // 非预期的控制点连接 + } + } + else if (fromNodeJunctionType == JunctionType.NextStep) + { + if (toNodeJunctionType == JunctionType.Execute) + { + // 顺序正确无须反转 + } + else + { + return false; // 非预期的控制点连接 + } + } + else // 其它类型的控制点,排除 + { + return false; // 非预期的控制点连接 + } + + + var sendObj = new + { + fromNodeGuid = fromNodeGuid, + toNodeGuid = toNodeGuid, + fromJunctionType = fromNodeJunctionType.ToString(), + toJunctionType = toNodeJunctionType.ToString(), + invokeType = invokeType.ToString(), + }; + var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.ConnectInvokeNode, sendObj); if (result) { OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, toNodeGuid, JunctionOfConnectionType.Invoke, - connectionType, + invokeType, NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI } return result; } + /// + /// 在两个节点之间创建参数传递关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 起始节点控制点 + /// 目标节点控制点 + /// 决定了方法参数来源 + /// 设置第几个参数 + public async Task ConnectArgSourceNodeAsync(string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionArgSourceType argSourceType, + int argIndex = 0) + { + + // 正确的顺序:起始节点[返回值控制点] 向 目标节点[入参控制点] 发起连接 + //Console.WriteLine(); + //Console.WriteLine($"起始节点:{fromNodeGuid}"); + //Console.WriteLine($"目标节点:{toNodeGuid}"); + //Console.WriteLine($"链接请求:{(fromNodeJunctionType, toNodeJunctionType)}"); + //Console.WriteLine((fromNodeJunctionType, toNodeJunctionType)); + + if (fromNodeJunctionType == JunctionType.ArgData) + { + if (toNodeJunctionType == JunctionType.ReturnData) + { + (fromNodeGuid, toNodeGuid) = (toNodeGuid, fromNodeGuid);// 需要反转 + } + else + { + return false; // 非预期的控制点连接 + } + } + else if (fromNodeJunctionType == JunctionType.ReturnData) + { + if (toNodeJunctionType == JunctionType.ArgData) + { + // 顺序正确无须反转 + } + else + { + return false; // 非预期的控制点连接 + } + } + else // 其它类型的控制点,排除 + { + return false; // 非预期的控制点连接 + } + + var sendObj = new + { + fromNodeGuid = fromNodeGuid, + toNodeGuid = toNodeGuid, + fromJunctionType = fromNodeJunctionType.ToString(), + toJunctionType = toNodeJunctionType.ToString(), + argSourceType = argSourceType.ToString(), + argIndex = argIndex, + }; + var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.ConnectArgSourceNode, sendObj); + if (result) + { + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + toNodeGuid, + JunctionOfConnectionType.Arg, + argIndex, + argSourceType, + NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI + } + return result; + } + + + /// + /// 移除两个节点之间的方法调用关系 + /// + /// 起始节点 + /// 目标节点 + /// 连接类型 + public async Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType invokeType) + { + var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveInvokeConnect, new + { + fromNodeGuid = fromNodeGuid, + toNodeGuid = toNodeGuid, + invokeType = invokeType.ToString(), + }); + if (result) + { + UIContextOperation.Invoke(() => + { + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + toNodeGuid, + JunctionOfConnectionType.Invoke, + invokeType, + NodeConnectChangeEventArgs.ConnectChangeType.Remote)); + }); + } + return result; + } + /// + /// 移除连接节点之间参数传递的关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 连接到第几个参数 + public async Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex) + { + var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveArgSourceConnect, new + { + fromNodeGuid = fromNodeGuid, + toNodeGuid = toNodeGuid, + argIndex = argIndex, + }); + if (result) + { + UIContextOperation.Invoke(() => + { + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + toNodeGuid, + JunctionOfConnectionType.Arg, + argIndex, + ConnectionArgSourceType.GetPreviousNodeData, + NodeConnectChangeEventArgs.ConnectChangeType.Remote)); // 通知UI + }); + } + return result; + } + + /// + /// 创建节点/区域/基础控件 + /// + /// 节点/区域/基础控件类型 + /// 节点在画布上的位置( + /// 节点绑定的方法说明 public async Task CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null) { var nodeInfo = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.CreateNode, new @@ -489,29 +691,6 @@ namespace Serein.NodeFlow.Env }); return nodeInfo; } - - public async Task RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) - { - var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveConnect, new - { - fromNodeGuid, - toNodeGuid, - connectionType = connectionType.ToString(), - }); - if (result) - { - UIContextOperation.Invoke(() => - { - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, - toNodeGuid, - JunctionOfConnectionType.Invoke, - connectionType, - NodeConnectChangeEventArgs.ConnectChangeType.Remote)); - }); - } - return result; - } - public async Task RemoveNodeAsync(string nodeGuid) { var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveNode, new @@ -548,13 +727,13 @@ namespace Serein.NodeFlow.Env }); } - public async Task SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass) + public async Task SetNodeInterruptAsync(string nodeGuid, bool isInterrupt) { var state = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.SetNodeInterrupt, // 设置节点中断 new { nodeGuid, - interruptClass = interruptClass.ToString(), + isInterrupt, }); return state; } @@ -638,12 +817,13 @@ namespace Serein.NodeFlow.Env public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) { //Console.WriteLine($"通知远程环境修改节点数据:{nodeGuid},name:{path},value:{value}"); - _ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new - { - nodeGuid = nodeGuid, - path = path, - value = value.ToString(), - }); + + //_ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new + //{ + // nodeGuid = nodeGuid, + // path = path, + // value = value.ToString(), + //}); } } diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index 81418a7..e7f799d 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -46,13 +46,27 @@ namespace Serein.NodeFlow /// public async Task StartFlowInSelectNodeAsync(IFlowEnvironment env, NodeModelBase startNode) { - IDynamicContext Context; + IDynamicContext context; #if NET6_0_OR_GREATER - Context = new Serein.Library.Core.NodeFlow.DynamicContext(env); // 从起始节点启动流程时创建上下文 + context = new Serein.Library.Core.NodeFlow.DynamicContext(env); // 从起始节点启动流程时创建上下文 #else Context = new Serein.Library.Framework.NodeFlow.DynamicContext(env); #endif - await startNode.StartFlowAsync(Context); // 开始运行时从选定节点开始运行 + await startNode.StartFlowAsync(context); // 开始运行时从选定节点开始运行 + context.Exit(); + +/* + + foreach (var node in NodeModels.Values) + { + if (node is not null) + { + node.ReleaseFlowData(); // 退出时释放对象 + } + } + + + */ } @@ -360,7 +374,7 @@ namespace Serein.NodeFlow if (nextNodes[i].DebugSetting.IsEnable) { nextNodes[i].PreviousNode = singleFlipFlopNode; - if (nextNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前 + if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前 { var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask(); await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); diff --git a/NodeFlow/Model/CompositeActionNode.cs b/NodeFlow/Model/CompositeActionNode.cs deleted file mode 100644 index 1801537..0000000 --- a/NodeFlow/Model/CompositeActionNode.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Serein.Library; -using Serein.Library.Api; - -namespace Serein.NodeFlow.Model -{ - - /// - /// 组合动作节点(用于动作区域) - /// - public class CompositeActionNode : NodeModelBase - { - public List ActionNodes; - - - /// - /// 组合动作节点(用于动作区域) - /// - public CompositeActionNode(IFlowEnvironment environment, List actionNodes):base(environment) - { - ActionNodes = actionNodes; - } - - //public override async Task Executing(IDynamicContext context) - public override Task ExecutingAsync(IDynamicContext context) - { - throw new NotImplementedException("动作区域暂未实现"); - } - - public override Parameterdata[] GetParameterdatas() - { - return []; - } - - public override NodeInfo? ToInfo() - { - if (MethodDetails is null) return null; - - var trueNodes = SuccessorNodes[ConnectionInvokeType.IsSucceed].Select(item => item.Guid); // 真分支 - var falseNodes = SuccessorNodes[ConnectionInvokeType.IsFail].Select(item => item.Guid);// 假分支 - var errorNodes = SuccessorNodes[ConnectionInvokeType.IsError].Select(item => item.Guid);// 异常分支 - var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支 - // 生成参数列表 - Parameterdata[] parameterData = GetParameterdatas(); - - return new NodeInfo - { - Guid = Guid, - MethodName = MethodDetails?.MethodName, - Label = DisplayName ?? "", - Type = this.GetType().ToString(), - TrueNodes = trueNodes.ToArray(), - FalseNodes = falseNodes.ToArray(), - UpstreamNodes = upstreamNodes.ToArray(), - ParameterData = parameterData.ToArray(), - ErrorNodes = errorNodes.ToArray(), - ChildNodeGuids = ActionNodes.Select(node => node.Guid).ToArray(), - }; - } - } - -} diff --git a/NodeFlow/Model/CompositeConditionNode.cs b/NodeFlow/Model/CompositeConditionNode.cs index 8d56f52..af32f8b 100644 --- a/NodeFlow/Model/CompositeConditionNode.cs +++ b/NodeFlow/Model/CompositeConditionNode.cs @@ -58,7 +58,8 @@ namespace Serein.NodeFlow.Model break; } } - return Task.FromResult( PreviousNode?.GetFlowData()); // 条件区域透传上一节点的数据 + + return Task.FromResult(context.GetFlowData(PreviousNode.Guid)); // 条件区域透传上一节点的数据 } @@ -78,7 +79,7 @@ namespace Serein.NodeFlow.Model } } - public override Parameterdata[] GetParameterdatas() + public override ParameterData[] GetParameterdatas() { return []; } @@ -97,7 +98,7 @@ namespace Serein.NodeFlow.Model var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支 // 生成参数列表 - Parameterdata[] parameterData = GetParameterdatas(); + ParameterData[] parameterData = GetParameterdatas(); return new NodeInfo { diff --git a/NodeFlow/Model/CompositeLoopNode.cs b/NodeFlow/Model/CompositeLoopNode.cs deleted file mode 100644 index acf3822..0000000 --- a/NodeFlow/Model/CompositeLoopNode.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Serein.NodeFlow.Model -{ - //public class CompositeLoopNode : NodeBase - //{ - //} -} diff --git a/NodeFlow/Model/SingleActionNode.cs b/NodeFlow/Model/SingleActionNode.cs index 92a9487..5602462 100644 --- a/NodeFlow/Model/SingleActionNode.cs +++ b/NodeFlow/Model/SingleActionNode.cs @@ -1,5 +1,6 @@ using Serein.Library.Api; using Serein.Library; +using System.Security.AccessControl; namespace Serein.NodeFlow.Model { @@ -12,13 +13,15 @@ namespace Serein.NodeFlow.Model { } - public override Parameterdata[] GetParameterdatas() + public override ParameterData[] GetParameterdatas() { if (base.MethodDetails.ParameterDetailss.Length > 0) { return MethodDetails.ParameterDetailss - .Select(it => new Parameterdata + .Select(it => new ParameterData { + SourceNodeGuid = it.ArgDataSourceNodeGuid, + SourceType = it.ArgDataSourceType.ToString(), State = it.IsExplicitData, Value = it.DataValue, }) diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs index bc53ae5..4c21f21 100644 --- a/NodeFlow/Model/SingleConditionNode.cs +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -50,7 +50,7 @@ namespace Serein.NodeFlow.Model { // 接收上一节点参数or自定义参数内容 object? parameter; - object? result = PreviousNode?.GetFlowData(); // 条件节点透传上一节点的数据 + object? result = context.GetFlowData(PreviousNode.Guid); // 条件节点透传上一节点的数据 if (IsCustomData) // 是否使用自定义参数 { // 表达式获取上一节点数据 @@ -88,7 +88,7 @@ namespace Serein.NodeFlow.Model return Task.FromResult(result); } - public override Parameterdata[] GetParameterdatas() + public override ParameterData[] GetParameterdatas() { var value = CustomData switch { @@ -99,7 +99,7 @@ namespace Serein.NodeFlow.Model Type when CustomData.GetType() == typeof(bool) => ((bool)CustomData).ToString(), _ => CustomData?.ToString()!, }; - return [new Parameterdata + return [new ParameterData { State = IsCustomData, Expression = Expression, @@ -114,11 +114,10 @@ namespace Serein.NodeFlow.Model this.Position = nodeInfo.Position;// 加载位置信息 for (int i = 0; i < nodeInfo.ParameterData.Length; i++) { - Parameterdata? pd = nodeInfo.ParameterData[i]; + ParameterData? pd = nodeInfo.ParameterData[i]; node.IsCustomData = pd.State; node.CustomData = pd.Value; node.Expression = pd.Expression; - } return this; } diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/SingleExpOpNode.cs index 5eaa994..da97270 100644 --- a/NodeFlow/Model/SingleExpOpNode.cs +++ b/NodeFlow/Model/SingleExpOpNode.cs @@ -31,7 +31,7 @@ namespace Serein.NodeFlow.Model //public override async Task Executing(IDynamicContext context) public override Task ExecutingAsync(IDynamicContext context) { - var data = PreviousNode?.GetFlowData(); // 表达式节点使用上一节点数据 + var data = context.GetFlowData(PreviousNode.Guid); // 表达式节点使用上一节点数据 try { @@ -59,9 +59,9 @@ namespace Serein.NodeFlow.Model } - public override Parameterdata[] GetParameterdatas() + public override ParameterData[] GetParameterdatas() { - return [new Parameterdata { Expression = Expression }]; + return [new ParameterData { Expression = Expression }]; } diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs index c32858b..899ab38 100644 --- a/NodeFlow/Model/SingleFlipflopNode.cs +++ b/NodeFlow/Model/SingleFlipflopNode.cs @@ -26,7 +26,7 @@ namespace Serein.NodeFlow.Model public override async Task ExecutingAsync(IDynamicContext context) { #region 执行前中断 - if (DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前 + if (DebugSetting.IsInterrupt) // 执行触发前 { string guid = this.Guid.ToString(); var cancelType = await this.DebugSetting.GetInterruptTask(); @@ -82,13 +82,15 @@ namespace Serein.NodeFlow.Model /// 获取触发器参数 /// /// - public override Parameterdata[] GetParameterdatas() + public override ParameterData[] GetParameterdatas() { if (base.MethodDetails.ParameterDetailss.Length > 0) { return MethodDetails.ParameterDetailss - .Select(it => new Parameterdata + .Select(it => new ParameterData { + SourceNodeGuid = it.ArgDataSourceNodeGuid, + SourceType = it.ArgDataSourceType.ToString(), State = it.IsExplicitData, Value = it.DataValue }) diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 51e7c4b..e57693b 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -1,7 +1,7 @@  - 1.0.17 + 1.0.18 net8.0 enable enable @@ -60,11 +60,6 @@ - - - - - diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs index 98f964a..882fe6f 100644 --- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs +++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs @@ -3,6 +3,7 @@ using Serein.Library.Utils; using Serein.Library; using System.Collections.Concurrent; using System.Reflection; +using Serein.Library.FlowNode; namespace Serein.NodeFlow.Tool; diff --git a/Serein.Library.MyGenerator/Attribute.cs b/Serein.Library.MyGenerator/Attribute.cs index 6f9bd35..b61d09c 100644 --- a/Serein.Library.MyGenerator/Attribute.cs +++ b/Serein.Library.MyGenerator/Attribute.cs @@ -33,7 +33,6 @@ namespace Serein.Library /// 节点的调试设置 /// DebugSetting, - } @@ -68,6 +67,12 @@ namespace Serein.Library /// 是否禁止参数进行修改(初始化后不能再通过 Setter 修改) /// public bool IsProtection = false; + + /// + /// 自定义代码 + /// + public string CustomCode = null; + } } diff --git a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs index 7f06ab8..14f723b 100644 --- a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs +++ b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs @@ -163,7 +163,7 @@ namespace Serein.Library.NodeGenerator var propertyName = field.ToPropertyName(); // 转为合适的属性名称 var attributeInfo = fieldKV.Value; // 缓存的特性信息 - var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), "true"); // 是否为保护字段 + var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), value => bool.Parse(value)); // 是否为保护字段 // 生成 getter / setter sb.AppendLine(leadingTrivia); @@ -174,11 +174,12 @@ namespace Serein.Library.NodeGenerator sb.AppendLine(" {"); sb.AppendLine($" if ({fieldName} {(isProtection ? "== default" : "!= value")})"); // 非保护的Setter sb.AppendLine(" {"); - if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsPrint), "true")) // 是否打印 + sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); // 通知UI属性发生改变了"); + if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsPrint), value => bool.Parse(value))) // 是否打印 { sb.AddCode(5, $"Console.WriteLine({fieldName});"); } - if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsNotification), "true")) // 是否通知 + if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsNotification), value => bool.Parse(value))) // 是否通知 { if (classInfo.ExitsPath(nameof(NodeValuePath.Node))) // 节点 or 自定义节点 { @@ -197,7 +198,12 @@ namespace Serein.Library.NodeGenerator sb.AddCode(5, $"NodeModel?.Env?.NotificationNodeValueChangeAsync(NodeModel.Guid, \"DebugSetting.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了"); } } - sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); // 通知UI属性发生改变了"); + if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.CustomCode), value => !string.IsNullOrEmpty(value))) // 是否打印 + { + var customCode = attributeInfo[nameof(PropertyInfo)][nameof(PropertyInfo.CustomCode)] as string; + customCode = customCode.Trim().Substring(1, customCode.Length - 2); + sb.AddCode(5, $"{customCode} // 添加的自定义代码"); + } //sb.AppendLine($" {fieldName} = value;"); //sb.AppendLine($" OnPropertyChanged(); // 通知UI属性发生改变了"); sb.AppendLine(" }"); @@ -510,7 +516,7 @@ namespace Serein.Library.NodeGenerator public static bool Search(this Dictionary> dict, string attributeName = null, string attributePropertyName = null, - string comparisonValue = null) + Func judgeFunc = null) { if (string.IsNullOrWhiteSpace(attributeName)) return false; @@ -521,9 +527,11 @@ namespace Serein.Library.NodeGenerator return true; if (!abs.TryGetValue(attributePropertyName, out var absValue)) return false; - if (string.IsNullOrWhiteSpace(comparisonValue)) + if (judgeFunc == null) return true; - return absValue.Equals(comparisonValue); + + return judgeFunc.Invoke(absValue); ; + //return absValue.Equals(comparisonValue); } diff --git a/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj index 125db7c..a813521 100644 --- a/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj +++ b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 1.0.1 true D:\Project\C#\DynamicControl\SereinFlow\.Output @@ -14,6 +15,12 @@ True true + + + + + + diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index bcc31fe..47b79f8 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -13,7 +13,7 @@ namespace Serein.Workbench void LoadLocalProject() { #if DEBUG - if (1 == 1) + if (1 == 11) { string filePath; filePath = @"F:\临时\project\linux\project.dnf"; diff --git a/WorkBench/MainWindow.xaml b/WorkBench/MainWindow.xaml index 5063664..a89165a 100644 --- a/WorkBench/MainWindow.xaml +++ b/WorkBench/MainWindow.xaml @@ -1,18 +1,26 @@  + @@ -35,7 +43,7 @@ - + @@ -99,7 +107,7 @@ - + @@ -199,6 +207,25 @@ Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Con + + + + + + + + + + + + + + diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index 2ee9010..ba39f55 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -5,6 +5,7 @@ using Serein.Library.Api; using Serein.Library.Utils; using Serein.Library.Utils.SereinExpression; using Serein.NodeFlow.Tool; +using Serein.Workbench.Extension; using Serein.Workbench.Node; using Serein.Workbench.Node.View; using Serein.Workbench.Node.ViewModel; @@ -83,7 +84,7 @@ namespace Serein.Workbench /// /// 标记是否正在进行连接操作 /// - private bool IsConnecting; + //private bool IsConnecting; /// /// 标记是否正在拖动控件 /// @@ -116,7 +117,7 @@ namespace Serein.Workbench /// /// 记录开始连接的文本块 /// - private NodeControlBase? startConnectNodeControl; + //private NodeControlBase? startConnectNodeControl; /// /// 当前正在绘制的连接线 /// @@ -124,7 +125,7 @@ namespace Serein.Workbench /// /// 当前正在绘制的真假分支属性 /// - private ConnectionInvokeType currentConnectionType; + //private ConnectionInvokeType currentConnectionType; /// @@ -144,36 +145,38 @@ namespace Serein.Workbench public MainWindow() { - InitializeComponent(); ViewModel = new MainWindowViewModel(this); + this.DataContext = ViewModel; + InitializeComponent(); EnvDecorator = ViewModel.FlowEnvironment; + ViewObjectViewer.FlowEnvironment = EnvDecorator; IOCObjectViewer.FlowEnvironment = EnvDecorator; + IOCObjectViewer.SelectObj += ViewObjectViewer.LoadObjectInformation; - - //this.FlowEnvironment.SetConsoleOut((msg) => LogOutWindow.AppendText(msg), () => LogOutWindow.Clear()); // 设置输出 - InitFlowEnvironmentEvent(); // 配置环境事件 - + + #region 缩放平移容器 canvasTransformGroup = new TransformGroup(); scaleTransform = new ScaleTransform(); translateTransform = new TranslateTransform(); - canvasTransformGroup.Children.Add(scaleTransform); canvasTransformGroup.Children.Add(translateTransform); - FlowChartCanvas.RenderTransform = canvasTransformGroup; + #endregion + InitFlowEnvironmentEvent(); // 配置环境事件 if (App.FlowProjectData is not null) { EnvDecorator.LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); // 加载项目 } - - IOCObjectViewer.SelectObj += ViewObjectViewer.LoadObjectInformation; + } + + /// /// 初始化环境事件 /// @@ -229,8 +232,6 @@ namespace Serein.Workbench } - - #region 窗体加载方法 private void Window_Loaded(object sender, RoutedEventArgs e) { @@ -382,13 +383,14 @@ namespace Serein.Workbench JunctionControlBase startJunction = IFormJunction.NextStepJunction; JunctionControlBase endJunction = IToJunction.ExecuteJunction; + // 添加连接 var connection = new ConnectionControl( FlowChartCanvas, connectionType, startJunction, endJunction, - () => EnvDecorator.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connectionType) + () => EnvDecorator.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, connectionType) ); if (toNodeControl is FlipflopNodeControl flipflopControl @@ -400,7 +402,7 @@ namespace Serein.Workbench Connections.Add(connection); fromNodeControl.AddCnnection(connection); toNodeControl.AddCnnection(connection); - EndConnection(); + EndConnection(); // 环境触发了创建节点连接事件 } @@ -440,7 +442,7 @@ namespace Serein.Workbench JunctionControlBase startJunction = eventArgs.ConnectionArgSourceType switch { - ConnectionArgSourceType.GetPreviousNodeData => IFormJunction.ExecuteJunction, // 自身节点 + ConnectionArgSourceType.GetPreviousNodeData => IFormJunction.ReturnDataJunction, // 自身节点 ConnectionArgSourceType.GetOtherNodeData => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点 ConnectionArgSourceType.GetOtherNodeDataOfInvoke => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点 _ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发 @@ -448,11 +450,6 @@ namespace Serein.Workbench JunctionControlBase endJunction = IToJunction.ArgDataJunction[eventArgs.ArgIndex]; LineType lineType = LineType.Bezier; - if(eventArgs.ConnectionArgSourceType == ConnectionArgSourceType.GetPreviousNodeData) - { - lineType = LineType.Semicircle; - } - // 添加连接 var connection = new ConnectionControl( lineType, @@ -461,19 +458,19 @@ namespace Serein.Workbench eventArgs.ConnectionArgSourceType, startJunction, endJunction, - () => EnvDecorator.RemoveConnectAsync(fromNodeGuid, toNodeGuid, 0) + () => EnvDecorator.RemoveConnectArgSourceAsync(fromNodeGuid, toNodeGuid, eventArgs.ArgIndex) ); - if (toNodeControl is FlipflopNodeControl flipflopControl - && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器 - { - NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除 - } + //if (toNodeControl is FlipflopNodeControl flipflopControl + // && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器 + //{ + // NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除 + //} connection.RefreshLine(); // 添加贝塞尔曲线显示 Connections.Add(connection); fromNodeControl.AddCnnection(connection); toNodeControl.AddCnnection(connection); - EndConnection(); + EndConnection(); // 环境触发了创建节点连接事件 } @@ -481,20 +478,27 @@ namespace Serein.Workbench { // 需要移除连接 var removeConnections = Connections.Where(c => c.Start.MyNode.Guid.Equals(fromNodeGuid) - && c.End.MyNode.Guid.Equals(toNodeGuid)) - .ToList(); + && c.End.MyNode.Guid.Equals(toNodeGuid)) + .ToList(); // 获取这两个节点之间的所有连接关系 + foreach (var connection in removeConnections) { - connection.DeleteConnection(); - Connections.Remove(connection); - fromNodeControl.RemoveCnnection(connection); - toNodeControl.RemoveCnnection(connection); - if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control)) + if(connection.End is ArgJunctionControl junctionControl && junctionControl.ArgIndex == eventArgs.ArgIndex) { - JudgmentFlipFlopNode(control); // 连接关系变更时判断 + // 找到符合删除条件的连接线 + connection.DeleteConnection(); // 从UI层面上移除 + Connections.Remove(connection); // 从本地记录中移除 + fromNodeControl.RemoveCnnection(connection); // 从节点持有的记录移除 + toNodeControl.RemoveCnnection(connection); // 从节点持有的记录移除 } + + + //if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control)) + //{ + // JudgmentFlipFlopNode(control); // 连接关系变更时判断 + //} } } #endregion @@ -1044,15 +1048,17 @@ namespace Serein.Workbench { if ((s is MenuItem menuItem) && menuItem is not null) { - if (nodeControl?.ViewModel?.NodeModel?.DebugSetting?.InterruptClass == InterruptClass.None) + if (nodeControl?.ViewModel?.NodeModel?.DebugSetting?.IsInterrupt == true) { - await EnvDecorator.SetNodeInterruptAsync(nodeGuid, InterruptClass.Branch); + await EnvDecorator.SetNodeInterruptAsync(nodeGuid,false); + nodeControl.ViewModel.IsInterrupt = false; menuItem.Header = "取消中断"; } else { - await EnvDecorator.SetNodeInterruptAsync(nodeGuid, InterruptClass.None); + nodeControl!.ViewModel!.IsInterrupt = true; + await EnvDecorator.SetNodeInterruptAsync(nodeGuid, true); menuItem.Header = "在此中断"; } @@ -1166,7 +1172,7 @@ namespace Serein.Workbench #endregion - #region 与流程图与节点相关 + #region 与流程图/节点相关 /// /// 鼠标在画布移动。 @@ -1176,36 +1182,28 @@ namespace Serein.Workbench /// private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e) { + var myData = GlobalJunctionData.MyGlobalConnectingData; + if (myData.IsCreateing && e.LeftButton == MouseButtonState.Pressed) + { + + if (myData.Type == JunctionOfConnectionType.Invoke) + { + ViewModel.IsConnectionInvokeNode = true; // 正在连接节点的调用关系 - if (e.LeftButton == MouseButtonState.Pressed && GlobalJunctionData.MyGlobalConnectingData is not null) - { - // 正在连接节点 - //var controlPointPosition = GlobalJunctionData.MyGlobalConnectingData.StartPoint; + } + else + { + ViewModel.IsConnectionArgSourceNode = true; // 正在连接节点的调用关系 + } var currentPoint = e.GetPosition(FlowChartCanvas); - GlobalJunctionData.MyGlobalConnectingData.UpdatePoint(currentPoint); - - - //virtualLine.VirtualLine.UpdatePoints(currentPoint); - - //virtualLine.VirtualLine.X1 = controlPointPosition.X; - //virtualLine.VirtualLine.Y1 = controlPointPosition.Y; - //virtualLine.VirtualLine.X2 = currentPoint.X; - //virtualLine.VirtualLine.Y2 = currentPoint.Y; + currentPoint.X -= 2; + currentPoint.Y -= 2; + myData.UpdatePoint(currentPoint); return; } - //if (IsConnecting) // 正在连接节点 - //{ - // Point position = e.GetPosition(FlowChartCanvas); - // if (currentLine is null || startConnectNodeControl is null) - // { - // return; - // } - // currentLine.X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2; - // currentLine.Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2; - // currentLine.X2 = position.X; - // currentLine.Y2 = position.Y; - //} - + + + if (IsCanvasDragging && e.MiddleButton == MouseButtonState.Pressed) // 正在移动画布(按住中键) { Point currentMousePosition = e.GetPosition(this); @@ -1399,8 +1397,6 @@ namespace Serein.Workbench /// private void Block_MouseMove(object sender, MouseEventArgs e) { - if (IsConnecting) - return; if (IsCanvasDragging) return; if (IsSelectControl) @@ -1465,8 +1461,8 @@ namespace Serein.Workbench } } - + // 改变对象树? private void ChangeViewerObjOfNode(NodeControlBase nodeControl) { @@ -1475,7 +1471,9 @@ namespace Serein.Workbench if (node is not null && node.MethodDetails?.ReturnType != typeof(void)) { var key = node.Guid; - var instance = node.GetFlowData(); // 对象预览树视图获取(后期更改) + object instance = null; + //Console.WriteLine("WindowXaml 后台代码中 ChangeViewerObjOfNode 需要重新设计"); + //var instance = node.GetFlowData(); // 对象预览树视图获取(后期更改) if(instance is not null) { ViewObjectViewer.LoadObjectInformation(key, instance); @@ -1505,12 +1503,12 @@ namespace Serein.Workbench EnvDecorator.SetMonitorObjState(key, true); // 通知环境,该节点的数据更新后需要传到UI } } - + #endregion #region UI连接控件操作 /// - /// 控件的鼠标左键松开事件,结束拖动操作,创建连线 + /// 控件的鼠标左键松开事件,结束拖动操作 /// private void Block_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { @@ -1534,52 +1532,18 @@ namespace Serein.Workbench //GlobalJunctionData.OK(); } - /// - /// 主窗口的KeyDown事件处理,用于在连接操作中按下Esc键取消连接。 - /// - private void MainWindow_KeyDown(object sender, KeyEventArgs e) - { - if (e.Key == Key.Escape && IsConnecting) - { - this.KeyDown -= MainWindow_KeyDown; - EndConnection(); - } - } /// /// 结束连接操作,清理状态并移除虚线。 /// private void EndConnection() { - IsConnecting = false; - startConnectNodeControl = null; - // 移除虚线 - //if (currentLine != null) - //{ - // FlowChartCanvas.Children.Remove(currentLine); - // currentLine = null; - //} + Mouse.OverrideCursor = null; // 恢复视觉效果 + ViewModel.IsConnectionArgSourceNode = false; + ViewModel.IsConnectionInvokeNode = false; + GlobalJunctionData.OK(); } - /// - /// 更新与指定控件相关的所有连接的位置。 - /// - //private void UpdateConnections(NodeControlBase nodeControl) - //{ - // nodeControl.UpdateLocationConnections(); - // //foreach (var connection in Connections) - // //{ - // // if (connection.Start.MyNode.Guid == nodeControl.ViewModel.NodeModel.Guid - // // || connection.End.MyNode.Guid == nodeControl.ViewModel.NodeModel.Guid) - // // { - // // connection.RefreshLine(); // 主动更新某个控件相关的所有连接线 - // // //connection.RemoveFromCanvas(); - // // //BezierLineDrawer.UpdateBezierLine(FlowChartCanvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath); - // // } - // //} - //} - #endregion - #region 拖动画布实现缩放平移效果 private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e) { @@ -1607,7 +1571,7 @@ namespace Serein.Workbench // if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { if (e.Delta < 0 && scaleTransform.ScaleX < 0.05) return; - if (e.Delta > 0 && scaleTransform.ScaleY > 1.5) return; + if (e.Delta > 0 && scaleTransform.ScaleY > 2.0) return; // 获取鼠标在 Canvas 内的相对位置 var mousePosition = e.GetPosition(FlowChartCanvas); @@ -1781,7 +1745,7 @@ namespace Serein.Workbench /// private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { - if (GlobalJunctionData.MyGlobalConnectingData is not null) + if (GlobalJunctionData.MyGlobalConnectingData.IsCreateing) { return; } @@ -1835,36 +1799,52 @@ namespace Serein.Workbench // 释放鼠标捕获 FlowChartCanvas.ReleaseMouseCapture(); } - + // 创建连线 - if (GlobalJunctionData.MyGlobalConnectingData is not null) + if (GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing) { - var myData = GlobalJunctionData.MyGlobalConnectingData; - GlobalJunctionData.OK(); - var canvas = this.FlowChartCanvas; + if (myData.IsCanConnected) { + var canvas = this.FlowChartCanvas; var currentendPoint = e.GetPosition(canvas); // 当前鼠标落点 var changingJunctionPosition = myData.CurrentJunction.TranslatePoint(new Point(0, 0), canvas); var changingJunctionRect = new Rect(changingJunctionPosition, new Size(myData.CurrentJunction.Width, myData.CurrentJunction.Height)); - + if (changingJunctionRect.Contains(currentendPoint)) // 可以创建连接 { - var argIndex = 0; - if(myData.StartJunction is ArgJunctionControl argJunction1) + #region 方法调用关系创建 + if (myData.Type == JunctionOfConnectionType.Invoke) { - argIndex = argJunction1.ArgIndex; + this.EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + myData.StartJunction.JunctionType, + myData.CurrentJunction.JunctionType, + myData.ConnectionInvokeType); + } + #endregion + #region 参数来源关系创建 + else if (myData.Type == JunctionOfConnectionType.Arg) + { + var argIndex = 0; + if (myData.StartJunction is ArgJunctionControl argJunction1) + { + argIndex = argJunction1.ArgIndex; + } + else if (myData.CurrentJunction is ArgJunctionControl argJunction2) + { + argIndex = argJunction2.ArgIndex; + } + + this.EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + myData.StartJunction.JunctionType, + myData.CurrentJunction.JunctionType, + myData.ConnectionArgSourceType, + argIndex); + } + #endregion - } - else if (myData.CurrentJunction is ArgJunctionControl argJunction2) - { - argIndex = argJunction2.ArgIndex; - } - this.EnvDecorator.ConnectNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, - myData.StartJunction.JunctionType, - myData.CurrentJunction.JunctionType, - ConnectionInvokeType.IsSucceed,argIndex); } + EndConnection(); } } @@ -2605,6 +2585,7 @@ namespace Serein.Workbench #region 顶部菜单栏 - 远程管理 private async void ButtonStartRemoteServer_Click(object sender, RoutedEventArgs e) { + await this.EnvDecorator.StartRemoteServerAsync(); } @@ -2618,12 +2599,13 @@ namespace Serein.Workbench var windowEnvRemoteLoginView = new WindowEnvRemoteLoginView(async (addres, port, token) => { ResetFlowEnvironmentEvent();// 移除事件 - (var isConnect, RemoteEnvControl remoteEnvControl) = await this.EnvDecorator.ConnectRemoteEnv(addres, port, token); - InitFlowEnvironmentEvent(); // 重新添加时间(如果没有连接成功,那么依然是原本的环境) + (var isConnect, var _) = await this.EnvDecorator.ConnectRemoteEnv(addres, port, token); + InitFlowEnvironmentEvent(); // 重新添加事件(如果没有连接成功,那么依然是原本的环境) if (isConnect) { // 连接成功,加载远程项目 var flowEnvInfo = await EnvDecorator.GetEnvInfoAsync(); + await Task.Delay(1000); EnvDecorator.LoadProject(flowEnvInfo, string.Empty);// 加载远程环境的项目 } }); @@ -2635,7 +2617,7 @@ namespace Serein.Workbench /// - /// 按键监听。esc取消操作 + /// 窗体按键监听。 /// /// /// @@ -2647,16 +2629,53 @@ namespace Serein.Workbench } if (e.KeyStates == Keyboard.GetKeyStates(Key.Escape)) - //if (Keyboard.Modifiers == ModifierKeys.Shift) { - IsConnecting = false; IsControlDragging = false; IsCanvasDragging = false; - EndConnection(); SelectionRectangle.Visibility = Visibility.Collapsed; CancelSelectNode(); - + EndConnection(); } + + if(GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing) + { + if(myData.Type == JunctionOfConnectionType.Invoke) + { + ConnectionInvokeType connectionInvokeType = e.KeyStates switch + { + KeyStates k when k == Keyboard.GetKeyStates(Key.D1) => ConnectionInvokeType.Upstream, + KeyStates k when k == Keyboard.GetKeyStates(Key.D2) => ConnectionInvokeType.IsSucceed, + KeyStates k when k == Keyboard.GetKeyStates(Key.D3) => ConnectionInvokeType.IsFail, + KeyStates k when k == Keyboard.GetKeyStates(Key.D4) => ConnectionInvokeType.IsError, + _ => ConnectionInvokeType.None, + }; + + if (connectionInvokeType != ConnectionInvokeType.None) + { + myData.ConnectionInvokeType = connectionInvokeType; + myData.MyLine.Line.UpdateLineColor(connectionInvokeType.ToLineColor()); + } + } + else if (myData.Type == JunctionOfConnectionType.Arg) + { + ConnectionArgSourceType connectionArgSourceType = e.KeyStates switch + { + KeyStates k when k == Keyboard.GetKeyStates(Key.D1) => ConnectionArgSourceType.GetOtherNodeData, + KeyStates k when k == Keyboard.GetKeyStates(Key.D2) => ConnectionArgSourceType.GetOtherNodeDataOfInvoke, + _ => ConnectionArgSourceType.GetPreviousNodeData, + }; + + if (connectionArgSourceType != ConnectionArgSourceType.GetPreviousNodeData) + { + myData.ConnectionArgSourceType = connectionArgSourceType; + myData.MyLine.Line.UpdateLineColor(connectionArgSourceType.ToLineColor()); + } + } + myData.CurrentJunction.InvalidateVisual(); // 刷新目标节点控制点样式 + + } + + } /// @@ -2705,12 +2724,13 @@ namespace Serein.Workbench } ObjDynamicCreateHelper.PrintObjectProperties(result!); Console.WriteLine( ); - var exp = "@set .Addresses[1].Street = qwq"; + var exp = "@set .Addresses[1].Street = 233"; var data = SerinExpressionEvaluator.Evaluate(exp, result!, out bool isChange); exp = "@get .Addresses[1].Street"; data = SerinExpressionEvaluator.Evaluate(exp,result!, out isChange); Console.WriteLine($"{exp} => {data}"); } + /// /// 卸载DLL文件,清空当前项目 /// @@ -2719,9 +2739,8 @@ namespace Serein.Workbench private void UnloadAllButton_Click(object sender, RoutedEventArgs e) { EnvDecorator.ClearAll(); - - } + /// /// 卸载DLL文件,清空当前项目 /// @@ -2732,23 +2751,10 @@ namespace Serein.Workbench Connections.Clear(); NodeControls.Clear(); //currentLine = null; - startConnectNodeControl = null; + //startConnectNodeControl = null; MessageBox.Show("所有DLL已卸载。", "信息", MessageBoxButton.OK, MessageBoxImage.Information); } } - - #region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线 - - #region 拓展方法 Extension - - - #endregion - - - - - #endregion - } \ No newline at end of file diff --git a/WorkBench/MainWindowViewModel.cs b/WorkBench/MainWindowViewModel.cs index 3a2d45e..0aedffd 100644 --- a/WorkBench/MainWindowViewModel.cs +++ b/WorkBench/MainWindowViewModel.cs @@ -1,6 +1,7 @@ using Serein.Library.Api; using Serein.Library.Utils; using Serein.NodeFlow.Env; +using System.ComponentModel; using System.Windows; namespace Serein.Workbench @@ -9,9 +10,10 @@ namespace Serein.Workbench /// 工作台数据视图 /// /// - public class MainWindowViewModel + public class MainWindowViewModel: INotifyPropertyChanged { private readonly MainWindow window ; + /// /// 运行环境 /// @@ -46,5 +48,54 @@ namespace Serein.Workbench } + private bool _isConnectionInvokeNode = false; + /// + /// 是否正在连接节点的方法调用关系 + /// + public bool IsConnectionInvokeNode { get => _isConnectionInvokeNode; set + { + if (_isConnectionInvokeNode != value) + { + SetProperty(ref _isConnectionInvokeNode, value); + } + } + } + + private bool _isConnectionArgSouceNode = false; + /// + /// 是否正在连接节点的参数传递关系 + /// + public bool IsConnectionArgSourceNode { get => _isConnectionArgSouceNode; set + { + if (_isConnectionArgSouceNode != value) + { + SetProperty(ref _isConnectionArgSouceNode, value); + } + } + } + + + /// + /// 略 + /// 此事件为自动生成 + /// + public event PropertyChangedEventHandler? PropertyChanged; + /// + /// 通知属性变更 + /// + /// 类型 + /// 绑定的变量 + /// 新的数据 + /// + protected void SetProperty(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) + { + if (Equals(storage, value)) + { + return; + } + + storage = value; + PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); + } } } diff --git a/WorkBench/Node/NodeControlViewModelBase.cs b/WorkBench/Node/NodeControlViewModelBase.cs index 1b127ac..7e695ee 100644 --- a/WorkBench/Node/NodeControlViewModelBase.cs +++ b/WorkBench/Node/NodeControlViewModelBase.cs @@ -54,26 +54,26 @@ namespace Serein.Workbench.Node.ViewModel - //private bool isInterrupt; - /////// - /////// 控制中断状态的视觉效果 - /////// - //public bool IsInterrupt - //{ - // get => NodeModel.DebugSetting.IsInterrupt; - // set - // { - // NodeModel.DebugSetting.IsInterrupt = value; - // OnPropertyChanged(); - // } - //} + private bool isInterrupt; + ///// + ///// 控制中断状态的视觉效果 + ///// + public bool IsInterrupt + { + get => NodeModel.DebugSetting.IsInterrupt; + set + { + NodeModel.DebugSetting.IsInterrupt = value; + OnPropertyChanged(); + } + } - //public event PropertyChangedEventHandler? PropertyChanged; - //protected void OnPropertyChanged([CallerMemberName] string propertyName = null) - //{ - // //Console.WriteLine(propertyName); - // PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - //} + public event PropertyChangedEventHandler? PropertyChanged; + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + //Console.WriteLine(propertyName); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } /// diff --git a/WorkBench/Node/View/ActionNodeControl.xaml b/WorkBench/Node/View/ActionNodeControl.xaml index 43f9657..03db766 100644 --- a/WorkBench/Node/View/ActionNodeControl.xaml +++ b/WorkBench/Node/View/ActionNodeControl.xaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Serein.Workbench.Node.View" xmlns:vm="clr-namespace:Serein.Workbench.Node.ViewModel" - xmlns:Converters="clr-namespace:Serein.Workbench.Tool.Converters" + xmlns:converters="clr-namespace:Serein.Workbench.Tool.Converters" xmlns:themes="clr-namespace:Serein.Workbench.Themes" d:DataContext="{d:DesignInstance vm:ActionNodeControlViewModel}" mc:Ignorable="d" @@ -13,7 +13,7 @@ - + @@ -27,15 +27,17 @@ - - + + diff --git a/Workbench/Extension/LineExtension.cs b/Workbench/Extension/LineExtension.cs new file mode 100644 index 0000000..ce0eb42 --- /dev/null +++ b/Workbench/Extension/LineExtension.cs @@ -0,0 +1,53 @@ +using Serein.Library; +using Serein.Workbench.Node.View; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; + +namespace Serein.Workbench.Extension +{ + /// + /// 线条颜色 + /// + public static class LineExtension + { + /// + /// 根据连接类型指定颜色 + /// + /// + /// + /// + public static SolidColorBrush ToLineColor(this ConnectionInvokeType currentConnectionType) + { + return currentConnectionType switch + { + ConnectionInvokeType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")), // 04FC10 & 027E08 + ConnectionInvokeType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")), + ConnectionInvokeType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")), + ConnectionInvokeType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")), + ConnectionInvokeType.None => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")), + _ => throw new Exception(), + }; + } + /// + /// 根据连接类型指定颜色 + /// + /// + /// + /// + public static SolidColorBrush ToLineColor(this ConnectionArgSourceType connection) + { + return connection switch + { + ConnectionArgSourceType.GetPreviousNodeData => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")), // 04FC10 & 027E08 + ConnectionArgSourceType.GetOtherNodeData => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")), + ConnectionArgSourceType.GetOtherNodeDataOfInvoke => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#B06BBB")), + _ => throw new Exception(), + }; + } + + } +} diff --git a/Workbench/Node/Junction/BezierLine.cs b/Workbench/Node/Junction/ConnectionLineShape.cs similarity index 79% rename from Workbench/Node/Junction/BezierLine.cs rename to Workbench/Node/Junction/ConnectionLineShape.cs index 2196bc3..e8805c4 100644 --- a/Workbench/Node/Junction/BezierLine.cs +++ b/Workbench/Node/Junction/ConnectionLineShape.cs @@ -28,10 +28,12 @@ namespace Serein.Workbench.Node.View Semicircle, } + + /// /// 贝塞尔曲线 /// - public class BezierLine : Shape + public class ConnectionLineShape : Shape { private readonly double strokeThickness; @@ -40,28 +42,44 @@ namespace Serein.Workbench.Node.View /// /// 确定起始坐标和目标坐标、外光样式的曲线 /// - /// - /// - /// - /// - public BezierLine(LineType lineType, Point start, Point end, Brush brush, double strokeThickness = 4) + /// 线条类型 + /// 起始坐标 + /// 结束坐标 + /// 颜色 + /// 是否为虚线 + public ConnectionLineShape(LineType lineType, + Point start, + Point end, + Brush brush, + bool isDotted = false, + bool isTop = false) { this.lineType = lineType; this.brush = brush; startPoint = start; endPoint = end; - this.strokeThickness = strokeThickness; - InitElementPoint(); + this.strokeThickness = 4; + InitElementPoint(isDotted, isTop); InvalidateVisual(); // 触发重绘 } - public void InitElementPoint() + public void InitElementPoint(bool isDotted , bool isTop = false) { hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线 hitVisiblePen.Freeze(); // Freeze以提高性能 visualPen = new Pen(brush, 3.0); // 默认可视化Pen + if (isDotted) + { + visualPen.DashStyle = DashStyles.Dash; // 选择虚线样式 + } visualPen.Freeze(); // Freeze以提高性能 + linkSize = 4; // 整线条粗细 - Panel.SetZIndex(this, -9999999); // 置底 + int zIndex = -999999; + if (isTop) + { + zIndex *= -1; + } + Panel.SetZIndex(this, zIndex); // 置底 } /// @@ -104,16 +122,17 @@ namespace Serein.Workbench.Node.View switch (this.lineType) { case LineType.Bezier: - DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize); + DrawBezierCurve(drawingContext, startPoint, endPoint); break; case LineType.Semicircle: - DrawBezierCurve(drawingContext, startPoint, endPoint); + DrawSemicircleCurve(drawingContext, startPoint, endPoint); break; default: break; } } + #region 重绘 private readonly StreamGeometry streamGeometry = new StreamGeometry(); private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心 @@ -125,8 +144,12 @@ namespace Serein.Workbench.Node.View private Brush brush; // 线条颜色 double linkSize; // 根据缩放比例调整线条粗细 protected override Geometry DefiningGeometry => streamGeometry; - - #region 工具方法 + + public void UpdateLineColor(Brush brush) + { + visualPen = new Pen(brush, 3.0); // 默认可视化Pen + InvalidateVisual(); // 触发重绘 + } private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑 @@ -134,15 +157,10 @@ namespace Serein.Workbench.Node.View private Vector startToEnd; private void DrawBezierCurve(DrawingContext drawingContext, Point start, - Point end, - double linkSize, - bool isHitTestVisible = false, - double strokeThickness = 1.0, - bool isMouseOver = false, - double dashOffset = 0.0) + Point end) { // 控制点的计算逻辑 - double power = 100; // 控制贝塞尔曲线的“拉伸”强度 + double power = 140; // 控制贝塞尔曲线的“拉伸”强度 // 计算轴向向量与起点到终点的向量 //var axis = new Vector(1, 0); @@ -165,31 +183,16 @@ namespace Serein.Workbench.Node.View context.BeginFigure(start, true, false); // 曲线起点 context.BezierTo(c0, c1, end, true, false); // 画贝塞尔曲线 } - drawingContext.DrawGeometry(null, visualPen, streamGeometry); - - // 绘制碰撞检测线 - //if (true) - //{ - // //hitVisiblePen = new Pen(Brushes.Transparent, linkSize + strokeThickness); - // //hitVisiblePen.Freeze(); - // drawingContext.DrawGeometry(null, hitVisiblePen, streamGeometry); - //} - //else - //{ - - - //} - } - #endregion + - private void DrawBezierCurve(DrawingContext drawingContext, Point start, Point end) + private void DrawSemicircleCurve(DrawingContext drawingContext, Point start, Point end) { // 计算中心点和半径 // 计算圆心和半径 - double x = 35 ; + double x = 35; // 创建一个弧线路径 streamGeometry.Clear(); using (var context = streamGeometry.Open()) @@ -216,6 +219,7 @@ namespace Serein.Workbench.Node.View drawingContext.DrawGeometry(null, visualPen, streamGeometry); } + #endregion } diff --git a/Workbench/Node/Junction/JunctionControlBase.cs b/Workbench/Node/Junction/JunctionControlBase.cs index 9597215..af31965 100644 --- a/Workbench/Node/Junction/JunctionControlBase.cs +++ b/Workbench/Node/Junction/JunctionControlBase.cs @@ -4,6 +4,7 @@ using System; using System.Net; using System.Reflection; using System.Windows; +using Serein.Workbench.Extension; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; @@ -16,18 +17,20 @@ namespace Serein.Workbench.Node.View public abstract class JunctionControlBase : Shape { - - protected JunctionControlBase() { this.Width = 25; this.Height = 20; - this.MouseDown += ControlPointBase_MouseDown; - this.MouseMove += ControlPointBase_MouseMove; + this.MouseDown += JunctionControlBase_MouseDown; + this.MouseMove += JunctionControlBase_MouseMove; + this.MouseLeave += JunctionControlBase_MouseLeave; ; } + + #region 控件属性,所在的节点 public static readonly DependencyProperty NodeProperty = - DependencyProperty.Register(nameof(MyNode), typeof(NodeModelBase), typeof(JunctionControlBase), new PropertyMetadata(default(NodeModelBase))); + DependencyProperty.Register(nameof(MyNode), typeof(NodeModelBase), typeof(JunctionControlBase), new PropertyMetadata(default(NodeModelBase))); + //public NodeModelBase NodeModel; /// /// 所在的节点 @@ -52,6 +55,7 @@ namespace Serein.Workbench.Node.View set { SetValue(JunctionTypeProperty, value.ToString()); } } #endregion + protected readonly StreamGeometry StreamGeometry = new StreamGeometry(); protected override Geometry DefiningGeometry => StreamGeometry; @@ -65,15 +69,29 @@ namespace Serein.Workbench.Node.View /// public abstract Point MyCenterPoint { get; } - // 处理鼠标悬停状态 + + + /// + /// 禁止连接 + /// + private bool IsConnectionDisable; + + /// + /// 处理鼠标悬停状态 + /// private bool _isMouseOver; public bool IsMouseOver { get => _isMouseOver; set { - _isMouseOver = value; - InvalidateVisual(); + if(_isMouseOver != value) + { + GlobalJunctionData.MyGlobalConnectingData.CurrentJunction = this; + _isMouseOver = value; + InvalidateVisual(); + } + } } @@ -86,13 +104,63 @@ namespace Serein.Workbench.Node.View Render(drawingContext); } - - protected void ControlPointBase_MouseMove(object sender, MouseEventArgs e) + /// + /// 获取背景颜色 + /// + /// + protected Brush GetBackgrounp() { - if (GlobalJunctionData.MyGlobalConnectingData is null) return; - GlobalJunctionData.MyGlobalConnectingData.CurrentJunction = this; + var myData = GlobalJunctionData.MyGlobalConnectingData; + if(!myData.IsCreateing) + { + return Brushes.Transparent; + } + if (IsMouseOver) + { + if (myData.IsCanConnected) + { + if (myData.Type == JunctionOfConnectionType.Invoke) + { + return myData.ConnectionInvokeType.ToLineColor(); + } + else + { + return myData.ConnectionArgSourceType.ToLineColor(); + } + } + else + { + return Brushes.Red; + } + } + else + { + return Brushes.Transparent; + } } + private object lockObj = new object(); + + // 控件获得鼠标焦点事件 + private void JunctionControlBase_MouseMove(object sender, MouseEventArgs e) + { + //if (!GlobalJunctionData.MyGlobalConnectingData.IsCreateing) return; + + //if (IsMouseOver) return; + IsMouseOver = true; + + //this.InvalidateVisual(); + + + } + // 控件失去鼠标焦点事件 + private void JunctionControlBase_MouseLeave(object sender, MouseEventArgs e) + { + IsMouseOver = false; + e.Handled = true; + //Console.WriteLine("控件失去鼠标焦点"); + + } /// @@ -100,20 +168,43 @@ namespace Serein.Workbench.Node.View /// /// /// - protected void ControlPointBase_MouseDown(object sender, MouseButtonEventArgs e) + protected void JunctionControlBase_MouseDown(object sender, MouseButtonEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) { - GlobalJunctionData.MyGlobalConnectingData = new ConnectingData(); - var myDataType = GlobalJunctionData.MyGlobalConnectingData; - myDataType.StartJunction = this; var canvas = MainWindow.GetParentOfType(this); if (canvas != null) { - //myDataType.StartPoint = this.MyCenterPoint; - myDataType.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas); - var bezierLine = new BezierLine(LineType.Bezier, myDataType.StartPoint, myDataType.StartPoint, Brushes.Green); - myDataType.VirtualLine = new MyLine(canvas, bezierLine); + var myData = GlobalJunctionData.MyGlobalConnectingData; + myData.Reset(); + myData.IsCreateing = true; // 表示开始连接 + myData.StartJunction = this; + myData.CurrentJunction = this; + myData.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas); + + var junctionOfConnectionType = this.JunctionType.ToConnectyionType(); + ConnectionLineShape bezierLine; // 类别 + Brush brushColor; // 临时线的颜色 + if (junctionOfConnectionType == JunctionOfConnectionType.Invoke) + { + brushColor = ConnectionInvokeType.IsSucceed.ToLineColor(); + } + else if(junctionOfConnectionType == JunctionOfConnectionType.Arg) + { + brushColor = ConnectionArgSourceType.GetOtherNodeData.ToLineColor(); + } + else + { + return; + } + bezierLine = new ConnectionLineShape(LineType.Bezier, + myData.StartPoint, + myData.StartPoint, + brushColor, + isTop: true); // 绘制临时的线 + + Mouse.OverrideCursor = Cursors.Cross; // 设置鼠标为正在创建连线 + myData.MyLine = new MyLine(canvas, bezierLine); } } e.Handled = true; @@ -123,6 +214,11 @@ namespace Serein.Workbench.Node.View { return new Point(this.ActualWidth / 2, this.ActualHeight / 2); // 起始节点选择右侧边缘中心 } + + + + + } diff --git a/Workbench/Node/Junction/JunctionData.cs b/Workbench/Node/Junction/JunctionData.cs index e43b80b..ceb3048 100644 --- a/Workbench/Node/Junction/JunctionData.cs +++ b/Workbench/Node/Junction/JunctionData.cs @@ -1,7 +1,10 @@ -using System; +using Serein.Library; +using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -13,28 +16,60 @@ namespace Serein.Workbench.Node.View #region Model,不科学的全局变量 public class MyLine { - public MyLine(Canvas canvas, BezierLine line) + public MyLine(Canvas canvas, ConnectionLineShape line) { Canvas = canvas; - VirtualLine = line; + Line = line; canvas?.Children.Add(line); } public Canvas Canvas { get; set; } - public BezierLine VirtualLine { get; set; } + public ConnectionLineShape Line { get; set; } public void Remove() { - Canvas?.Children.Remove(VirtualLine); + Canvas?.Children.Remove(Line); } } public class ConnectingData { + + /// + /// 是否正在创建连线 + /// + public bool IsCreateing { get; set; } + /// + /// 起始控制点 + /// public JunctionControlBase StartJunction { get; set; } + /// + /// 当前的控制点 + /// public JunctionControlBase CurrentJunction { get; set; } + /// + /// 开始坐标 + /// public Point StartPoint { get; set; } - public MyLine VirtualLine { get; set; } + /// + /// 线条样式 + /// + public MyLine MyLine { get; set; } + + /// + /// 线条类别(方法调用) + /// + public ConnectionInvokeType ConnectionInvokeType { get; set; } = ConnectionInvokeType.IsSucceed; + /// + /// 线条类别(参数传递) + /// + public ConnectionArgSourceType ConnectionArgSourceType { get; set; } = ConnectionArgSourceType.GetOtherNodeData; + + /// + /// 判断当前连接类型 + /// + public JunctionOfConnectionType Type => StartJunction.JunctionType.ToConnectyionType(); + /// /// 是否允许连接 @@ -42,32 +77,22 @@ namespace Serein.Workbench.Node.View public bool IsCanConnected { get { + if(StartJunction is null || CurrentJunction is null ) { return false; } - if (!StartPoint.Equals(CurrentJunction)) + + + if (!StartJunction.MyNode.Equals(CurrentJunction.MyNode) + && StartJunction.JunctionType.IsCanConnection(CurrentJunction.JunctionType)) { return true; } else { - // 自己连接自己的情况下,只能是从arg控制点连接到execute控制点。 - if (CurrentJunction.JunctionType == Library.JunctionType.Execute - && StartJunction.JunctionType == Library.JunctionType.ArgData) - { - - return true; - } - - if (CurrentJunction.JunctionType == Library.JunctionType.ArgData - && StartJunction.JunctionType == Library.JunctionType.Execute) - { - // 需要是自己连接自己,且只能是从arg控制点连接到execute控制点。 - return true; - } return false; } } @@ -88,46 +113,48 @@ namespace Serein.Workbench.Node.View if (StartJunction.JunctionType == Library.JunctionType.Execute || StartJunction.JunctionType == Library.JunctionType.ArgData) { - VirtualLine.VirtualLine.UpdateStartPoints(point); + MyLine.Line.UpdateStartPoints(point); } else { - VirtualLine.VirtualLine.UpdateEndPoints(point); + MyLine.Line.UpdateEndPoints(point); } } + /// + /// 重置 + /// + public void Reset() + { + IsCreateing = false; + StartJunction = null; + CurrentJunction = null; + MyLine?.Remove(); + ConnectionInvokeType = ConnectionInvokeType.IsSucceed; + ConnectionArgSourceType = ConnectionArgSourceType.GetOtherNodeData; + } + } public static class GlobalJunctionData { - private static ConnectingData? myGlobalData; - private static object _lockObj = new object(); + //private static ConnectingData? myGlobalData; + //private static object _lockObj = new object(); /// /// 创建节点之间控制点的连接行为 /// - public static ConnectingData? MyGlobalConnectingData - { - get => myGlobalData; - set - { - lock (_lockObj) - { - myGlobalData ??= value; - } - } - } + public static ConnectingData MyGlobalConnectingData { get; } = new ConnectingData(); /// /// 删除连接视觉效果 /// public static void OK() { - myGlobalData?.VirtualLine.Remove(); - myGlobalData = null; + MyGlobalConnectingData.Reset(); } } #endregion diff --git a/Workbench/Node/Junction/View/ArgJunctionControl.cs b/Workbench/Node/Junction/View/ArgJunctionControl.cs index c9ca30e..d2f67ee 100644 --- a/Workbench/Node/Junction/View/ArgJunctionControl.cs +++ b/Workbench/Node/Junction/View/ArgJunctionControl.cs @@ -30,29 +30,27 @@ namespace Serein.Workbench.Node.View #endregion private Point _myCenterPoint; public override Point MyCenterPoint { get => _myCenterPoint; } + + public override void Render(DrawingContext drawingContext) { double width = ActualWidth; double height = ActualHeight; - + var background = GetBackgrounp(); // 输入连接器的背景 - var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent; - var connectorRect = new Rect(4, 4, width - 8, height - 8); - drawingContext.DrawRectangle(connectorBackground, null, connectorRect); + var connectorRect = new Rect(0, 0, width, height); + drawingContext.DrawRectangle(Brushes.Transparent, null, connectorRect); // 定义圆形的大小和位置 double connectorSize = 10; // 连接器的大小 double circleCenterX = 8; // 圆心 X 坐标 double circleCenterY = height / 2; // 圆心 Y 坐标 var circlePoint = new Point(circleCenterX, circleCenterY); + _myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY); // 中心坐标 // 绘制连接器的圆形部分 var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); - _myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY); - drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse); - - - + drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse); // 定义三角形的间距 double triangleOffsetX = 4; // 三角形与圆形的间距 @@ -68,7 +66,7 @@ namespace Serein.Workbench.Node.View context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false); } - drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry); + drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry); } } diff --git a/Workbench/Node/Junction/View/ExecuteJunctionControl.cs b/Workbench/Node/Junction/View/ExecuteJunctionControl.cs index b86bbd7..c9309e4 100644 --- a/Workbench/Node/Junction/View/ExecuteJunctionControl.cs +++ b/Workbench/Node/Junction/View/ExecuteJunctionControl.cs @@ -23,7 +23,7 @@ namespace Serein.Workbench.Node.View { double width = ActualWidth; double height = ActualHeight; - + var background = GetBackgrounp(); // 绘制边框 //var borderBrush = new SolidColorBrush(Colors.Black); //var borderThickness = 1.0; @@ -31,22 +31,22 @@ namespace Serein.Workbench.Node.View //drawingContext.DrawRectangle(null, new Pen(borderBrush, borderThickness), borderRect); // 输入连接器的背景 - var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent; - var connectorRect = new Rect(4, 4, width - 8, height - 8); - drawingContext.DrawRectangle(connectorBackground, null, connectorRect); + var connectorRect = new Rect(0, 0, width, height); + drawingContext.DrawRectangle(Brushes.Transparent,null, connectorRect); + //drawingContext.DrawRectangle(Brushes.Transparent, new Pen(background,2), connectorRect); // 定义圆形的大小和位置 double connectorSize = 10; // 连接器的大小 double circleCenterX = 8; // 圆心 X 坐标 double circleCenterY = height / 2; // 圆心 Y 坐标 + _myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY); // 中心坐标 var circlePoint = new Point(circleCenterX, circleCenterY); // 绘制连接器的圆形部分 var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); - _myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY); - drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse); + drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse); @@ -65,7 +65,7 @@ namespace Serein.Workbench.Node.View context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false); } - drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry); + drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry); // 绘制标签 //var formattedText = new FormattedText( diff --git a/Workbench/Node/Junction/View/NextStepJunctionControl.cs b/Workbench/Node/Junction/View/NextStepJunctionControl.cs index 003f03b..9dd1e65 100644 --- a/Workbench/Node/Junction/View/NextStepJunctionControl.cs +++ b/Workbench/Node/Junction/View/NextStepJunctionControl.cs @@ -20,26 +20,25 @@ namespace Serein.Workbench.Node.View { double width = ActualWidth; double height = ActualHeight; - + var background = GetBackgrounp(); // 输入连接器的背景 - var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent; - var connectorRect = new Rect(4, 4, width - 8, height - 8); - drawingContext.DrawRectangle(connectorBackground, null, connectorRect); + var connectorRect = new Rect(0, 0, width, height); + drawingContext.DrawRectangle(Brushes.Transparent, null, connectorRect); // 定义圆形的大小和位置 double connectorSize = 10; // 连接器的大小 double circleCenterX = 8; // 圆心 X 坐标 double circleCenterY = height / 2; // 圆心 Y 坐标 + _myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY); // 中心坐标 var circlePoint = new Point(circleCenterX, circleCenterY); // 绘制连接器的圆形部分 var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); - drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse); - _myCenterPoint = new Point(circleCenterX + connectorSize / 2, circleCenterY); + drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse); // 绘制连接器的圆形部分 //var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); - + // 定义三角形的间距 double triangleOffsetX = 4; // 三角形与圆形的间距 @@ -55,7 +54,7 @@ namespace Serein.Workbench.Node.View context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false); } - drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry); + drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry); } } } diff --git a/Workbench/Node/Junction/View/ResultJunctionControl.cs b/Workbench/Node/Junction/View/ResultJunctionControl.cs index 15b0de3..4e7f6db 100644 --- a/Workbench/Node/Junction/View/ResultJunctionControl.cs +++ b/Workbench/Node/Junction/View/ResultJunctionControl.cs @@ -24,9 +24,10 @@ namespace Serein.Workbench.Node.View double height = ActualHeight; // 输入连接器的背景 - var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent; - var connectorRect = new Rect(4, 4, width - 8, height - 8); - drawingContext.DrawRectangle(connectorBackground, null, connectorRect); + var connectorRect = new Rect(0, 0, width, height); + drawingContext.DrawRectangle(Brushes.Transparent, null, connectorRect); + + var background = GetBackgrounp(); // 定义圆形的大小和位置 double connectorSize = 10; // 连接器的大小 @@ -34,10 +35,11 @@ namespace Serein.Workbench.Node.View double circleCenterY = height / 2; // 圆心 Y 坐标 var circlePoint = new Point(circleCenterX, circleCenterY); + _myCenterPoint = new Point(circleCenterX - connectorSize / 2 , circleCenterY); // 中心坐标 + // 绘制连接器的圆形部分 var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); - _myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY); - drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse); + drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse); // 定义三角形的间距 double triangleOffsetX = 4; // 三角形与圆形的间距 @@ -53,7 +55,7 @@ namespace Serein.Workbench.Node.View context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false); } - drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry); + drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry); } } } diff --git a/Workbench/Node/View/ConnectionControl.cs b/Workbench/Node/View/ConnectionControl.cs index 835fdb5..e32014e 100644 --- a/Workbench/Node/View/ConnectionControl.cs +++ b/Workbench/Node/View/ConnectionControl.cs @@ -112,52 +112,6 @@ namespace Serein.Workbench.Node.View private readonly Action RemoteCallback; - /// - /// 关于调用 - /// - /// - /// - public ConnectionControl(Canvas Canvas, - ConnectionInvokeType Type, - JunctionControlBase Start, - JunctionControlBase End, - Action remoteCallback) - { - this.LineType = LineType.Bezier; - this.RemoteCallback = remoteCallback; - this.Canvas = Canvas; - this.Type = Type; - this.Start = Start; - this.End = End; - //this.Start.Background = GetLineColor(Type); // 线条颜色 - //this.End.Background = GetLineColor(Type); // 线条颜色 - InitElementPoint(); - } - - /// - /// 关于入参 - /// - /// - /// - public ConnectionControl(LineType LineType, - Canvas Canvas, - int argIndex, - ConnectionArgSourceType connectionArgSourceType, - JunctionControlBase Start, - JunctionControlBase End, - Action remoteCallback) - { - this.LineType = LineType; - this.RemoteCallback = remoteCallback; - this.Canvas = Canvas; - this.ArgIndex = ArgIndex; - this.ConnectionArgSourceType = connectionArgSourceType; - this.Start = Start; - this.End = End; - //this.Start.Background = GetLineColor(Type); // 线条颜色 - //this.End.Background = GetLineColor(Type); // 线条颜色 - InitElementPoint(); - } /// /// 所在的画布 @@ -167,7 +121,7 @@ namespace Serein.Workbench.Node.View /// /// 调用方法类型,连接类型 /// - public ConnectionInvokeType Type { get; } + public ConnectionInvokeType InvokeType { get; } /// /// 获取参数类型,第几个参数 @@ -177,7 +131,7 @@ namespace Serein.Workbench.Node.View /// /// 参数来源(决定了连接线的样式) /// - public ConnectionArgSourceType ConnectionArgSourceType { get; set; } + public ConnectionArgSourceType ArgSourceType { get; set; } /// /// 起始控制点 @@ -192,14 +146,86 @@ namespace Serein.Workbench.Node.View /// /// 连接线 /// - private BezierLine BezierLine; + private ConnectionLineShape BezierLine; private LineType LineType; + /// + /// 关于调用 + /// + /// + /// + public ConnectionControl(Canvas Canvas, + ConnectionInvokeType invokeType, + JunctionControlBase Start, + JunctionControlBase End, + Action remoteCallback) + { + this.LineType = LineType.Bezier; + this.RemoteCallback = remoteCallback; + this.Canvas = Canvas; + this.InvokeType = invokeType; + this.Start = Start; + this.End = End; + InitElementPoint(); + } + + /// + /// 关于入参 + /// + /// + /// + public ConnectionControl(LineType LineType, + Canvas Canvas, + int argIndex, + ConnectionArgSourceType argSourceType, + JunctionControlBase Start, + JunctionControlBase End, + Action remoteCallback) + { + this.LineType = LineType; + this.RemoteCallback = remoteCallback; + this.Canvas = Canvas; + this.ArgIndex = ArgIndex; + this.ArgSourceType = argSourceType; + this.Start = Start; + this.End = End; + InitElementPoint(); + } + + /// + /// 绘制 + /// + public void InitElementPoint() + { + leftCenterOfEndLocation = Start.MyCenterPoint; + rightCenterOfStartLocation = End.MyCenterPoint; + + (Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End); + var connectionType = Start.JunctionType.ToConnectyionType(); + bool isDotted; + Brush brush; + if(connectionType == JunctionOfConnectionType.Invoke) + { + brush = InvokeType.ToLineColor(); + isDotted = false; + } + else + { + brush = ArgSourceType.ToLineColor(); + isDotted = true; // 如果为参数,则绘制虚线 + } + BezierLine = new ConnectionLineShape(LineType, startPoint, endPoint, brush, isDotted); + Grid.SetZIndex(BezierLine, -9999999); // 置底 + Canvas.Children.Add(BezierLine); + + ConfigureLineContextMenu(); //配置右键菜单 + } + + /// /// 配置连接曲线的右键菜单 /// - /// private void ConfigureLineContextMenu() { var contextMenu = new ContextMenu(); @@ -213,17 +239,15 @@ namespace Serein.Workbench.Node.View /// public void DeleteConnection() { - if(this.Start is JunctionControlBase startJunctionControlBase) - { - //startJunctionControlBase.Background = Brushes.Transparent; - } - if (this.End is JunctionControlBase endJunctionControlBase) - { - //endJunctionControlBase.Background = Brushes.Transparent; - } - Canvas.Children.Remove(BezierLine); // 移除线 RemoteCallback?.Invoke(); } + /// + /// 删除该连线 + /// + public void Remote() + { + Canvas.Children.Remove(BezierLine); + } /// /// 重新绘制 @@ -232,38 +256,8 @@ namespace Serein.Workbench.Node.View { (Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End); BezierLine.UpdatePoints(startPoint, endPoint); - //BezierLine.UpdatePoints(startPoint, endPoint); - } - /// - /// 删除该连线 - /// - public void Remote() - { - Canvas.Children.Remove(BezierLine); - } - /// - /// 绘制 - /// - public void InitElementPoint() - { - leftCenterOfEndLocation = Start.MyCenterPoint; - rightCenterOfStartLocation = End.MyCenterPoint; - //leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心 - //rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心 - - - linkSize = 4; // 整线条粗细 - (Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End); - BezierLine = new BezierLine(LineType, startPoint, endPoint, GetLineColor(Type), linkSize); - Grid.SetZIndex(BezierLine, -9999999); // 置底 - Canvas.Children.Add(BezierLine); - - - ConfigureLineContextMenu(); //配置右键菜单 } - - double linkSize; // 根据缩放比例调整线条粗细 private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心 private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心 @@ -275,24 +269,8 @@ namespace Serein.Workbench.Node.View return (startPoint, endPoint); } - /// - /// 根据连接类型指定颜色 - /// - /// - /// - /// - public static SolidColorBrush GetLineColor(ConnectionInvokeType currentConnectionType) - { - return currentConnectionType switch - { - ConnectionInvokeType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")), - ConnectionInvokeType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")), - ConnectionInvokeType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")), - ConnectionInvokeType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")), - _ => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")), - //_ => throw new Exception(), - }; - } + + #endregion }