diff --git a/FlowStartTool/Program.cs b/FlowStartTool/Program.cs index b84451f..1b30d8d 100644 --- a/FlowStartTool/Program.cs +++ b/FlowStartTool/Program.cs @@ -13,7 +13,7 @@ namespace Serein.FlowStartTool { public static void Main(string[] args) { -#if true +#if debug args = [@"F:\临时\project\linux\project.dnf"]; #endif diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index 5232b71..f743537 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -263,9 +263,9 @@ namespace Serein.Library /// /// 执行单个节点对应的方法,并不做状态检查 /// - /// + /// 运行时上下文 /// - public virtual async Task InvokeAsync(IFlowEnvironment env) + public virtual async Task InvokeAsync(IDynamicContext context) { try { @@ -274,16 +274,16 @@ namespace Serein.Library { throw new Exception($"不存在方法信息{md.MethodName}"); } - if (!env.TryGetDelegateDetails(md.MethodName, out var dd)) + if (!Env.TryGetDelegateDetails(md.MethodName, out var dd)) { throw new Exception($"不存在对应委托{md.MethodName}"); } if (md.ActingInstance is null) { - md.ActingInstance = env.IOC.Get(md.ActingInstanceType); + md.ActingInstance = Env.IOC.Get(md.ActingInstanceType); if (md.ActingInstance is null) { - md.ActingInstance = env.IOC.Instantiate(md.ActingInstanceType); + md.ActingInstance = Env.IOC.Instantiate(md.ActingInstanceType); if (md.ActingInstance is null) { throw new Exception($"无法创建相应的实例{md.ActingInstanceType.FullName}"); @@ -291,7 +291,7 @@ namespace Serein.Library } } - object[] args = await GetParametersAsync(null, this, md); + object[] args = await GetParametersAsync(context, this, md); var result = await dd.InvokeAsync(md.ActingInstance, args); return result; } @@ -368,7 +368,6 @@ namespace Serein.Library else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) { // 立刻调用对应节点获取数据。 - var result = await context.Env.InvokeNodeAsync(ed.ArgDataSourceNodeGuid); inputParameter = result; } diff --git a/Library/Network/WebSocket/Attribute.cs b/Library/Network/WebSocket/Attribute.cs index 0096a8e..af189a7 100644 --- a/Library/Network/WebSocket/Attribute.cs +++ b/Library/Network/WebSocket/Attribute.cs @@ -25,8 +25,17 @@ namespace Serein.Library.Network.WebSocketCommunication [AttributeUsage(AttributeTargets.Class)] public sealed class AutoSocketModuleAttribute : Attribute { + /// + /// 业务标识 + /// public string ThemeKey; + /// + /// 数据标识 + /// public string DataKey; + /// + /// ID标识 + /// public string MsgIdKey; } @@ -55,10 +64,10 @@ namespace Serein.Library.Network.WebSocketCommunication public string ThemeValue = string.Empty; /// /// 标记方法执行完成后是否需要将结果发送。 - /// 但以下情况将不会发送: - /// 1.返回类型为void - /// 2.返回类型为Task - /// 3.返回了null + /// 注意以下返回值,返回的 json 中将不会新建 DataKey 字段: + /// 1.返回类型为 void + /// 2.返回类型为 Task + /// 2.返回类型为 Unit /// 补充:如果返回类型是Task<TResult> /// 会进行异步等待,当Task结束后,自动获取TResult进行发送(请避免Task<Task<TResult>>诸如此类的Task泛型嵌套) /// diff --git a/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs b/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs index e19a994..d374f16 100644 --- a/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs +++ b/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs @@ -89,9 +89,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// 处理JSON数据 /// - public async void HandleSocketMsg(WebSocketMsgContext context) // Func sendAsync, JObject jsonObject + public async Task HandleAsync(WebSocketMsgContext context) { - var jsonObject = context.JsonObject; // 获取到消息 string theme = jsonObject.GetValue(moduleConfig.ThemeJsonKey)?.ToString(); if (!MyHandleConfigs.TryGetValue(theme, out var handldConfig)) @@ -112,7 +111,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle { var dataObj = jsonObject.GetValue(moduleConfig.DataJsonKey)?.ToObject(); context.MsgData = dataObj; // 添加消息 - if (TryGetParameters(handldConfig, context, out var args)) + if (WebSocketHandleModule.TryGetParameters(handldConfig, context, out var args)) { var result = await WebSocketHandleModule.HandleAsync(handldConfig, args); if (handldConfig.IsReturnValue) @@ -149,7 +148,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// 处理上下文 /// 返回的入参参数 /// - internal static bool TryGetParameters(HandleConfiguration config,WebSocketMsgContext context, out object[] args) + internal static bool TryGetParameters(HandleConfiguration config, WebSocketMsgContext context, out object[] args) { args = new object[config.ParameterType.Length]; bool isCanInvoke = true; ; // 表示是否可以调用方法 diff --git a/Library/Network/WebSocket/Handle/WebSocketMsgContext.cs b/Library/Network/WebSocket/Handle/WebSocketMsgContext.cs index 3785f54..bdc7101 100644 --- a/Library/Network/WebSocket/Handle/WebSocketMsgContext.cs +++ b/Library/Network/WebSocket/Handle/WebSocketMsgContext.cs @@ -11,23 +11,28 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// 消息处理上下文 /// - public class WebSocketMsgContext + public class WebSocketMsgContext : IDisposable { public WebSocketMsgContext(Func sendAsync) { this._sendAsync = sendAsync; } + + public void Dispose() + { + JsonObject = null; + MsgTheme = null; + MsgId = null; + MsgData = null; + MsgData = null; + _sendAsync = null; + } /// /// 标记是否已经处理,如果是,则提前退出 /// public bool Handle { get; set; } - /// - /// 消息本体(文本) - /// - public string Msg { get; set; } - /// /// 消息本体(JObject) /// @@ -107,6 +112,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle await SendAsync(msg); } + } } diff --git a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs index fec41a0..c957de4 100644 --- a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs +++ b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs @@ -17,6 +17,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Diagnostics.CodeAnalysis; +using System.Reactive; namespace Serein.Library.Network.WebSocketCommunication.Handle { @@ -118,14 +119,30 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle } #region 生成处理配置 - var config = new WebSocketHandleConfiguration - { - IsReturnValue = methodsAttribute.IsReturnValue, - ThemeValue = methodsAttribute.ThemeValue, - ArgNotNull = methodsAttribute.ArgNotNull, - }; - var parameterInfos = methodInfo.GetParameters(); + var config = new WebSocketHandleConfiguration(); + config.ThemeValue = methodsAttribute.ThemeValue; + config.ArgNotNull = methodsAttribute.ArgNotNull; + config.IsReturnValue = methodsAttribute.IsReturnValue; + //if (config.IsReturnValue) + //{ + // // 重新检查是否能够返回 + // if (methodInfo.ReturnType == typeof(void)) + // { + // config.IsReturnValue = false; // void 不返回 + // } + // else if (methodInfo.ReturnType == typeof(Unit)) + // { + // config.IsReturnValue = false; // Unit 不返回 + // } + // else if (methodInfo.ReturnType == typeof(Task)) + // { + // config.IsReturnValue = false; // void 不返回 + // } + + //} + var parameterInfos = methodInfo.GetParameters(); + config.DelegateDetails = new DelegateDetails(methodInfo); // 对应theme的emit构造委托调用工具类 config.Instance = socketControlBase; // 调用emit委托时的实例 config.OnExceptionTracking = onExceptionTracking; // 异常追踪 @@ -184,12 +201,11 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// 此次请求的上下文 /// - public void HandleMsg(WebSocketMsgContext context) + public async Task HandleAsync(WebSocketMsgContext context) { - // OnExceptionTracking foreach (var module in MyHandleModuleDict.Values) { - module.HandleSocketMsg(context); + await module.HandleAsync(context); } } diff --git a/Library/Network/WebSocket/WebSocketClient.cs b/Library/Network/WebSocket/WebSocketClient.cs index 439b1ef..94234be 100644 --- a/Library/Network/WebSocket/WebSocketClient.cs +++ b/Library/Network/WebSocket/WebSocketClient.cs @@ -129,21 +129,28 @@ namespace Serein.Library.Network.WebSocketCommunication public async Task HandleMsgAsync(WebSocket webSocket, MsgQueueUtil msgQueueUtil) { - + async Task sendasync(string text) + { + await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 + } while (true) { var message = await msgQueueUtil.WaitMsgAsync(); // 有消息时通知 + using (var context = new WebSocketMsgContext(sendasync)) + { + context.JsonObject = JObject.Parse(message); + await MsgHandleHelper.HandleAsync(context); // 处理消息 + } - - _ = Task.Run(() => { - JObject json = JObject.Parse(message); - WebSocketMsgContext context = new WebSocketMsgContext(async (text) => - { - await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 - }); - context.JsonObject = json; - MsgHandleHelper.HandleMsg(context); // 处理消息 - }); + //_ = Task.Run(() => { + // JObject json = JObject.Parse(message); + // WebSocketMsgContext context = new WebSocketMsgContext(async (text) => + // { + // await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 + // }); + // context.JsonObject = json; + // await MsgHandleHelper.HandleAsync(context); // 处理消息 + //}); } diff --git a/Library/Network/WebSocket/WebSocketServer.cs b/Library/Network/WebSocket/WebSocketServer.cs index 71d6a30..240727f 100644 --- a/Library/Network/WebSocket/WebSocketServer.cs +++ b/Library/Network/WebSocket/WebSocketServer.cs @@ -234,7 +234,10 @@ namespace Serein.Library.Network.WebSocketCommunication MsgQueueUtil msgQueueUtil, WebSocketAuthorizedHelper authorizedHelper) { - + async Task sendasync(string text) + { + await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 + } while (true) { var message = await msgQueueUtil.WaitMsgAsync(); // 有消息时通知 @@ -251,16 +254,16 @@ namespace Serein.Library.Network.WebSocketCommunication return; } } - - _ = Task.Run(() => { - JObject json = JObject.Parse(message); - WebSocketMsgContext context = new WebSocketMsgContext(async (text) => - { - await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入 - }); - context.JsonObject = json; - MsgHandleHelper.HandleMsg(context); // 处理消息 - }); + + using (var context = new WebSocketMsgContext(sendasync)) + { + context.JsonObject = JObject.Parse(message); + await MsgHandleHelper.HandleAsync(context); // 处理消息 + } + //_ = Task.Run(() => { + + + //}); } diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 10f3f94..4037dba 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.Core.NodeFlow; using Serein.Library.FlowNode; using Serein.Library.Utils; using Serein.Library.Utils.SereinExpression; @@ -407,21 +408,18 @@ namespace Serein.NodeFlow.Env /// /// 单独运行一个节点 /// - /// /// /// public async Task InvokeNodeAsync(string nodeGuid) { - - - if(this.NodeModels.TryGetValue(nodeGuid, out var model)) + IDynamicContext context = new DynamicContext(this); + object result = true; + if (this.NodeModels.TryGetValue(nodeGuid, out var model)) { - return await model.InvokeAsync(this); - } - else - { - return null; + result = await model.InvokeAsync(context); } + context.Exit(); + return result; } /// diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs index 9d4164c..e5b0280 100644 --- a/NodeFlow/Env/FlowEnvironmentDecorator.cs +++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs @@ -364,9 +364,9 @@ namespace Serein.NodeFlow.Env await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid); } - public async Task InvokeNodeAsync(string nodeGuid) + public async Task InvokeNodeAsync( string nodeGuid) { - return await currentFlowEnvironment.InvokeNodeAsync(nodeGuid); + return await currentFlowEnvironment.InvokeNodeAsync( nodeGuid); } public async Task StartRemoteServerAsync(int port = 7525) diff --git a/NodeFlow/Env/MsgControllerOfClient.cs b/NodeFlow/Env/MsgControllerOfClient.cs index 9e16a41..f85f72d 100644 --- a/NodeFlow/Env/MsgControllerOfClient.cs +++ b/NodeFlow/Env/MsgControllerOfClient.cs @@ -78,7 +78,7 @@ namespace Serein.NodeFlow.Env /// /// /// - [AutoSocketHandle(ThemeValue = EnvMsgTheme.GetEnvInfo)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.GetEnvInfo, IsReturnValue = false)] public void GetEnvInfo([UseMsgId] string msgId, [UseData] FlowEnvInfo flowEnvInfo) { remoteFlowEnvironment.TriggerSignal(msgId, flowEnvInfo); @@ -90,19 +90,19 @@ namespace Serein.NodeFlow.Env /// /// /// - [AutoSocketHandle(ThemeValue = EnvMsgTheme.GetProjectInfo)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.GetProjectInfo, IsReturnValue = false)] public void GetProjectInfo([UseMsgId] string msgId, [UseData] SereinProjectData sereinProjectData) { remoteFlowEnvironment.TriggerSignal(msgId, sereinProjectData); } - [AutoSocketHandle(ThemeValue = EnvMsgTheme.SetNodeInterrupt)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.SetNodeInterrupt, IsReturnValue = false)] public void SetNodeInterrupt([UseMsgId] string msgId) { remoteFlowEnvironment.TriggerSignal(msgId, null); } - [AutoSocketHandle(ThemeValue = EnvMsgTheme.AddInterruptExpression)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.AddInterruptExpression, IsReturnValue = false)] public void AddInterruptExpression([UseMsgId] string msgId) { remoteFlowEnvironment.TriggerSignal(msgId, null); @@ -110,37 +110,37 @@ namespace Serein.NodeFlow.Env - [AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode, IsReturnValue = false)] public void CreateNode([UseMsgId] string msgId, [UseData] NodeInfo nodeInfo) { remoteFlowEnvironment.TriggerSignal(msgId, nodeInfo); } - [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode, IsReturnValue = false)] public void RemoveNode([UseMsgId] string msgId, bool state) { remoteFlowEnvironment.TriggerSignal(msgId, state); } - [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectInvokeNode)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectInvokeNode, IsReturnValue = false)] public void ConnectInvokeNode([UseMsgId] string msgId, bool state) { remoteFlowEnvironment.TriggerSignal(msgId, state); } - [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveInvokeConnect)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveInvokeConnect, IsReturnValue = false)] public void RemoveInvokeConnect([UseMsgId] string msgId, bool state) { remoteFlowEnvironment.TriggerSignal(msgId, state); } - [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectArgSourceNode)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectArgSourceNode, IsReturnValue = false)] public void ConnectArgSourceNode([UseMsgId] string msgId, bool state) { remoteFlowEnvironment.TriggerSignal(msgId, state); } - [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveArgSourceConnect)] + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveArgSourceConnect, IsReturnValue = false)] public void RemoveArgSourceConnect([UseMsgId] string msgId, bool state) { remoteFlowEnvironment.TriggerSignal(msgId, state); diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index e7f799d..31db32a 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -358,33 +358,42 @@ namespace Serein.NodeFlow /// private async Task FlipflopExecuteAsync(IFlowEnvironment env, SingleFlipflopNode singleFlipFlopNode, CancellationTokenSource cts) { - var context = new DynamicContext(env); // 启动全局触发器时新建上下文 + if(_flipFlopCts is null) + { + Console.WriteLine("flowStarter -> FlipflopExecuteAsync -> _flipFlopCts is null"); + return; + } while (!_flipFlopCts.IsCancellationRequested && !cts.IsCancellationRequested) { + var context = new DynamicContext(env); // 启动全局触发器时新建上下文 try { var newFlowData = await singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task await NodeModelBase.RefreshFlowDataAndExpInterrupt(context, singleFlipFlopNode, newFlowData); // 全局触发器触发后刷新该触发器的节点数据 - if (context.NextOrientation != ConnectionInvokeType.None) + if (context.NextOrientation == ConnectionInvokeType.None) { - var nextNodes = singleFlipFlopNode.SuccessorNodes[context.NextOrientation]; - for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--) - { - // 筛选出启用的节点 - if (nextNodes[i].DebugSetting.IsEnable) - { - nextNodes[i].PreviousNode = singleFlipFlopNode; - if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前 - { - var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask(); - await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); - } - await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点 - } - } + continue; } + var nextNodes = singleFlipFlopNode.SuccessorNodes[context.NextOrientation]; + for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--) + { + // 筛选出启用的节点 + if (!nextNodes[i].DebugSetting.IsEnable) + { + continue; + } + + nextNodes[i].PreviousNode = singleFlipFlopNode; + if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前 + { + var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask(); + await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); + } + await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点 + } + } - catch(FlipflopException ex) + catch (FlipflopException ex) { await Console.Out.WriteLineAsync($"触发器[{singleFlipFlopNode.MethodDetails.MethodName}]因非预期异常终止。"+ex.Message); if (ex.Type == FlipflopException.CancelClass.Flow) @@ -396,6 +405,10 @@ namespace Serein.NodeFlow { await Console.Out.WriteLineAsync(ex.Message); } + finally + { + context.Exit(); + } } } diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs index 882fe6f..31cb4af 100644 --- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs +++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs @@ -9,35 +9,7 @@ namespace Serein.NodeFlow.Tool; public static class NodeMethodDetailsHelper { - - /// - /// 生成方法信息 - /// - /// - /// - /// - //public static List GetList(Type type) - //{ - // var methodDetailsDictionary = new List(); - // var delegateDictionary = new List(); - // var assemblyName = type.Assembly.GetName().Name; - // var methods = GetMethodsToProcess(type); - // foreach (var method in methods) - // { - - // (var methodDetails,var methodDelegate) = CreateMethodDetails(type, method, assemblyName); - - // methodDetailsDictionary.Add(methodDetails); - // delegateDictionary.Add(methodDelegate); - // } - - // var mds = methodDetailsDictionary.OrderBy(it => it.MethodName).ToList(); - // var dels = delegateDictionary; - - // return mds; - //} - /// /// 获取处理方法 /// diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index 47b79f8..bcc31fe 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -13,7 +13,7 @@ namespace Serein.Workbench void LoadLocalProject() { #if DEBUG - if (1 == 11) + if (1 == 1) { string filePath; filePath = @"F:\临时\project\linux\project.dnf"; diff --git a/WorkBench/MainWindow.xaml b/WorkBench/MainWindow.xaml index a89165a..a5c1efe 100644 --- a/WorkBench/MainWindow.xaml +++ b/WorkBench/MainWindow.xaml @@ -213,17 +213,17 @@ Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Con Visibility="{Binding IsConnectionInvokeNode, Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" > - - - - + + + + - - + + diff --git a/WorkBench/Node/View/ActionNodeControl.xaml b/WorkBench/Node/View/ActionNodeControl.xaml index 03db766..bd25f67 100644 --- a/WorkBench/Node/View/ActionNodeControl.xaml +++ b/WorkBench/Node/View/ActionNodeControl.xaml @@ -18,14 +18,11 @@ - - - - + diff --git a/WorkBench/Node/View/ActionNodeControl.xaml.cs b/WorkBench/Node/View/ActionNodeControl.xaml.cs index f5b4068..c223617 100644 --- a/WorkBench/Node/View/ActionNodeControl.xaml.cs +++ b/WorkBench/Node/View/ActionNodeControl.xaml.cs @@ -23,10 +23,10 @@ namespace Serein.Workbench.Node.View /// 入参控制点(可能有,可能没) /// JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl; + /// /// 下一个调用方法控制点(可能有,可能没) /// - JunctionControlBase INodeJunction.NextStepJunction => this.NextStepJunctionControl; /// @@ -34,6 +34,10 @@ namespace Serein.Workbench.Node.View /// JunctionControlBase INodeJunction.ReturnDataJunction => this.ResultJunctionControl; + /// + /// 方法入参控制点(可能有,可能没) + /// + private JunctionControlBase[] argDataJunction; /// /// 方法入参控制点(可能有,可能没) /// @@ -67,28 +71,8 @@ namespace Serein.Workbench.Node.View return argDataJunction; } } - /// - /// 方法入参控制点(可能有,可能没) - /// - private JunctionControlBase[] argDataJunction; + - private T FindVisualChild(DependencyObject parent) where T : DependencyObject - { - for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) - { - var child = VisualTreeHelper.GetChild(parent, i); - if (child is T typedChild) - { - return typedChild; - } - - var childOfChild = FindVisualChild(child); - if (childOfChild != null) - { - return childOfChild; - } - } - return null; - } + } } diff --git a/WorkBench/Node/View/FlipflopNodeControl.xaml b/WorkBench/Node/View/FlipflopNodeControl.xaml index adaa5bf..d6e89e0 100644 --- a/WorkBench/Node/View/FlipflopNodeControl.xaml +++ b/WorkBench/Node/View/FlipflopNodeControl.xaml @@ -29,34 +29,79 @@ + + - + + + + + + + + + + + + + + + + + + + + + - + + - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WorkBench/Node/View/FlipflopNodeControl.xaml.cs b/WorkBench/Node/View/FlipflopNodeControl.xaml.cs index f5db756..df0a2f8 100644 --- a/WorkBench/Node/View/FlipflopNodeControl.xaml.cs +++ b/WorkBench/Node/View/FlipflopNodeControl.xaml.cs @@ -1,17 +1,76 @@ using Serein.NodeFlow.Model; using Serein.Workbench.Node.ViewModel; +using System.Windows.Controls; +using System.Windows; namespace Serein.Workbench.Node.View { /// /// StateNode.xaml 的交互逻辑 /// - public partial class FlipflopNodeControl : NodeControlBase + public partial class FlipflopNodeControl : NodeControlBase, INodeJunction { public FlipflopNodeControl(FlipflopNodeControlViewModel viewModel) : base(viewModel) { DataContext = viewModel; InitializeComponent(); } + + + /// + /// 入参控制点(可能有,可能没) + /// + JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl; + + /// + /// 下一个调用方法控制点(可能有,可能没) + /// + JunctionControlBase INodeJunction.NextStepJunction => this.NextStepJunctionControl; + + /// + /// 返回值控制点(可能有,可能没) + /// + JunctionControlBase INodeJunction.ReturnDataJunction => this.ResultJunctionControl; + + /// + /// 方法入参控制点(可能有,可能没) + /// + private JunctionControlBase[] argDataJunction; + /// + /// 方法入参控制点(可能有,可能没) + /// + JunctionControlBase[] INodeJunction.ArgDataJunction + { + get + { + if (argDataJunction == null) + { + // 获取 MethodDetailsControl 实例 + var methodDetailsControl = this.MethodDetailsControl; + argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length]; + + var itemsControl = FindVisualChild(methodDetailsControl); // 查找 ItemsControl + if (itemsControl != null) + { + var controls = new List(); + + for (int i = 0; i < itemsControl.Items.Count; i++) + { + var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement; + if (container != null) + { + var argControl = FindVisualChild(container); + if (argControl != null) + { + controls.Add(argControl); // 收集 ArgJunctionControl 实例 + } + } + } + argDataJunction = controls.ToArray(); + } + } + return argDataJunction; + } + } } } diff --git a/Workbench/Node/NodeControlBase.cs b/Workbench/Node/NodeControlBase.cs index e394184..c010507 100644 --- a/Workbench/Node/NodeControlBase.cs +++ b/Workbench/Node/NodeControlBase.cs @@ -99,7 +99,30 @@ namespace Serein.Workbench.Node.View BindingOperations.SetBinding(this, Canvas.TopProperty, topBinding); } - + /// + /// 穿透视觉树获取指定类型的第一个元素 + /// + /// + /// + /// + protected T FindVisualChild(DependencyObject parent) where T : DependencyObject + { + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) + { + var child = VisualTreeHelper.GetChild(parent, i); + if (child is T typedChild) + { + return typedChild; + } + + var childOfChild = FindVisualChild(child); + if (childOfChild != null) + { + return childOfChild; + } + } + return null; + }