mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
更改了远程环境下websocket会来回发送重复消息的问题
This commit is contained in:
@@ -13,7 +13,7 @@ namespace Serein.FlowStartTool
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#if true
|
||||
#if debug
|
||||
args = [@"F:\临时\project\linux\project.dnf"];
|
||||
#endif
|
||||
|
||||
|
||||
@@ -263,9 +263,9 @@ namespace Serein.Library
|
||||
/// <summary>
|
||||
/// 执行单个节点对应的方法,并不做状态检查
|
||||
/// </summary>
|
||||
/// <param name="env"></param>
|
||||
/// <param name="context">运行时上下文</param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<object> InvokeAsync(IFlowEnvironment env)
|
||||
public virtual async Task<object> 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;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,17 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class AutoSocketModuleAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 业务标识
|
||||
/// </summary>
|
||||
public string ThemeKey;
|
||||
/// <summary>
|
||||
/// 数据标识
|
||||
/// </summary>
|
||||
public string DataKey;
|
||||
/// <summary>
|
||||
/// ID标识
|
||||
/// </summary>
|
||||
public string MsgIdKey;
|
||||
}
|
||||
|
||||
@@ -55,10 +64,10 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
public string ThemeValue = string.Empty;
|
||||
/// <summary>
|
||||
/// <para>标记方法执行完成后是否需要将结果发送。</para>
|
||||
/// <para>但以下情况将不会发送:</para>
|
||||
/// <para>1.返回类型为void</para>
|
||||
/// <para>2.返回类型为Task</para>
|
||||
/// <para>3.返回了null</para>
|
||||
/// <para>注意以下返回值,返回的 json 中将不会新建 DataKey 字段:</para>
|
||||
/// <para>1.返回类型为 void </para>
|
||||
/// <para>2.返回类型为 Task </para>
|
||||
/// <para>2.返回类型为 Unit </para>
|
||||
/// <para>补充:如果返回类型是Task<TResult></para>
|
||||
/// <para>会进行异步等待,当Task结束后,自动获取TResult进行发送(请避免Task<Task<TResult>>诸如此类的Task泛型嵌套)</para>
|
||||
/// </summary>
|
||||
|
||||
@@ -89,9 +89,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
/// <summary>
|
||||
/// 处理JSON数据
|
||||
/// </summary>
|
||||
public async void HandleSocketMsg(WebSocketMsgContext context) // Func<string, Task> 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<JObject>();
|
||||
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
|
||||
/// <param name="context">处理上下文</param>
|
||||
/// <param name="args">返回的入参参数</param>
|
||||
/// <returns></returns>
|
||||
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; ; // 表示是否可以调用方法
|
||||
|
||||
@@ -11,23 +11,28 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
/// <summary>
|
||||
/// 消息处理上下文
|
||||
/// </summary>
|
||||
public class WebSocketMsgContext
|
||||
public class WebSocketMsgContext : IDisposable
|
||||
{
|
||||
public WebSocketMsgContext(Func<string, Task> sendAsync)
|
||||
{
|
||||
this._sendAsync = sendAsync;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
JsonObject = null;
|
||||
MsgTheme = null;
|
||||
MsgId = null;
|
||||
MsgData = null;
|
||||
MsgData = null;
|
||||
_sendAsync = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// 标记是否已经处理,如果是,则提前退出
|
||||
/// </summary>
|
||||
public bool Handle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息本体(文本)
|
||||
/// </summary>
|
||||
public string Msg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息本体(JObject)
|
||||
/// </summary>
|
||||
@@ -107,6 +112,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||
await SendAsync(msg);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
/// <param name="context">此次请求的上下文</param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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); // 处理消息
|
||||
//});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
|
||||
//});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// 单独运行一个节点
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<object> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -364,9 +364,9 @@ namespace Serein.NodeFlow.Env
|
||||
await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid);
|
||||
}
|
||||
|
||||
public async Task<object> InvokeNodeAsync(string nodeGuid)
|
||||
public async Task<object> InvokeNodeAsync( string nodeGuid)
|
||||
{
|
||||
return await currentFlowEnvironment.InvokeNodeAsync(nodeGuid);
|
||||
return await currentFlowEnvironment.InvokeNodeAsync( nodeGuid);
|
||||
}
|
||||
|
||||
public async Task StartRemoteServerAsync(int port = 7525)
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Serein.NodeFlow.Env
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="flowEnvInfo"></param>
|
||||
[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
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="sereinProjectData"></param>
|
||||
[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);
|
||||
|
||||
@@ -358,33 +358,42 @@ namespace Serein.NodeFlow
|
||||
/// <returns></returns>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,35 +9,7 @@ namespace Serein.NodeFlow.Tool;
|
||||
|
||||
public static class NodeMethodDetailsHelper
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 生成方法信息
|
||||
/// </summary>
|
||||
/// <param name="serviceContainer"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
//public static List<MethodDetails> GetList(Type type)
|
||||
//{
|
||||
// var methodDetailsDictionary = new List<MethodDetails>();
|
||||
// var delegateDictionary = new List<Delegate>();
|
||||
// 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;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 获取处理方法
|
||||
/// </summary>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -213,17 +213,17 @@ Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Con
|
||||
Visibility="{Binding IsConnectionInvokeNode,
|
||||
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" >
|
||||
<TextBlock Margin="8,2,8,0" Foreground="#FF2727" FontSize="14" Text="正在设置调用关系"/>
|
||||
<TextBlock Margin="8,0,8,0" Foreground="#4A82E4" FontSize="14" Text=" 按 1 切换:上游分支"/>
|
||||
<TextBlock Margin="8,0,8,0" Foreground="#04FC10" FontSize="14" Text=" 按 2 切换:Succeed 分支"/>
|
||||
<TextBlock Margin="8,0,8,0" Foreground="#F18905" FontSize="14" Text=" 按 3 切换:Fail 分支"/>
|
||||
<TextBlock Margin="8,0,8,2" Foreground="#FE1343" FontSize="14" Text=" 按 4 切换:异常分支"/>
|
||||
<TextBlock Margin="8,0,8,0" Foreground="#4A82E4" FontSize="14" Text=" 按 1 切换:上游分支(运行本节点前,优先执行目标节点)"/>
|
||||
<TextBlock Margin="8,0,8,0" Foreground="#04FC10" FontSize="14" Text=" 按 2 切换:Succeed 分支(本节点运行完成,将会运行目标节点)"/>
|
||||
<TextBlock Margin="8,0,8,0" Foreground="#F18905" FontSize="14" Text=" 按 3 切换:Fail 分支(条件节点的false分支)"/>
|
||||
<TextBlock Margin="8,0,8,2" Foreground="#FE1343" FontSize="14" Text=" 按 4 切换:异常分支(本节点运行发生异常时执行目标节点)"/>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="14" Width="auto" HorizontalAlignment="Left" Background="White" Opacity="0.9"
|
||||
Visibility="{Binding IsConnectionArgSourceNode,
|
||||
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" >
|
||||
<TextBlock Margin="8,2,8,0" Foreground="#FF2727" FontSize="14" Text="正在设置参数传递关系"/>
|
||||
<TextBlock Margin="8,0,8,0" Foreground="#56CEF6" FontSize="14" Text=" 按 1 切换:调用时获取指定节点的返回值"/>
|
||||
<TextBlock Margin="8,0,8,2" Foreground="#B06BBB" FontSize="14" Text=" 按 2 切换:调用时立刻调用指定节点,使用其返回值作为入参参数"/>
|
||||
<TextBlock Margin="8,0,8,0" Foreground="#56CEF6" FontSize="14" Text=" 按 1 切换:入参使用目标节点返回值"/>
|
||||
<TextBlock Margin="8,0,8,2" Foreground="#B06BBB" FontSize="14" Text=" 按 2 切换:立刻调用目标节点,其返回值将作为入参参数"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
@@ -18,14 +18,11 @@
|
||||
</UserControl.Resources>
|
||||
|
||||
<Border BorderBrush="#8DE9FD" BorderThickness="4">
|
||||
|
||||
|
||||
<Grid>
|
||||
<Grid.ToolTip>
|
||||
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding NodeModel.MethodDetails.MethodTips}" />
|
||||
</Grid.ToolTip>
|
||||
|
||||
|
||||
|
||||
<!--<TextBlock Text="{Binding NodelModel.DebugSetting.IsInterrupt}}"></TextBlock>-->
|
||||
<Border x:Name="InterruptBorder" DataContext="{Binding}">
|
||||
<Border.Style>
|
||||
|
||||
@@ -23,10 +23,10 @@ namespace Serein.Workbench.Node.View
|
||||
/// 入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl;
|
||||
|
||||
/// <summary>
|
||||
/// 下一个调用方法控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
|
||||
JunctionControlBase INodeJunction.NextStepJunction => this.NextStepJunctionControl;
|
||||
|
||||
/// <summary>
|
||||
@@ -34,6 +34,10 @@ namespace Serein.Workbench.Node.View
|
||||
/// </summary>
|
||||
JunctionControlBase INodeJunction.ReturnDataJunction => this.ResultJunctionControl;
|
||||
|
||||
/// <summary>
|
||||
/// 方法入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
private JunctionControlBase[] argDataJunction;
|
||||
/// <summary>
|
||||
/// 方法入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
@@ -67,28 +71,8 @@ namespace Serein.Workbench.Node.View
|
||||
return argDataJunction;
|
||||
} }
|
||||
|
||||
/// <summary>
|
||||
/// 方法入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
private JunctionControlBase[] argDataJunction;
|
||||
|
||||
|
||||
private T FindVisualChild<T>(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<T>(child);
|
||||
if (childOfChild != null)
|
||||
{
|
||||
return childOfChild;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,34 +29,79 @@
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Background="#FCB334">
|
||||
<Grid Grid.Row="0" Background="#FCB334" >
|
||||
<!--<Grid Grid.Row="0" >-->
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="3*"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<local:ExecuteJunctionControl Grid.Column="0" MyNode="{Binding NodeModel}" x:Name="ExecuteJunctionControl" HorizontalAlignment="Left" Grid.RowSpan="2"/>
|
||||
<StackPanel Grid.Column="1" Grid.RowSpan="2" >
|
||||
<TextBlock Text="{Binding NodeModel.MethodDetails.MethodTips, Mode=TwoWay}" HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<local:NextStepJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="NextStepJunctionControl" HorizontalAlignment="Right" Grid.RowSpan="2"/>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!--<StackPanel Grid.Row="0" Orientation="Horizontal" Background="#FCB334">
|
||||
<CheckBox IsChecked="{Binding NodeModel.DebugSetting.IsEnable, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
||||
<CheckBox IsChecked="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
||||
|
||||
<TextBlock Text="{Binding NodeModel.MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding NodeModel.MethodDetails}" />
|
||||
</StackPanel>-->
|
||||
|
||||
<Border Grid.Row="1" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
|
||||
<themes:MethodDetailsControl x:Name="MethodDetailsControl" Grid.Row="1" MethodDetails="{Binding NodeModel.MethodDetails}" />
|
||||
|
||||
<Border Grid.Row="2" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
|
||||
Visibility="{Binding NodeModel.MethodDetails.IsProtectionParameter, Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" />
|
||||
<!--<Border Grid.Row="0" Background="#FCB334" >
|
||||
|
||||
</Border>-->
|
||||
<!--<themes:ExplicitDataControl Grid.Row="1" ExplicitDatas="{Binding ExplicitDatas}" />-->
|
||||
<Grid Grid.Row="2" Background="#D5F0FC" >
|
||||
<Grid Grid.Row="3" Background="#D5F0FC" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="0" BorderThickness="1">
|
||||
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<Border Grid.Column="0" BorderThickness="1">
|
||||
<TextBlock Text="result ->" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Border Grid.Column="1" BorderThickness="1">
|
||||
<TextBlock Text="{Binding NodeModel.MethodDetails.ReturnType}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
<Border Grid.Column="1" BorderThickness="1">
|
||||
<TextBlock Text="{Binding NodeModel.MethodDetails.ReturnType.FullName, Mode=OneTime}" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
|
||||
<Border Grid.Column="2" BorderThickness="1">
|
||||
<local:ResultJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="ResultJunctionControl" HorizontalAlignment="Right"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="4" Background="Azure" >
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox Grid.Row="0" Grid.Column="0" IsChecked="{Binding NodeModel.DebugSetting.IsEnable, Mode=TwoWay}"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="是否使能" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
<CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay}"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="参数保护" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
<CheckBox Grid.Row="2" Grid.Column="0" IsChecked="{Binding NodeModel.DebugSetting.IsInterrupt, Mode=TwoWay}"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Text="中断节点" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
<!--<themes:ConditionControl Grid.Row="2" ></themes:ConditionControl>-->
|
||||
</Grid>
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// StateNode.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class FlipflopNodeControl : NodeControlBase
|
||||
public partial class FlipflopNodeControl : NodeControlBase, INodeJunction
|
||||
{
|
||||
public FlipflopNodeControl(FlipflopNodeControlViewModel viewModel) : base(viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl;
|
||||
|
||||
/// <summary>
|
||||
/// 下一个调用方法控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
JunctionControlBase INodeJunction.NextStepJunction => this.NextStepJunctionControl;
|
||||
|
||||
/// <summary>
|
||||
/// 返回值控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
JunctionControlBase INodeJunction.ReturnDataJunction => this.ResultJunctionControl;
|
||||
|
||||
/// <summary>
|
||||
/// 方法入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
private JunctionControlBase[] argDataJunction;
|
||||
/// <summary>
|
||||
/// 方法入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
JunctionControlBase[] INodeJunction.ArgDataJunction
|
||||
{
|
||||
get
|
||||
{
|
||||
if (argDataJunction == null)
|
||||
{
|
||||
// 获取 MethodDetailsControl 实例
|
||||
var methodDetailsControl = this.MethodDetailsControl;
|
||||
argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
|
||||
|
||||
var itemsControl = FindVisualChild<ItemsControl>(methodDetailsControl); // 查找 ItemsControl
|
||||
if (itemsControl != null)
|
||||
{
|
||||
var controls = new List<JunctionControlBase>();
|
||||
|
||||
for (int i = 0; i < itemsControl.Items.Count; i++)
|
||||
{
|
||||
var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
|
||||
if (container != null)
|
||||
{
|
||||
var argControl = FindVisualChild<ArgJunctionControl>(container);
|
||||
if (argControl != null)
|
||||
{
|
||||
controls.Add(argControl); // 收集 ArgJunctionControl 实例
|
||||
}
|
||||
}
|
||||
}
|
||||
argDataJunction = controls.ToArray();
|
||||
}
|
||||
}
|
||||
return argDataJunction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,30 @@ namespace Serein.Workbench.Node.View
|
||||
BindingOperations.SetBinding(this, Canvas.TopProperty, topBinding);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 穿透视觉树获取指定类型的第一个元素
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parent"></param>
|
||||
/// <returns></returns>
|
||||
protected T FindVisualChild<T>(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<T>(child);
|
||||
if (childOfChild != null)
|
||||
{
|
||||
return childOfChild;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user