diff --git a/NodeFlow/Base/NodeModelBaseFunc.cs b/NodeFlow/Base/NodeModelBaseFunc.cs index a49481c..92a078a 100644 --- a/NodeFlow/Base/NodeModelBaseFunc.cs +++ b/NodeFlow/Base/NodeModelBaseFunc.cs @@ -47,7 +47,7 @@ namespace Serein.NodeFlow.Base #region 导出/导入项目文件节点信息 internal abstract Parameterdata[] GetParameterdatas(); - internal virtual NodeInfo ToInfo() + public virtual NodeInfo ToInfo() { // if (MethodDetails == null) return null; @@ -74,7 +74,7 @@ namespace Serein.NodeFlow.Base }; } - internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo) + public virtual NodeModelBase LoadInfo(NodeInfo nodeInfo) { this.Guid = nodeInfo.Guid; if (this.MethodDetails is not null) @@ -99,14 +99,15 @@ namespace Serein.NodeFlow.Base /// /// /// - public async Task StartExecute(IDynamicContext context) + public async Task StartFlowAsync(IDynamicContext context) { Stack stack = new Stack(); stack.Push(this); var flowCts = context.Env.IOC.Get(FlowStarter.FlipFlopCtsName); + bool hasFlipflow = flowCts != null; while (stack.Count > 0) // 循环中直到栈为空才会退出循环 { - if (flowCts is not null) + if (hasFlipflow && flowCts is not null) { if (flowCts.IsCancellationRequested) break; @@ -131,7 +132,7 @@ namespace Serein.NodeFlow.Base await Console.Out.WriteLineAsync($"[{upstreamNode.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支"); } upstreamNode.PreviousNode = currentNode; - await upstreamNode.StartExecute(context); // 执行流程节点的上游分支 + await upstreamNode.StartFlowAsync(context); // 执行流程节点的上游分支 if (upstreamNode.NextOrientation == ConnectionType.IsError) { // 如果上游分支执行失败,不再继续执行 @@ -144,7 +145,7 @@ namespace Serein.NodeFlow.Base // 上游分支执行完成,才执行当前节点 object? newFlowData = await currentNode.ExecutingAsync(context); - if (flowCts is null || flowCts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None) + if (hasFlipflow && (flowCts is null || flowCts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None)) { // 不再执行 break; diff --git a/NodeFlow/FlowEnvironment.cs b/NodeFlow/FlowEnvironment.cs index c994551..097dc75 100644 --- a/NodeFlow/FlowEnvironment.cs +++ b/NodeFlow/FlowEnvironment.cs @@ -157,7 +157,7 @@ namespace Serein.NodeFlow /// /// 容器管理 /// - private readonly SereinIOC sereinIOC; + public readonly SereinIOC sereinIOC; /// /// 存储加载的程序集路径 @@ -166,43 +166,43 @@ namespace Serein.NodeFlow /// /// 存储加载的程序集 /// - private List NodeLibrarys { get; } = []; + public List NodeLibrarys { get; } = []; /// /// 存储所有方法信息 /// //private MethodDetailss { get; } = []; - private Dictionary> MethodDetailss { get; } = []; + public Dictionary> MethodDetailss { get; } = []; /// /// 环境加载的节点集合 /// Node Guid - Node Model /// - private Dictionary Nodes { get; } = []; + public Dictionary Nodes { get; } = []; /// /// 存放触发器节点(运行时全部调用) /// - private List FlipflopNodes { get; } = []; - private Dictionary> AutoRegisterTypes { get; } = []; + public List FlipflopNodes { get; } = []; + public Dictionary> AutoRegisterTypes { get; } = []; /// /// 存放委托 /// /// md.Methodname - delegate /// - - private ConcurrentDictionary MethodDelegates { get; } = []; + + public ConcurrentDictionary MethodDelegates { get; } = []; /// /// 起始节点私有属性 /// - private NodeModelBase? _startNode = null; + public NodeModelBase? _startNode = null; /// /// 起始节点 /// - private NodeModelBase? StartNode + public NodeModelBase? StartNode { get { @@ -226,7 +226,7 @@ namespace Serein.NodeFlow /// /// 流程启动器(每次运行时都会重新new一个) /// - private FlowStarter? flowStarter; + public FlowStarter? flowStarter; #endregion @@ -865,7 +865,7 @@ namespace Serein.NodeFlow /// 节点Guid /// 节点Model /// 无法获取节点、Guid/节点为null时报错 - private NodeModelBase? GuidToModel(string nodeGuid) + public NodeModelBase? GuidToModel(string nodeGuid) { if (string.IsNullOrEmpty(nodeGuid)) { diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index 9a72f1b..b9be258 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -95,7 +95,7 @@ namespace Serein.NodeFlow public async Task StartFlowInSelectNodeAsync(NodeModelBase startNode) { if (Context is null) return; - await startNode.StartExecute(Context); // 开始运行时从选定节点开始运行 + await startNode.StartFlowAsync(Context); // 开始运行时从选定节点开始运行 } @@ -310,7 +310,7 @@ namespace Serein.NodeFlow }).ToArray(); _ = Task.WhenAll(tasks); } - await startNode.StartExecute(Context); // 开始运行时从起始节点开始运行 + await startNode.StartFlowAsync(Context); // 开始运行时从起始节点开始运行 // 等待结束 if(FlipFlopState == RunState.Running && _flipFlopCts is not null) { @@ -407,7 +407,7 @@ namespace Serein.NodeFlow var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask(); await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); } - await nextNodes[i].StartExecute(context); // 启动执行触发器后继分支的节点 + await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点 } } } diff --git a/NodeFlow/Model/CompositeActionNode.cs b/NodeFlow/Model/CompositeActionNode.cs index 022ee78..d8c78e8 100644 --- a/NodeFlow/Model/CompositeActionNode.cs +++ b/NodeFlow/Model/CompositeActionNode.cs @@ -30,7 +30,7 @@ namespace Serein.NodeFlow.Model { return []; } - internal override NodeInfo? ToInfo() + public override NodeInfo? ToInfo() { if (MethodDetails is null) return null; diff --git a/NodeFlow/Model/CompositeConditionNode.cs b/NodeFlow/Model/CompositeConditionNode.cs index 3eea781..e14627e 100644 --- a/NodeFlow/Model/CompositeConditionNode.cs +++ b/NodeFlow/Model/CompositeConditionNode.cs @@ -62,7 +62,7 @@ namespace Serein.NodeFlow.Model return []; } - internal override NodeInfo ToInfo() + public override NodeInfo ToInfo() { //if (MethodDetails == null) return null; diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs index 4e61fa1..1c7b705 100644 --- a/NodeFlow/Model/SingleConditionNode.cs +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -94,7 +94,7 @@ namespace Serein.NodeFlow.Model - internal override NodeModelBase LoadInfo(NodeInfo nodeInfo) + public override NodeModelBase LoadInfo(NodeInfo nodeInfo) { var node = this; if (node != null) diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/SingleExpOpNode.cs index 048360b..9c3083e 100644 --- a/NodeFlow/Model/SingleExpOpNode.cs +++ b/NodeFlow/Model/SingleExpOpNode.cs @@ -56,7 +56,7 @@ namespace Serein.NodeFlow.Model - internal override NodeModelBase LoadInfo(NodeInfo nodeInfo) + public override NodeModelBase LoadInfo(NodeInfo nodeInfo) { var node = this; if (node != null) diff --git a/Serein.FlowRemoteManagement/FlowRemoteManagement.cs b/Serein.FlowRemoteManagement/FlowRemoteManagement.cs index bb739ae..825056f 100644 --- a/Serein.FlowRemoteManagement/FlowRemoteManagement.cs +++ b/Serein.FlowRemoteManagement/FlowRemoteManagement.cs @@ -6,17 +6,45 @@ 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; namespace SereinFlowRemoteManagement { + public enum FlowEnvCommand + { + A, + B, + C, + D + } + + + /// + /// SereinFlow 远程管理模块 + /// [DynamicFlow] [AutoRegister] - public class FlowRemoteManagement + [AutoSocketModule(ThemeKey ="theme",DataKey ="data")] + public class FlowRemoteManagement : FlowTrigger, ISocketHandleModule { - private readonly IFlowEnvironment environment; + #region 初始化 + public Guid HandleGuid { get; } = new Guid(); + + private readonly FlowEnvironment environment; public FlowRemoteManagement(IFlowEnvironment environment) { - this.environment = environment; + if(environment is FlowEnvironment env) + { + this.environment = env; + } + else + { + throw new Exception(); + } } [NodeAction(NodeType.Init)] @@ -30,16 +58,129 @@ namespace SereinFlowRemoteManagement { environment.IOC.Run(async (socketServer) => { + socketServer.MsgHandleHelper.AddModule(this, + (ex, send) => + { + send(new + { + code = 400, + ex = ex.Message + }); + }); await socketServer.StartAsync("http://*:7525/"); }); SereinProjectData projectData = environment.SaveProject(); - } + } + #endregion + #region 对外接口 - public void GetAllNodeInfo() + /// + /// 更改两个节点的连接关系 + /// + /// + /// + /// + [AutoSocketHandle(ThemeValue = "ConnectionChange")] + public void ChangeNodeConnection(ConnectionInfoData nodeInfo, Func Send) { + if (string.IsNullOrEmpty(nodeInfo.FromNodeGuid) || string.IsNullOrEmpty(nodeInfo.ToNodeGuid)) + { + throw new InvalidOperationException("Guid错误"); + } + if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var connectionType)) + { + throw new InvalidOperationException("类型错误"); + } + if (nodeInfo.Op) + { + environment.ConnectNode(nodeInfo.FromNodeGuid, nodeInfo.ToNodeGuid, connectionType); + } + else + { + environment.RemoteConnect(nodeInfo.FromNodeGuid, nodeInfo.ToNodeGuid, connectionType); + } } + /// + /// 远程调用某个节点 + /// + [AutoSocketHandle(ThemeValue = "InvokeNode")] + public async Task InvokeNode(bool isBranchEx, string nodeGuid, Func Send) + { + if (string.IsNullOrEmpty(nodeGuid)) + { + throw new InvalidOperationException("Guid错误"); + } + if(!environment.Nodes.TryGetValue(nodeGuid, out var nodeModel) ) + { + throw new InvalidOperationException("不存在这样的节点"); + } + IDynamicContext dynamicContext = new DynamicContext(environment); + object? result = null; + if(isBranchEx) + { + await nodeModel.StartFlowAsync(dynamicContext); + } + else + { + result = await nodeModel.ExecutingAsync(dynamicContext); + } + + if(result is not Task) + { + await Send(new + { + state = 200, + tips = "执行完成", + data = result + }) ; + } + } + + /// + /// 获取项目配置文件信息 + /// + [AutoSocketHandle(ThemeValue = "GetProjectInfo")] + public async Task GetProjectInfo() + { + await Task.Delay(0); + return environment.SaveProject(); + } + + + #endregion + + #region 测试节点 + + [NodeAction(NodeType.Flipflop, "触发器等待")] + public async Task> WaitFlipflop(FlowEnvCommand flowEnvCommand) + { + var result = await this.CreateTaskAsync(flowEnvCommand); + return new FlipflopContext(FlipflopStateType.Succeed, result); + } + + [NodeAction(NodeType.Action, "测试")] + public void Test() + { + Console.WriteLine("Hello World"); + } + + [NodeAction(NodeType.Action, "等待")] + public async Task Wait(int wait = 5) + { + await Task.Delay(1000 * wait); + } + + [NodeAction(NodeType.Action, "输出")] + public void Console2(string value) + { + Console.WriteLine(value); + } + #endregion + + + } } diff --git a/Serein.FlowRemoteManagement/Model/ConnectionInfoData.cs b/Serein.FlowRemoteManagement/Model/ConnectionInfoData.cs new file mode 100644 index 0000000..c8f4fb7 --- /dev/null +++ b/Serein.FlowRemoteManagement/Model/ConnectionInfoData.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.FlowRemoteManagement.Model +{ + public class ConnectionInfoData + { + public bool Op { get; set; } + public string? FromNodeGuid { get; set; } + public string? ToNodeGuid { get; set; } + // None Upstream IsSucceed IsFail IsError + public string? Type { get; set; } + } +} diff --git a/Serein.FlowRemoteManagement/Serein.FlowRemoteManagement.csproj b/Serein.FlowRemoteManagement/Serein.FlowRemoteManagement.csproj index ea3a544..0c7929c 100644 --- a/Serein.FlowRemoteManagement/Serein.FlowRemoteManagement.csproj +++ b/Serein.FlowRemoteManagement/Serein.FlowRemoteManagement.csproj @@ -7,13 +7,10 @@ D:\Project\C#\DynamicControl\SereinFlow\.Output - - - - + diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index bc452f9..92191c8 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -161,7 +161,8 @@ namespace Serein.WorkBench string filePath; //filePath = @"F:\临时\project\tmp\project.dnf"; //filePath = @"D:\Project\C#\TestNetFramework\Net45DllTest\Net45DllTest\bin\Debug\project.dnf"; - filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net462DllTest\bin\Debug\project.dnf"; + //filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net462DllTest\bin\Debug\project.dnf"; + filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\project.dnf"; //string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf"; string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 App.FlowProjectData = JsonConvert.DeserializeObject(content);