From 1d97ea5da1d8975b5cf706805b6dad6afaace97f Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Tue, 15 Oct 2024 21:56:09 +0800 Subject: [PATCH] =?UTF-8?q?web=20socket=E6=B7=BB=E5=8A=A0token=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E8=83=BD=E5=8A=9B=EF=BC=88=E4=BD=BF=E7=94=A8token+?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=97=B6=E8=AE=BE=E7=BD=AE=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=EF=BC=89=EF=BC=9BflowEnv=E6=B7=BB=E5=8A=A0We?= =?UTF-8?q?bSocket=E5=A4=84=E7=90=86=E6=B6=88=E6=81=AF=E8=83=BD=E5=8A=9B?= =?UTF-8?q?=EF=BC=8C=E4=B8=8B=E4=B8=80=E6=AD=A5=E5=B0=86=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E7=99=BB=E5=BD=95=E7=99=BB=E5=BD=95=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Serein.Extend.RemoteControl.csproj | 1 + .../SereinFlowRemoteControl.cs | 12 +- FlowStartTool/Serein.FlowStartTool.csproj | 1 + Library.Core/Serein.Library.Core.csproj | 1 + Library.Framework/Properties/AssemblyInfo.cs | 4 +- Library/Api/IFlowEnvironment.cs | 72 ++- .../Handle/WebSocketMsgHandleHelper.cs | 11 +- Library/Network/WebSocket/WebSocketClient.cs | 42 +- Library/Network/WebSocket/WebSocketServer.cs | 183 +++++++- Library/NodeAttribute.cs | 4 + Library/Serein.Library.csproj | 1 + NodeFlow/FlowEnvironment.cs | 433 ++++++++++++------ NodeFlow/FlowStarter.cs | 2 +- NodeFlow/Serein.NodeFlow.csproj | 1 + SereinFlow.sln | 14 +- WorkBench/App.xaml | 4 +- WorkBench/App.xaml.cs | 2 +- WorkBench/LogWindow.xaml | 4 +- WorkBench/LogWindow.xaml.cs | 4 +- WorkBench/MainWindow.xaml | 22 +- WorkBench/MainWindow.xaml.cs | 177 +++---- WorkBench/MainWindowViewModel.cs | 16 +- WorkBench/Node/NodeControlViewModelBase.cs | 2 +- WorkBench/Node/View/ActionNodeControl.xaml | 10 +- WorkBench/Node/View/ActionNodeControl.xaml.cs | 4 +- WorkBench/Node/View/ActionRegionControl.xaml | 4 +- .../Node/View/ActionRegionControl.xaml.cs | 3 +- WorkBench/Node/View/ConditionNodeControl.xaml | 8 +- .../Node/View/ConditionNodeControl.xaml.cs | 4 +- .../Node/View/ConditionRegionControl.xaml | 4 +- .../Node/View/ConditionRegionControl.xaml.cs | 4 +- WorkBench/Node/View/DllControlControl.xaml | 6 +- WorkBench/Node/View/DllControlControl.xaml.cs | 2 +- WorkBench/Node/View/ExpOpNodeControl.xaml | 4 +- WorkBench/Node/View/ExpOpNodeControl.xaml.cs | 4 +- WorkBench/Node/View/FlipflopNodeControl.xaml | 10 +- .../Node/View/FlipflopNodeControl.xaml.cs | 4 +- WorkBench/Node/View/NodeControlBase.cs | 33 +- .../ViewModel/ActionNodeControlViewModel.cs | 4 +- .../ConditionNodeControlViewModel.cs | 11 +- .../ConditionRegionNodeControlViewModel.cs | 4 +- .../Node/ViewModel/ExpOpNodeViewModel.cs | 4 +- .../ViewModel/FlipflopNodeControlViewModel.cs | 4 +- .../Node/ViewModel/TypeToStringConverter.cs | 2 +- WorkBench/Themes/IOCObjectViewControl.xaml | 4 +- WorkBench/Themes/IOCObjectViewControl.xaml.cs | 2 +- WorkBench/Themes/InputDialog.xaml | 4 +- WorkBench/Themes/InputDialog.xaml.cs | 2 +- WorkBench/Themes/MethodDetailsControl.xaml | 2 +- WorkBench/Themes/MethodDetailsControl.xaml.cs | 2 +- WorkBench/Themes/NodeTreeItemViewControl.xaml | 4 +- .../Themes/NodeTreeItemViewControl.xaml.cs | 6 +- WorkBench/Themes/NodeTreeViewControl.xaml | 4 +- WorkBench/Themes/NodeTreeViewControl.xaml.cs | 2 +- WorkBench/Themes/ObjectViewerControl.xaml | 4 +- WorkBench/Themes/ObjectViewerControl.xaml.cs | 4 +- WorkBench/Themes/TypeViewerWindow.xaml | 4 +- WorkBench/Themes/TypeViewerWindow.xaml.cs | 2 +- WorkBench/Themes/WindowDialogInput.xaml | 4 +- WorkBench/Themes/WindowDialogInput.xaml.cs | 2 +- .../InvertableBooleanToVisibilityConverter.cs | 2 +- .../Tool/Converters/ThumbPositionConverter.cs | 2 +- .../Tool/Converters/TypeToColorConverter.cs | 2 +- WorkBench/tool/LogTextWriter.cs | 2 +- 64 files changed, 811 insertions(+), 395 deletions(-) diff --git a/Extend.FlowRemoteManagement/Serein.Extend.RemoteControl.csproj b/Extend.FlowRemoteManagement/Serein.Extend.RemoteControl.csproj index 28986e2..5abb2a8 100644 --- a/Extend.FlowRemoteManagement/Serein.Extend.RemoteControl.csproj +++ b/Extend.FlowRemoteManagement/Serein.Extend.RemoteControl.csproj @@ -1,6 +1,7 @@  + 1.0.0 net8.0 enable enable diff --git a/Extend.FlowRemoteManagement/SereinFlowRemoteControl.cs b/Extend.FlowRemoteManagement/SereinFlowRemoteControl.cs index 2a385b1..d715ae4 100644 --- a/Extend.FlowRemoteManagement/SereinFlowRemoteControl.cs +++ b/Extend.FlowRemoteManagement/SereinFlowRemoteControl.cs @@ -34,14 +34,6 @@ namespace SereinFlowRemoteManagement public SereinFlowRemoteControl(IFlowEnvironment environment) { this.environment = environment; - //if (environment is FlowEnvironment env) - //{ - // this.environment = env; - //} - //else - //{ - // throw new Exception(); - //} } [NodeAction(NodeType.Init)] @@ -153,7 +145,7 @@ namespace SereinFlowRemoteManagement throw new InvalidOperationException("Guid错误"); } - await environment.StartFlowInSelectNodeAsync(nodeGuid); + await environment.StartAsyncInSelectNode(nodeGuid); await Send(new { @@ -172,7 +164,7 @@ namespace SereinFlowRemoteManagement return environment.GetProjectInfo(); } - + #endregion } } diff --git a/FlowStartTool/Serein.FlowStartTool.csproj b/FlowStartTool/Serein.FlowStartTool.csproj index 124420a..5124857 100644 --- a/FlowStartTool/Serein.FlowStartTool.csproj +++ b/FlowStartTool/Serein.FlowStartTool.csproj @@ -1,6 +1,7 @@  + 1.0.0 Exe net8.0 true diff --git a/Library.Core/Serein.Library.Core.csproj b/Library.Core/Serein.Library.Core.csproj index 93046da..b484e3a 100644 --- a/Library.Core/Serein.Library.Core.csproj +++ b/Library.Core/Serein.Library.Core.csproj @@ -1,6 +1,7 @@  + 1.0.13 net8.0 enable enable diff --git a/Library.Framework/Properties/AssemblyInfo.cs b/Library.Framework/Properties/AssemblyInfo.cs index 9b6d4f3..2d53e45 100644 --- a/Library.Framework/Properties/AssemblyInfo.cs +++ b/Library.Framework/Properties/AssemblyInfo.cs @@ -33,6 +33,6 @@ using System.Runtime.InteropServices; //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.0.1.0")] +[assembly: AssemblyFileVersion("1.0.1.0")] [assembly: NeutralResourcesLanguage("")] diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index c5d5f9c..0342d0a 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -80,6 +80,12 @@ namespace Serein.Library.Api /// public delegate void NodeLocatedHandler(NodeLocatedEventArgs eventArgs); + /// + /// 节点移动了(远程插件) + /// + /// + public delegate void NodeMovedHandler(NodeMovedEventArgs eventArgs); + #endregion #region 环境事件签名 @@ -381,7 +387,9 @@ namespace Serein.Library.Api public object Instance { get; private set; } } - + /// + /// 节点需要定位 + /// public class NodeLocatedEventArgs : FlowEventArgs { public NodeLocatedEventArgs(string nodeGuid) @@ -391,6 +399,31 @@ namespace Serein.Library.Api public string NodeGuid { get; private set; } } + /// + /// 节点移动了 + /// + public class NodeMovedEventArgs : FlowEventArgs + { + public NodeMovedEventArgs(string nodeGuid, double x, double y) + { + this.NodeGuid = nodeGuid; + this.X = x; + this.Y = y; + } + /// + /// 节点唯一标识 + /// + public string NodeGuid { get; private set; } + /// + /// 画布上的x坐标 + /// + public double X { get; private set; } + /// + /// 画布上的y坐标 + /// + public double Y { get; private set; } + } + #endregion @@ -492,11 +525,15 @@ namespace Serein.Library.Api /// event IOCMembersChangedHandler OnIOCMembersChanged; - /// /// 节点需要定位 /// - event NodeLocatedHandler OnNodeLocate; + event NodeLocatedHandler OnNodeLocated; + + /// + /// 节点移动了(远程插件) + /// + event NodeMovedHandler OnNodeMoved; #endregion @@ -520,6 +557,15 @@ namespace Serein.Library.Api //bool TryGetNodeData(string methodName, out NodeData node); #region 环境基础接口 + /// + /// 启动远程服务 + /// + Task StartRemoteServerAsync(int port = 7525); + /// + /// 停止远程服务 + /// + void StopRemoteServer(); + /// /// 保存当前项目 @@ -532,7 +578,6 @@ namespace Serein.Library.Api /// /// void LoadProject(SereinProjectData projectFile, string filePath); - /// /// 加载远程项目 /// @@ -556,8 +601,7 @@ namespace Serein.Library.Api /// 清理加载的DLL(待更改) /// void ClearAll(); - - + /// /// 开始运行 /// @@ -567,18 +611,27 @@ namespace Serein.Library.Api /// /// /// - Task StartFlowInSelectNodeAsync(string startNodeGuid); + Task StartAsyncInSelectNode(string startNodeGuid); /// /// 结束运行 /// - void Exit(); + void ExitFlow(); + + /// + /// 移动了某个节点(远程插件使用) + /// + /// + /// + /// + void MoveNode(string nodeGuid,double x, double y); /// /// 设置流程起点节点 /// /// void SetStartNode(string nodeGuid); + /// /// 在两个节点之间创建连接关系 /// @@ -586,6 +639,7 @@ namespace Serein.Library.Api /// 目标节点Guid /// 连接类型 void ConnectNode(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType); + /// /// 创建节点/区域/基础控件 /// @@ -601,6 +655,7 @@ namespace Serein.Library.Api /// 目标节点 /// 连接类型 void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType); + /// /// 移除节点/区域/基础控件 /// @@ -612,6 +667,7 @@ namespace Serein.Library.Api /// /// void ActivateFlipflopNode(string nodeGuid); + /// /// 终结一个全局触发器,在它触发后将不会再次监听消息(表现为已经启动的触发器至少会再次处理一次消息,后面版本再修正这个非预期行为) /// diff --git a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs index c3293ed..f74cebd 100644 --- a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs +++ b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs @@ -129,7 +129,15 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle foreach ((var model, var method) in methods) { Console.WriteLine($"theme value : {model.ThemeValue}"); - handlemodule.AddHandleConfigs(model, socketControlBase, method, onExceptionTracking); + try + { + handlemodule.AddHandleConfigs(model, socketControlBase, method, onExceptionTracking); + } + catch (Exception ex) + { + + Console.WriteLine($"error in add method: {method.Name}{Environment.NewLine}{ex}"); + } } } @@ -147,7 +155,6 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle { foreach (var module in MyHandleModuleDict.Values) { - module.HandleSocketMsg(RecoverAsync, json); } diff --git a/Library/Network/WebSocket/WebSocketClient.cs b/Library/Network/WebSocket/WebSocketClient.cs index cde011a..57ffc58 100644 --- a/Library/Network/WebSocket/WebSocketClient.cs +++ b/Library/Network/WebSocket/WebSocketClient.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json.Linq; using Serein.Library.Attributes; +using Serein.Library.Network.WebSocketCommunication.Handle; using Serein.Library.Web; using System; using System.Collections.Concurrent; @@ -13,35 +14,58 @@ using System.Threading.Tasks; namespace Serein.Library.Network.WebSocketCommunication { - + /// + /// WebSocket客户端 + /// [AutoRegister] public class WebSocketClient { + /// + /// WebSocket客户端 + /// public WebSocketClient() { } - + + /// + /// 消息处理 + /// + public WebSocketMsgHandleHelper MsgHandleHelper { get; } = new WebSocketMsgHandleHelper(); private ClientWebSocket _client = new ClientWebSocket(); - + /// + /// 连接到指定WebSocket Server服务 + /// + /// + /// public async Task ConnectAsync(string uri) { await _client.ConnectAsync(new Uri(uri), CancellationToken.None); await ReceiveAsync(); } - - public async Task SendAsync(WebSocket webSocket,string message) + /// + /// 发送消息 + /// + /// + /// + /// + public async Task SendAsync(string message) { var buffer = Encoding.UTF8.GetBytes(message); - await webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None); + await _client.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, CancellationToken.None); } + /// + /// 开始处理消息 + /// + /// private async Task ReceiveAsync() { var buffer = new byte[1024]; + while (_client.State == WebSocketState.Open) { try @@ -54,8 +78,7 @@ namespace Serein.Library.Network.WebSocketCommunication else { var message = Encoding.UTF8.GetString(buffer, 0, result.Count); - - + _ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息 Debug.WriteLine($"Received: {message}"); } } @@ -67,9 +90,6 @@ namespace Serein.Library.Network.WebSocketCommunication } } - - - } diff --git a/Library/Network/WebSocket/WebSocketServer.cs b/Library/Network/WebSocket/WebSocketServer.cs index b550172..42b92a4 100644 --- a/Library/Network/WebSocket/WebSocketServer.cs +++ b/Library/Network/WebSocket/WebSocketServer.cs @@ -2,6 +2,7 @@ using Serein.Library.Attributes; using Serein.Library.Network.WebSocketCommunication.Handle; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; @@ -14,6 +15,84 @@ using System.Threading.Tasks; namespace Serein.Library.Network.WebSocketCommunication { + /// + /// WebSocket JSON 消息授权管理 + /// + public class WebSocketAuthorizedHelper + { + /// + /// WebSocket JSON 消息授权管理 + /// + public WebSocketAuthorizedHelper(string addresPort,string token, Func> inspectionAuthorizedFunc) + { + this.AddresPort = addresPort; + this.TokenKey = token; + this.InspectionAuthorizedFunc = inspectionAuthorizedFunc; + } + + /// + /// 客户端地址 + /// + public string AddresPort { get; } + + /// + /// 是否已经鉴权 + /// + public bool IsAuthorized { get => isAuthorized; } //set => isAuthorized = value; + + /// + /// 是否已经鉴权 + /// + private bool isAuthorized; + + /// + /// 授权字段 + /// + private readonly string TokenKey; + + /// + /// 处理消息授权事件 + /// + private readonly Func> InspectionAuthorizedFunc; + + private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1); + + /// + /// 处理消息授权 + /// + /// + public async Task HandleAuthorized(string message) + { + if(!isAuthorized && semaphoreSlim is null) // 需要重新授权 + { + semaphoreSlim = new SemaphoreSlim(1); + } + await semaphoreSlim.WaitAsync(1); + if(isAuthorized) // 授权通过,无须再次检查授权 + { + return; + } + JObject json = JObject.Parse(message); + if(json.TryGetValue(TokenKey,out var token)) + { + // 交给之前定义的授权方法进行判断 + isAuthorized = await InspectionAuthorizedFunc?.Invoke(token); + if (isAuthorized) + { + // 授权通过,释放资源 + semaphoreSlim.Release(); + semaphoreSlim.Dispose(); + semaphoreSlim = null; + } + } + else + { + isAuthorized = false; + } + } + + } + /// /// WebSocket服务类 @@ -28,6 +107,36 @@ namespace Serein.Library.Network.WebSocketCommunication private HttpListener listener; + /// + /// 创建无须授权验证的WebSocket服务端 + /// + public WebSocketServer() + { + this.AuthorizedClients = new ConcurrentDictionary(); + this.InspectionAuthorizedFunc = (tokenObj) => Task.FromResult(true); + this.IsNeedInspectionAuthorized = false; + } + + /// + /// 创建需要授权验证的WebSocket服务端 + /// + /// token 字段 + /// 验证token的方法 + public WebSocketServer(string tokenKey, Func> inspectionAuthorizedFunc) + { + this.TokenKey = tokenKey; + this.AuthorizedClients = new ConcurrentDictionary(); + this.InspectionAuthorizedFunc = inspectionAuthorizedFunc; + this.IsNeedInspectionAuthorized = true; + } + + /// + /// 授权 + /// + public ConcurrentDictionary AuthorizedClients; + private readonly string TokenKey; + private readonly Func> InspectionAuthorizedFunc; + private bool IsNeedInspectionAuthorized = false; /// /// 进行监听服务 /// @@ -37,6 +146,7 @@ namespace Serein.Library.Network.WebSocketCommunication { listener = new HttpListener(); listener.Prefixes.Add(url); + await Console.Out.WriteLineAsync($"WebSocket消息处理已启动[{url}]"); try { listener.Start(); @@ -46,7 +156,6 @@ namespace Serein.Library.Network.WebSocketCommunication await Console.Out.WriteLineAsync(ex.Message); return; } - while (true) { @@ -56,11 +165,20 @@ namespace Serein.Library.Network.WebSocketCommunication string clientPoint = context.Request.RemoteEndPoint?.ToString(); await Console.Out.WriteLineAsync($"新的连接加入:{clientPoint}"); + if (context.Request.IsWebSocketRequest) { - var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接 + WebSocketAuthorizedHelper authorizedHelper = null; + if (IsNeedInspectionAuthorized) + { + if (AuthorizedClients.TryAdd(clientPoint, new WebSocketAuthorizedHelper(clientPoint, TokenKey, InspectionAuthorizedFunc))) + { + AuthorizedClients.TryGetValue(clientPoint, out authorizedHelper); + } + } - _ = HandleWebSocketAsync(webSocketContext.WebSocket); // 处理消息 + var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接 + _ = HandleWebSocketAsync(webSocketContext.WebSocket, authorizedHelper); // 处理消息 } } catch (Exception ex) @@ -79,12 +197,21 @@ namespace Serein.Library.Network.WebSocketCommunication listener?.Stop(); } - private async Task HandleWebSocketAsync(WebSocket webSocket) + private async Task HandleWebSocketAsync(WebSocket webSocket, WebSocketAuthorizedHelper authorizedHelper) { - Func SendAsync = async (text) => + // 需要授权,却没有成功创建授权类,关闭连接 + if (IsNeedInspectionAuthorized && authorizedHelper is null) + { + await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); + return; + } + + + Func SendAsync = async (text) => { await WebSocketServer.SendAsync(webSocket, text); }; + var buffer = new byte[1024]; while (webSocket.State == WebSocketState.Open) { @@ -93,21 +220,45 @@ namespace Serein.Library.Network.WebSocketCommunication { SendAsync = null; await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); + if (IsNeedInspectionAuthorized) + { + AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _); + } } else { - var message = Encoding.UTF8.GetString(buffer, 0, result.Count); - - _ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); - - //foreach (var item in HandldHelpers) - //{ - // await item.HandleSocketMsg(webSocket, message); - //} - //Console.WriteLine($"Received: {message}"); - //var echoMessage = Encoding.UTF8.GetBytes(message); - //await webSocket.SendAsync(new ArraySegment(echoMessage, 0, echoMessage.Length), result.MessageType, result.EndOfMessage, CancellationToken.None); + var message = Encoding.UTF8.GetString(buffer, 0, result.Count); // 序列为文本 + if(!IsNeedInspectionAuthorized) + { + // 无须授权 + _ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息 + + } + else + { + // 需要授权 + if (!authorizedHelper.IsAuthorized) + { + // 该连接尚未验证授权,尝试检测授权 + _ = SendAsync("正在授权"); + await authorizedHelper.HandleAuthorized(message); + } + + if (authorizedHelper.IsAuthorized) + { + // 该连接通过了验证 + _ = SendAsync("授权成功"); + _ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息 + } + else + { + _ = SendAsync("授权失败"); + } + + } + + } } } diff --git a/Library/NodeAttribute.cs b/Library/NodeAttribute.cs index 7a720a4..0cab7c2 100644 --- a/Library/NodeAttribute.cs +++ b/Library/NodeAttribute.cs @@ -15,6 +15,10 @@ namespace Serein.Library.Attributes { } + + /// + /// 注册顺序 + /// public enum RegisterSequence { /// /// 不自动初始化 diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index cdfaa25..bb11020 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -1,6 +1,7 @@  + 1.0.13 net8.0;net462 D:\Project\C#\DynamicControl\SereinFlow\.Output True diff --git a/NodeFlow/FlowEnvironment.cs b/NodeFlow/FlowEnvironment.cs index d47d667..59717bc 100644 --- a/NodeFlow/FlowEnvironment.cs +++ b/NodeFlow/FlowEnvironment.cs @@ -4,6 +4,7 @@ using Serein.Library.Api; using Serein.Library.Attributes; using Serein.Library.Entity; using Serein.Library.Enums; +using Serein.Library.Network.WebSocketCommunication; using Serein.Library.Utils; using Serein.NodeFlow.Base; using Serein.NodeFlow.Model; @@ -40,53 +41,144 @@ namespace Serein.NodeFlow - /* - - public List get(){ - } + /* - libray - { - string dllname, - MethodInfo[] nodeinfos - } - - methodInfo{ - + public List get(){ } - - - - */ + + libray + { + string dllname, + MethodInfo[] nodeinfos + } + + methodInfo{ + + } + */ + + /* + void StopRemoteServer()//结束远程管理 + async Task StartAsync()//异步运行 + void ExitFlow()//退出 + Task StartAsyncInSelectNode(string startNodeGuid)//从选定节点开始运行 + void ActivateFlipflopNode(string nodeGuid)//激活全局触发器 + void TerminateFlipflopNode(string nodeGuid)//关闭全局触发器 + object GetEnvInfo() // 获取当前环境信息(远程连接) + void LoadProject(SereinProjectData project, string filePath) //加载项目文件 + void LoadRemoteProject(string addres, int port, string token) //加载远程项目 + SereinProjectData GetProjectInfo() // 序列化当前项目的依赖信息、节点信息 + void LoadDll(string dllPath) //从文件路径中加载DLL + bool RemoteDll(string assemblyFullName) //移除DLL + NodeModelBase CreateNode(NodeControlType nodeControlType, Position position, MethodDetailsInfo? methodDetailsInfo = null) //流程正在运行时创建节点 + void RemoveNode(string nodeGuid) //移除节点 + void ConnectNode(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) // 连接节点 + void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) //移除连接关系 + + void MoveNode(string nodeGuid,double x,double y) //移动了某个节点(远程插件使用) + void SetStartNode(string newNodeGuid) //设置起点控件 + bool SetNodeInterrupt(string nodeGuid, InterruptClass interruptClass) //中断指定节点,并指定中断等级。 + bool AddInterruptExpression(string key, string expression)//添加表达式中断 + void SetMonitorObjState(string key, bool isMonitor) // 设置对象的监视状态 + + */ + /// /// 运行环境 /// - public class FlowEnvironment : IFlowEnvironment, ISereinIOC + [AutoSocketModule(ThemeKey = "theme", DataKey = "data")] + public class FlowEnvironment : IFlowEnvironment, ISereinIOC, ISocketHandleModule { + /// + /// 流程运行环境 + /// public FlowEnvironment() { sereinIOC = new SereinIOC(); ChannelFlowInterrupt = new ChannelFlowInterrupt(); - //LoadedAssemblyPaths = new List(); - //LoadedAssemblies = new List(); - //MethodDetailss = new List(); - //Nodes = new Dictionary(); - //FlipflopNodes = new List(); IsGlobalInterrupt = false; flowStarter = null; - sereinIOC.OnIOCMembersChanged += e => this?.OnIOCMembersChanged?.Invoke(e) ; // 监听IOC容器的注册 } + /// + /// WebSocket处理 + /// + public Guid HandleGuid { get; } = new Guid(); + + /// + /// 流程环境远程管理服务 + /// + private WebSocketServer FlowEnvRemoteWebSocket; + + + private async Task InspectionAuthorized(dynamic token) + { + await Task.Delay(0); + var tokenValue = token.ToString(); + if ("123456".Equals(tokenValue)) + { + return true; + } + else + { + return false; + } + } + /// + /// 打开远程管理 + /// + /// + public async Task StartRemoteServerAsync(int port = 7525) + { + FlowEnvRemoteWebSocket ??= new WebSocketServer("token", InspectionAuthorized); + FlowEnvRemoteWebSocket.MsgHandleHelper.AddModule(this, + (ex, send) => + { + send(new + { + code = 400, + ex = ex.Message + }); + }); + var url = $"http://*:{port}/"; + try + { + await FlowEnvRemoteWebSocket.StartAsync(url); + } + catch (Exception ex) + { + FlowEnvRemoteWebSocket.MsgHandleHelper.RemoveModule(this); + Console.WriteLine("打开远程管理异常:" + ex); + } + } + + /// + /// 结束远程管理 + /// + [AutoSocketHandle] + public void StopRemoteServer() + { + try + { + FlowEnvRemoteWebSocket.Stop(); + } + catch (Exception ex) + { + Console.WriteLine("结束远程管理异常:" + ex); + } + } + + /// /// 节点的命名空间 /// public const string SpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}"; - #region 环境接口事件 + #region 环境运行事件 /// /// 加载Dll /// @@ -150,7 +242,13 @@ namespace Serein.NodeFlow /// /// 节点需要定位 /// - public event NodeLocatedHandler? OnNodeLocate; + public event NodeLocatedHandler? OnNodeLocated; + + /// + /// 节点移动了(远程插件) + /// + public event NodeMovedHandler? OnNodeMoved; + #endregion #region 属性 @@ -268,14 +366,13 @@ namespace Serein.NodeFlow #endregion - #region 基础接口 - - + #region 环境对外接口 /// /// 异步运行 /// /// + [AutoSocketHandle] public async Task StartAsync() { ChannelFlowInterrupt?.CancelAllTasks(); @@ -311,12 +408,18 @@ namespace Serein.NodeFlow if (this.FlipFlopState == RunState.Completion) { - this.Exit(); // 未运行触发器时,才会调用结束方法 + this.ExitFlow(); // 未运行触发器时,才会调用结束方法 } flowStarter = null; } - public async Task StartFlowInSelectNodeAsync(string startNodeGuid) + /// + /// 从选定节点开始运行 + /// + /// + /// + [AutoSocketHandle] + public async Task StartAsyncInSelectNode(string startNodeGuid) { if (flowStarter is null) { @@ -340,7 +443,8 @@ namespace Serein.NodeFlow /// /// 退出 /// - public void Exit() + [AutoSocketHandle] + public void ExitFlow() { ChannelFlowInterrupt?.CancelAllTasks(); flowStarter?.Exit(); @@ -359,6 +463,74 @@ namespace Serein.NodeFlow GC.Collect(); } + /// + /// 激活全局触发器 + /// + /// + [AutoSocketHandle] + public void ActivateFlipflopNode(string nodeGuid) + { + var nodeModel = GuidToModel(nodeGuid); + if (nodeModel is null) return; + if (flowStarter is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 + { + if (this.FlowState != RunState.Completion + && flipflopNode.NotExitPreviousNode()) // 正在运行,且该触发器没有上游节点 + { + _ = flowStarter.RunGlobalFlipflopAsync(this, flipflopNode);// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中 + + } + } + } + /// + /// 关闭全局触发器 + /// + /// + [AutoSocketHandle] + public void TerminateFlipflopNode(string nodeGuid) + { + var nodeModel = GuidToModel(nodeGuid); + if (nodeModel is null) return; + if (flowStarter is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 + { + flowStarter.TerminateGlobalFlipflopRuning(flipflopNode); + } + } + + /// + /// 获取当前环境信息(远程连接) + /// + /// + [AutoSocketHandle] + public object GetEnvInfo() + { + Dictionary> LibraryMds = []; + + foreach (var mdskv in this.MethodDetailsOfLibrarys) + { + var library = mdskv.Key; + var mds = mdskv.Value; + foreach (var md in mds) + { + if (!LibraryMds.TryGetValue(library, out var t_mds)) + { + t_mds = new List(); + LibraryMds[library] = t_mds; + } + var mdInfo = md.ToInfo(); + mdInfo.LibraryName = library.Assembly.GetName().FullName; + t_mds.Add(mdInfo); + } + } + var project = this.GetProjectInfo(); + return new + { + project = project, + envNode = LibraryMds.Values, + }; + } + + /// /// 清除所有 /// @@ -376,6 +548,7 @@ namespace Serein.NodeFlow /// /// /// + [AutoSocketHandle] public void LoadProject(SereinProjectData project, string filePath) { // 加载项目配置文件 @@ -515,6 +688,7 @@ namespace Serein.NodeFlow /// 远程项目地址 /// 远程项目端口 /// 密码 + [AutoSocketHandle] public void LoadRemoteProject(string addres, int port, string token) { // -- 第1种,直接从远程环境复制所有dll信息,项目信息,在本地打开?(安全问题) @@ -543,6 +717,7 @@ namespace Serein.NodeFlow /// 序列化当前项目的依赖信息、节点信息 /// /// + [AutoSocketHandle] public SereinProjectData GetProjectInfo() { var projectData = new SereinProjectData() @@ -560,6 +735,7 @@ namespace Serein.NodeFlow /// /// /// + [AutoSocketHandle] public void LoadDll(string dllPath) { LoadDllNodeInfo(dllPath); @@ -570,6 +746,7 @@ namespace Serein.NodeFlow /// /// /// + [AutoSocketHandle] public bool RemoteDll(string assemblyFullName) { var library = Librarys.Values.FirstOrDefault(nl => assemblyFullName.Equals(nl.Assembly.FullName)); @@ -624,6 +801,7 @@ namespace Serein.NodeFlow /// /// /// 如果是表达式节点条件节点,该项为null + [AutoSocketHandle] public void CreateNode(NodeControlType nodeControlType, Position position, MethodDetailsInfo? methodDetailsInfo = null) { MethodDetails? methodDetails = null; @@ -656,6 +834,7 @@ namespace Serein.NodeFlow /// /// /// + [AutoSocketHandle] public void RemoveNode(string nodeGuid) { var remoteNode = GuidToModel(nodeGuid); @@ -710,6 +889,7 @@ namespace Serein.NodeFlow /// 起始节点 /// 目标节点 /// 连接关系 + [AutoSocketHandle] public void ConnectNode(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) { // 获取起始节点与目标节点 @@ -729,6 +909,7 @@ namespace Serein.NodeFlow /// 目标节点Guid /// 连接关系 /// + [AutoSocketHandle] public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) { // 获取起始节点与目标节点 @@ -740,11 +921,12 @@ namespace Serein.NodeFlow } - + /// /// 获取方法描述 /// + public bool TryGetMethodDetailsInfo(string name, out MethodDetailsInfo? md) { if (!string.IsNullOrEmpty(name)) @@ -792,11 +974,23 @@ namespace Serein.NodeFlow } + /// + /// 移动了某个节点(远程插件使用) + /// + /// + /// + /// + [AutoSocketHandle] + public void MoveNode(string nodeGuid,double x,double y) + { + this.OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y)); + } /// /// 设置起点控件 /// /// + [AutoSocketHandle] public void SetStartNode(string newNodeGuid) { var newStartNodeModel = GuidToModel(newNodeGuid); @@ -810,6 +1004,7 @@ namespace Serein.NodeFlow /// 被中断的目标节点Guid /// 中断级别 /// 操作是否成功 + [AutoSocketHandle] public bool SetNodeInterrupt(string nodeGuid, InterruptClass interruptClass) { var nodeModel = GuidToModel(nodeGuid); @@ -845,9 +1040,10 @@ namespace Serein.NodeFlow /// /// 添加表达式中断 /// - /// - /// + /// 如果是节点,传入Guid;如果是对象,传入类型FullName + /// 合法的条件表达式 /// + [AutoSocketHandle] public bool AddInterruptExpression(string key, string expression) { if(string.IsNullOrEmpty(expression)) return false; @@ -874,9 +1070,10 @@ namespace Serein.NodeFlow /// /// 设置对象的监视状态 /// - /// - /// + /// 如果是节点,传入Guid;如果是对象,传入类型FullName + /// ture监视对象;false取消对象监视 /// + [AutoSocketHandle] public void SetMonitorObjState(string key, bool isMonitor) { if (string.IsNullOrEmpty(key)) { return; } @@ -900,8 +1097,8 @@ namespace Serein.NodeFlow /// /// 检查一个对象是否处于监听状态,如果是,则传出与该对象相关的表达式(用于中断),如果不是,则返回false。 /// - /// - /// + /// + /// /// public bool CheckObjMonitorState(string key, out List? exps) { @@ -910,9 +1107,11 @@ namespace Serein.NodeFlow } /// - /// 启动器调用,节点数据更新通知 + /// 启动器调用,运行到某个节点时触发了监视对象的更新(对象预览视图将会自动更新) /// /// + /// + /// public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) { OnMonitorObjectChange?.Invoke(new MonitorObjectEventArgs(nodeGuid, monitorData, sourceType)); @@ -929,38 +1128,7 @@ namespace Serein.NodeFlow OnInterruptTrigger?.Invoke(new InterruptTriggerEventArgs(nodeGuid, expression, type)); } - /// - /// 激活全局触发器 - /// - /// - public void ActivateFlipflopNode(string nodeGuid) - { - var nodeModel = GuidToModel(nodeGuid); - if (nodeModel is null) return; - if (flowStarter is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 - { - if (this.FlowState != RunState.Completion - && flipflopNode.NotExitPreviousNode()) // 正在运行,且该触发器没有上游节点 - { - _ = flowStarter.RunGlobalFlipflopAsync(this, flipflopNode);// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中 - - } - } - } - /// - /// 关闭全局触发器 - /// - /// - public void TerminateFlipflopNode(string nodeGuid) - { - var nodeModel = GuidToModel(nodeGuid); - if (nodeModel is null) return; - if (flowStarter is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 - { - flowStarter.TerminateGlobalFlipflopRuning(flipflopNode); - } - } - + /// /// 环境执行中断 /// @@ -971,37 +1139,6 @@ namespace Serein.NodeFlow return ChannelFlowInterrupt.GetOrCreateChannelAsync(this.EnvName); } - /// - /// 获取当前环境信息(远程连接) - /// - /// - public object GetEnvInfo() - { - Dictionary> LibraryMds = []; - - foreach (var mdskv in this.MethodDetailsOfLibrarys) - { - var library = mdskv.Key; - var mds = mdskv.Value; - foreach (var md in mds) - { - if (!LibraryMds.TryGetValue(library, out var t_mds)) - { - t_mds = new List(); - LibraryMds[library] = t_mds; - } - var mdInfo = md.ToInfo(); - mdInfo.LibraryName = library.Assembly.GetName().FullName; - t_mds.Add(mdInfo); - } - } - var project = this.GetProjectInfo(); - return new - { - project = project, - envNode = LibraryMds.Values, - }; - } /// /// Guid 转 NodeModel @@ -1345,7 +1482,7 @@ namespace Serein.NodeFlow /// public void NodeLocated(string nodeGuid) { - OnNodeLocate?.Invoke(new NodeLocatedEventArgs(nodeGuid)); + OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)); } #endregion @@ -1471,8 +1608,18 @@ namespace Serein.NodeFlow } + + + /// + /// 流程环境需要的扩展方法 + /// public static class FlowFunc { + /// + /// 程序集封装依赖 + /// + /// + /// public static Library.Entity.Library ToLibrary(this Assembly assembly) { var tmp = assembly.ManifestModule.Name; @@ -1483,6 +1630,12 @@ namespace Serein.NodeFlow }; } + /// + /// 触发器运行后状态转为对应的后继分支类别 + /// + /// + /// + /// public static ConnectionType ToContentType(this FlipflopStateType flowStateType) { return flowStateType switch @@ -1495,38 +1648,11 @@ namespace Serein.NodeFlow }; } - - public static Type? ControlTypeToModel(this NodeControlType nodeControlType) - { - // 确定创建的节点类型 - Type? nodeType = nodeControlType switch - { - NodeControlType.Action => typeof(SingleActionNode), - NodeControlType.Flipflop => typeof(SingleFlipflopNode), - - NodeControlType.ExpOp => typeof(SingleExpOpNode), - NodeControlType.ExpCondition => typeof(SingleConditionNode), - NodeControlType.ConditionRegion => typeof(CompositeConditionNode), - _ => null - }; - return nodeType; - } - public static NodeControlType ModelToControlType(this NodeControlType nodeControlType) - { - var type = nodeControlType.GetType(); - NodeControlType controlType = type switch - { - Type when type == typeof(SingleActionNode) => NodeControlType.Action, - Type when type == typeof(SingleFlipflopNode) => NodeControlType.Flipflop, - - Type when type == typeof(SingleExpOpNode) => NodeControlType.ExpOp, - Type when type == typeof(SingleConditionNode) => NodeControlType.ExpCondition, - Type when type == typeof(CompositeConditionNode) => NodeControlType.ConditionRegion, - _ => NodeControlType.None, - }; - return controlType; - } - + /// + /// 判断 触发器节点 是否存在上游分支 + /// + /// + /// public static bool NotExitPreviousNode(this SingleFlipflopNode node) { ConnectionType[] ct = [ConnectionType.IsSucceed, @@ -1542,6 +1668,43 @@ namespace Serein.NodeFlow } return true; } + + + ///// + ///// 从节点类型枚举中转为对应的 Model 类型 + ///// + ///// + ///// + //public static Type? ControlTypeToModel(this NodeControlType nodeControlType) + //{ + // // 确定创建的节点类型 + // Type? nodeType = nodeControlType switch + // { + // NodeControlType.Action => typeof(SingleActionNode), + // NodeControlType.Flipflop => typeof(SingleFlipflopNode), + + // NodeControlType.ExpOp => typeof(SingleExpOpNode), + // NodeControlType.ExpCondition => typeof(SingleConditionNode), + // NodeControlType.ConditionRegion => typeof(CompositeConditionNode), + // _ => null + // }; + // return nodeType; + //} + //public static NodeControlType ModelToControlType(this NodeControlType nodeControlType) + //{ + // var type = nodeControlType.GetType(); + // NodeControlType controlType = type switch + // { + // Type when type == typeof(SingleActionNode) => NodeControlType.Action, + // Type when type == typeof(SingleFlipflopNode) => NodeControlType.Flipflop, + + // Type when type == typeof(SingleExpOpNode) => NodeControlType.ExpOp, + // Type when type == typeof(SingleConditionNode) => NodeControlType.ExpCondition, + // Type when type == typeof(CompositeConditionNode) => NodeControlType.ConditionRegion, + // _ => NodeControlType.None, + // }; + // return controlType; + //} } diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index 5bc6e80..7f18871 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -72,7 +72,7 @@ namespace Serein.NodeFlow /// - /// 开始运行 + /// 开始运行(需要准备好方法信息) /// /// 运行环境 /// 环境中已加载的所有节点 diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 64eec3c..2ce5c50 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -1,6 +1,7 @@  + 1.0.13 net8.0 enable enable diff --git a/SereinFlow.sln b/SereinFlow.sln index 55ea542..6093d90 100644 --- a/SereinFlow.sln +++ b/SereinFlow.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34728.123 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.WorkBench", "WorkBench\Serein.WorkBench.csproj", "{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Workbench", "WorkBench\Serein.Workbench.csproj", "{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library.Core", "Library.Core\Serein.Library.Core.csproj", "{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}" EndProject @@ -22,10 +22,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net462DllTest", "Net462DllT EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Extend.RemoteControl", "Extend.FlowRemoteManagement\Serein.Extend.RemoteControl.csproj", "{3E568C47-74C6-4C28-9D43-C9BA29008DB7}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.WorkBench.Remote", "WorkBench.Remote\Serein.WorkBench.Remote.csproj", "{D550688A-4EAB-4872-8243-66D39FE3817D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.WorkBench.ControlLibrary.Core", "WorkBench.ControlLibrary.Core\Serein.WorkBench.ControlLibrary.Core.csproj", "{789E3885-3D64-461B-BEF1-DC965E7CEF57}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.FlowStartTool", "FlowStartTool\Serein.FlowStartTool.csproj", "{38D0FA92-5139-4616-A41E-8186AA4C1532}" EndProject Global @@ -62,14 +58,6 @@ Global {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Release|Any CPU.Build.0 = Release|Any CPU - {D550688A-4EAB-4872-8243-66D39FE3817D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D550688A-4EAB-4872-8243-66D39FE3817D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D550688A-4EAB-4872-8243-66D39FE3817D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D550688A-4EAB-4872-8243-66D39FE3817D}.Release|Any CPU.Build.0 = Release|Any CPU - {789E3885-3D64-461B-BEF1-DC965E7CEF57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {789E3885-3D64-461B-BEF1-DC965E7CEF57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {789E3885-3D64-461B-BEF1-DC965E7CEF57}.Release|Any CPU.ActiveCfg = Release|Any CPU - {789E3885-3D64-461B-BEF1-DC965E7CEF57}.Release|Any CPU.Build.0 = Release|Any CPU {38D0FA92-5139-4616-A41E-8186AA4C1532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {38D0FA92-5139-4616-A41E-8186AA4C1532}.Debug|Any CPU.Build.0 = Debug|Any CPU {38D0FA92-5139-4616-A41E-8186AA4C1532}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/WorkBench/App.xaml b/WorkBench/App.xaml index 8cc76d1..e6ecadf 100644 --- a/WorkBench/App.xaml +++ b/WorkBench/App.xaml @@ -1,7 +1,7 @@ - diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index fce1f39..a5769d2 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -5,7 +5,7 @@ using Serein.NodeFlow.Tool; using System.Diagnostics; using System.Windows; -namespace Serein.WorkBench +namespace Serein.Workbench { /// /// Interaction logic for App.xaml diff --git a/WorkBench/LogWindow.xaml b/WorkBench/LogWindow.xaml index 43dde90..29bc07e 100644 --- a/WorkBench/LogWindow.xaml +++ b/WorkBench/LogWindow.xaml @@ -1,9 +1,9 @@ - /// DebugWindow.xaml 的交互逻辑 @@ -23,7 +23,7 @@ namespace Serein.WorkBench private const int MaxLines = 1000; // 最大显示的行数 private bool autoScroll = true; // 自动滚动标识 private int flushThreshold = 1000; // 设置日志刷新阈值 - private const int maxFlushSize = 10000; // 每次最大刷新字符数 + private const int maxFlushSize = 1000; // 每次最大刷新字符数 public LogWindow() { diff --git a/WorkBench/MainWindow.xaml b/WorkBench/MainWindow.xaml index 92039b8..c59145c 100644 --- a/WorkBench/MainWindow.xaml +++ b/WorkBench/MainWindow.xaml @@ -1,10 +1,10 @@ - - + - - + @@ -51,6 +50,11 @@ + + + + + @@ -107,8 +111,8 @@ MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown" MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp" MouseDown="FlowChartCanvas_MouseDown" - MouseMove="FlowChartCanvas_MouseMove" MouseUp="FlowChartCanvas_MouseUp" + MouseMove="FlowChartCanvas_MouseMove" MouseWheel="FlowChartCanvas_MouseWheel" Drop="FlowChartCanvas_Drop" DragOver="FlowChartCanvas_DragOver"> diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index 4a2efd6..afce5eb 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -9,11 +9,12 @@ using Serein.NodeFlow.Base; using Serein.NodeFlow.Model; using Serein.NodeFlow.Tool; using Serein.NodeFlow.Tool.SereinExpression; -using Serein.WorkBench.Node; -using Serein.WorkBench.Node.View; -using Serein.WorkBench.Node.ViewModel; -using Serein.WorkBench.Themes; -using Serein.WorkBench.tool; +using Serein.Workbench.Node; +using Serein.Workbench.Node.View; +using Serein.Workbench.Node.ViewModel; +using Serein.Workbench.Themes; +using Serein.Workbench.tool; +using System; using System.IO; using System.Reflection; using System.Text.Json; @@ -31,18 +32,21 @@ using System.Windows.Threading; using System.Xml.Linq; using DataObject = System.Windows.DataObject; -namespace Serein.WorkBench +namespace Serein.Workbench { /// /// 拖拽创建节点类型 /// public static class MouseNodeType { + /// + /// 创建来自DLL的节点 + /// public static string CreateDllNodeInCanvas { get; } = nameof(CreateDllNodeInCanvas); + /// + /// 创建基础节点 + /// public static string CreateBaseNodeInCanvas { get; } = nameof(CreateBaseNodeInCanvas); - //public static string RegionType { get; } = nameof(RegionType); - //public static string BaseNodeType { get; } = nameof(BaseNodeType); - //public static string DllNodeType { get; } = nameof(DllNodeType); } @@ -153,6 +157,7 @@ namespace Serein.WorkBench public MainWindow() { InitializeComponent(); + logWindow = InitConsoleOut(); // 重定向 Console 输出 ViewModel = new MainWindowViewModel(this); FlowEnvironment = ViewModel.FlowEnvironment; @@ -160,7 +165,6 @@ namespace Serein.WorkBench IOCObjectViewer.FlowEnvironment = FlowEnvironment; InitFlowEnvironmentEvent(); // 配置环境事件 - logWindow = InitConsoleOut(); // 重定向 Console 输出 canvasTransformGroup = new TransformGroup(); scaleTransform = new ScaleTransform(); @@ -199,10 +203,12 @@ namespace Serein.WorkBench FlowEnvironment.OnIOCMembersChanged += FlowEnvironment_OnIOCMembersChanged; - FlowEnvironment.OnNodeLocate += FlowEnvironment_OnNodeLocate; + FlowEnvironment.OnNodeLocated += FlowEnvironment_OnNodeLocate; + FlowEnvironment.OnNodeMoved += FlowEnvironment_OnNodeMoved; } + private LogWindow InitConsoleOut() { @@ -710,6 +716,26 @@ namespace Serein.WorkBench nodeControl.RenderTransform = null; // 或者重新设置为默认值 }; } + private void FlowEnvironment_OnNodeMoved(NodeMovedEventArgs eventArgs) + { + if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl)) return; + + var newLeft = eventArgs.X; + var newTop = eventArgs.Y; + + // 限制控件不超出FlowChartCanvas的边界 + if (newLeft >= 0 && newLeft + nodeControl.ActualWidth <= FlowChartCanvas.ActualWidth) + { + Canvas.SetLeft(nodeControl, newLeft); + } + if (newTop >= 0 && newTop + nodeControl.ActualHeight <= FlowChartCanvas.ActualHeight) + { + Canvas.SetTop(nodeControl, newTop); + } + + //Canvas.SetLeft(nodeControl,); + //Canvas.SetTop(nodeControl, ); + } /// /// Guid 转 NodeControl @@ -819,7 +845,7 @@ namespace Serein.WorkBench } /// - /// 配置节点事件 + /// 配置节点事件(移动,点击相关) /// /// private void ConfigureNodeEvents(NodeControlBase nodeControl) @@ -1295,87 +1321,65 @@ namespace Serein.WorkBench if (IsControlDragging) // 如果正在拖动控件 { Point currentPosition = e.GetPosition(FlowChartCanvas); // 获取当前鼠标位置 - // 批量移动 与 单个节点控件移动 - if (selectNodeControls.Count > 0 && sender is NodeControlBase element && selectNodeControls.Contains(element)) + + if (selectNodeControls.Count > 0 && sender is NodeControlBase nodeControlMain && selectNodeControls.Contains(nodeControlMain)) { - // 获取element控件的旧位置 - double oldLeft = Canvas.GetLeft(element); - double oldTop = Canvas.GetTop(element); + // 进行批量移动 + // 获取旧位置 + var oldLeft = Canvas.GetLeft(nodeControlMain); + var oldTop = Canvas.GetTop(nodeControlMain); // 计算被选择控件的偏移量 - double deltaX = (int)(currentPosition.X - startControlDragPoint.X); - double deltaY = (int)(currentPosition.Y - startControlDragPoint.Y); + var deltaX = /*(int)*/(currentPosition.X - startControlDragPoint.X); + var deltaY = /*(int)*/(currentPosition.Y - startControlDragPoint.Y); // 移动被选择的控件 - double newLeft = oldLeft + deltaX; - double newTop = oldTop + deltaY; + var newLeft = oldLeft + deltaX; + var newTop = oldTop + deltaY; - // 限制控件不超出FlowChartCanvas的边界 - if (newLeft >= 0 && newLeft + element.ActualWidth <= FlowChartCanvas.ActualWidth) - { - Canvas.SetLeft(element, newLeft); - } - if (newTop >= 0 && newTop + element.ActualHeight <= FlowChartCanvas.ActualHeight) - { - Canvas.SetTop(element, newTop); - } + this.FlowEnvironment.MoveNode(nodeControlMain.ViewModel.Node.Guid, newLeft, newTop); // 移动节点 + + // 计算控件实际移动的距离 + var actualDeltaX = newLeft - oldLeft; + var actualDeltaY = newTop - oldTop; - // 计算element实际移动的距离 - double actualDeltaX = newLeft - oldLeft; - double actualDeltaY = newTop - oldTop; // 移动其它选中的控件 foreach (var nodeControl in selectNodeControls) { - if (nodeControl != element) // 跳过已经移动的控件 + if (nodeControl != nodeControlMain) // 跳过已经移动的控件 { - double otherNewLeft = Canvas.GetLeft(nodeControl) + actualDeltaX; - double otherNewTop = Canvas.GetTop(nodeControl) + actualDeltaY; - - // 限制控件不超出FlowChartCanvas的边界 - if (otherNewLeft >= 0 && otherNewLeft + nodeControl.ActualWidth <= FlowChartCanvas.ActualWidth) - { - Canvas.SetLeft(nodeControl, otherNewLeft); - } - if (otherNewTop >= 0 && otherNewTop + nodeControl.ActualHeight <= FlowChartCanvas.ActualHeight) - { - Canvas.SetTop(nodeControl, otherNewTop); - } + var otherNewLeft = Canvas.GetLeft(nodeControl) + actualDeltaX; + var otherNewTop = Canvas.GetTop(nodeControl) + actualDeltaY; + this.FlowEnvironment.MoveNode(nodeControl.ViewModel.Node.Guid, otherNewLeft, otherNewTop); // 移动节点 } } + + // 更新节点之间线的连接位置 foreach (var nodeControl in selectNodeControls) { UpdateConnections(nodeControl); } - startControlDragPoint = currentPosition; // 更新起始点位置 } else - { // 获取引发事件的控件 - if (sender is not UserControl block) + { // 单个节点移动 + if (sender is not NodeControlBase nodeControl) { return; } double deltaX = currentPosition.X - startControlDragPoint.X; // 计算X轴方向的偏移量 double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量 - - double newLeft = Canvas.GetLeft(block) + deltaX; // 新的左边距 - double newTop = Canvas.GetTop(block) + deltaY; // 新的上边距 - //Console.WriteLine((Canvas.GetLeft(block), Canvas.GetTop(block))); - - // 限制控件不超出FlowChartCanvas的边界 - if (newLeft >= 0 && newLeft + block.ActualWidth <= FlowChartCanvas.ActualWidth) - { - Canvas.SetLeft(block, newLeft); - } - if (newTop >= 0 && newTop + block.ActualHeight <= FlowChartCanvas.ActualHeight) - { - Canvas.SetTop(block, newTop); - } - - UpdateConnections(block); + double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距 + double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距 + this.FlowEnvironment.MoveNode(nodeControl.ViewModel.Node.Guid, newLeft, newTop); // 移动节点 + UpdateConnections(nodeControl); } startControlDragPoint = currentPosition; // 更新起始点位置 } + } + + + private void ChangeViewerObjOfNode(NodeControlBase nodeControl) { var node = nodeControl.ViewModel.Node; @@ -1755,7 +1759,6 @@ namespace Serein.WorkBench /// private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { - Console.WriteLine(1); if (!IsSelectControl) { // 进入选取状态 @@ -2352,7 +2355,7 @@ namespace Serein.WorkBench /// private void ButtonDebugFlipflopNode_Click(object sender, RoutedEventArgs e) { - FlowEnvironment?.Exit(); // 在运行平台上点击了退出 + FlowEnvironment?.ExitFlow(); // 在运行平台上点击了退出 } /// @@ -2372,7 +2375,7 @@ namespace Serein.WorkBench } else { - await this.FlowEnvironment.StartFlowInSelectNodeAsync(selectNodeControls[0].ViewModel.Node.Guid); + await this.FlowEnvironment.StartAsyncInSelectNode(selectNodeControls[0].ViewModel.Node.Guid); } } @@ -2489,25 +2492,12 @@ namespace Serein.WorkBench /// /// /// - private void OpenLocalProject_Click(object sender, RoutedEventArgs e) + private void ButtonOpenLocalProject_Click(object sender, RoutedEventArgs e) { } - /// - /// 连接远程运行环境 - /// - /// - /// - private void OpenRemoteProject_Click(object sender, RoutedEventArgs e) - { - var windowEnvRemoteLoginView = new WindowEnvRemoteLoginView((addres,port,token) => - { - this.FlowEnvironment.LoadRemoteProject(addres, port, token); - }); - windowEnvRemoteLoginView.Show(); - - } + #endregion @@ -2531,6 +2521,26 @@ namespace Serein.WorkBench #endregion + #region 顶部菜单栏 - 远程管理 + private async void ButtonStartRemoteServer_Click(object sender, RoutedEventArgs e) + { + await this.FlowEnvironment.StartRemoteServerAsync(); + } + /// + /// 连接远程运行环境 + /// + /// + /// + private void ButtonConnectionRemoteEnv_Click(object sender, RoutedEventArgs e) + { + var windowEnvRemoteLoginView = new WindowEnvRemoteLoginView((addres, port, token) => + { + this.FlowEnvironment.LoadRemoteProject(addres, port, token); + }); + windowEnvRemoteLoginView.Show(); + + } + #endregion @@ -2636,6 +2646,7 @@ namespace Serein.WorkBench MessageBox.Show("所有DLL已卸载。", "信息", MessageBoxButton.OK, MessageBoxImage.Information); } + } #region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线 diff --git a/WorkBench/MainWindowViewModel.cs b/WorkBench/MainWindowViewModel.cs index 0aa9541..03c64e0 100644 --- a/WorkBench/MainWindowViewModel.cs +++ b/WorkBench/MainWindowViewModel.cs @@ -4,7 +4,7 @@ using Serein.Library.Entity; using Serein.Library.Utils; using Serein.NodeFlow; using Serein.NodeFlow.Tool; -using Serein.WorkBench.Node.View; +using Serein.Workbench.Node.View; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -14,12 +14,24 @@ using System.Text; using System.Threading.Tasks; using System.Windows; -namespace Serein.WorkBench +namespace Serein.Workbench { + /// + /// 工作台数据视图 + /// + /// public class MainWindowViewModel { private readonly MainWindow window ; + /// + /// 运行环境 + /// public IFlowEnvironment FlowEnvironment { get; set; } + + /// + /// 工作台数据视图 + /// + /// public MainWindowViewModel(MainWindow window) { FlowEnvironment = new FlowEnvironment(); diff --git a/WorkBench/Node/NodeControlViewModelBase.cs b/WorkBench/Node/NodeControlViewModelBase.cs index 91273af..5d0c534 100644 --- a/WorkBench/Node/NodeControlViewModelBase.cs +++ b/WorkBench/Node/NodeControlViewModelBase.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; -namespace Serein.WorkBench.Node.ViewModel +namespace Serein.Workbench.Node.ViewModel { public abstract class NodeControlViewModelBase : INotifyPropertyChanged { diff --git a/WorkBench/Node/View/ActionNodeControl.xaml b/WorkBench/Node/View/ActionNodeControl.xaml index 4faf4b3..633c67e 100644 --- a/WorkBench/Node/View/ActionNodeControl.xaml +++ b/WorkBench/Node/View/ActionNodeControl.xaml @@ -1,12 +1,12 @@ - diff --git a/WorkBench/Node/View/ActionNodeControl.xaml.cs b/WorkBench/Node/View/ActionNodeControl.xaml.cs index f8852ec..c9269f1 100644 --- a/WorkBench/Node/View/ActionNodeControl.xaml.cs +++ b/WorkBench/Node/View/ActionNodeControl.xaml.cs @@ -1,9 +1,9 @@ using Serein.NodeFlow.Model; -using Serein.WorkBench.Node.ViewModel; +using Serein.Workbench.Node.ViewModel; using System.Runtime.CompilerServices; using System.Windows.Controls; -namespace Serein.WorkBench.Node.View +namespace Serein.Workbench.Node.View { /// /// ActionNode.xaml 的交互逻辑 diff --git a/WorkBench/Node/View/ActionRegionControl.xaml b/WorkBench/Node/View/ActionRegionControl.xaml index 236d16d..bc1862c 100644 --- a/WorkBench/Node/View/ActionRegionControl.xaml +++ b/WorkBench/Node/View/ActionRegionControl.xaml @@ -1,9 +1,9 @@ -