From 0a7e24d3186aab864963b97bfa376946d415ac6f Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Tue, 22 Oct 2024 00:13:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9F=BA=E7=A1=80=E8=8A=82=E7=82=B9=E7=9A=84?= =?UTF-8?q?bug=EF=BC=8C=E5=A2=9E=E5=8A=A0WebSocket=20JSON=20ID=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=EF=BC=8C=E8=BF=9C=E7=A8=8B=E7=8E=AF=E5=A2=83=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E4=BD=BF=E7=94=A8=E6=B6=88=E6=81=AFID=E4=BD=9C?= =?UTF-8?q?=E4=B8=BA=E5=93=8D=E5=BA=94key=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FlowStartTool/Properties/launchSettings.json | 10 + FlowStartTool/Serein.FlowStartTool.csproj | 13 +- Library/FlowNode/Attribute.cs | 20 +- Library/FlowNode/MethodDetails.cs | 4 +- Library/FlowNode/NodeDebugSetting.cs | 9 +- Library/FlowNode/NodeModelBaseData.cs | 3 +- Library/FlowNode/NodeModelBaseFunc.cs | 9 +- Library/FlowNode/ParameterDetails.cs | 2 +- Library/FlowNode/SereinProjectData.cs | 22 +- Library/Network/WebSocket/Attribute.cs | 13 +- Library/Network/WebSocket/Handle/Attribute.cs | 52 ++++ .../WebSocket/Handle/JsonMsgHandleConfig.cs | 60 ++-- .../WebSocket/Handle/WebSocketHandleModule.cs | 104 +++++-- .../Handle/WebSocketMsgHandleHelper.cs | 11 +- Library/Network/WebSocket/WebSocketClient.cs | 2 +- Library/Network/WebSocket/WebSocketServer.cs | 15 +- Library/Serein.Library.csproj | 6 +- Library/Utils/RemoteEnvControl.cs | 115 +++++-- NodeFlow/Env/EnvMsgTheme.cs | 8 +- NodeFlow/Env/FlowEnvironment.cs | 74 ++--- NodeFlow/Env/FlowEnvironmentDecorator.cs | 8 - NodeFlow/Env/FlowFunc.cs | 6 - NodeFlow/Env/MsgControllerOfClient.cs | 77 ++--- NodeFlow/Env/MsgControllerOfServer.cs | 19 +- NodeFlow/Env/RemoteFlowEnvironment.cs | 50 ++- NodeFlow/Model/CompositeConditionNode.cs | 23 +- NodeFlow/Model/SingleConditionNode.cs | 76 ++--- NodeFlow/Model/SingleExpOpNode.cs | 35 ++- NodeFlow/Serein.NodeFlow.csproj | 9 +- NodeFlow/Tool/ObjDynamicCreateHelper.cs | 3 +- Serein.Library.MyGenerator/Attribute.cs | 73 +++++ .../ParameterDetailsPropertyGenerator.cs | 106 +++++-- .../Properties/launchSettings.json | 8 +- .../Serein.Library.NodeGenerator.csproj | 2 +- WorkBench/App.xaml.cs | 4 +- WorkBench/MainWindow.xaml.cs | 82 +++-- WorkBench/MainWindowViewModel.cs | 1 + WorkBench/Node/NodeControlViewModelBase.cs | 130 ++++---- WorkBench/Node/View/ActionNodeControl.xaml | 24 +- WorkBench/Node/View/ConditionNodeControl.xaml | 12 +- WorkBench/Node/View/ExpOpNodeControl.xaml | 2 +- WorkBench/Node/View/FlipflopNodeControl.xaml | 15 +- WorkBench/Node/View/NodeControlBase.cs | 7 +- .../ViewModel/ActionNodeControlViewModel.cs | 13 +- .../ConditionNodeControlViewModel.cs | 52 ++-- .../Node/ViewModel/ExpOpNodeViewModel.cs | 24 +- .../ViewModel/FlipflopNodeControlViewModel.cs | 4 +- .../Serein.Workbench_wjzi1sgn_wpftmp.csproj | 292 ++++++++++++++++++ 48 files changed, 1209 insertions(+), 500 deletions(-) create mode 100644 FlowStartTool/Properties/launchSettings.json create mode 100644 Serein.Library.MyGenerator/Attribute.cs create mode 100644 Workbench/Serein.Workbench_wjzi1sgn_wpftmp.csproj diff --git a/FlowStartTool/Properties/launchSettings.json b/FlowStartTool/Properties/launchSettings.json new file mode 100644 index 0000000..a742007 --- /dev/null +++ b/FlowStartTool/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "Serein.FlowStartTool": { + "commandName": "Project" + }, + "配置文件 1": { + "commandName": "DebugRoslynComponent" + } + } +} \ No newline at end of file diff --git a/FlowStartTool/Serein.FlowStartTool.csproj b/FlowStartTool/Serein.FlowStartTool.csproj index bb0d729..1d48b61 100644 --- a/FlowStartTool/Serein.FlowStartTool.csproj +++ b/FlowStartTool/Serein.FlowStartTool.csproj @@ -9,6 +9,9 @@ enable prompt starter + + + @@ -23,8 +26,16 @@ + + + + + + - + + diff --git a/Library/FlowNode/Attribute.cs b/Library/FlowNode/Attribute.cs index 22a16c7..f282b59 100644 --- a/Library/FlowNode/Attribute.cs +++ b/Library/FlowNode/Attribute.cs @@ -6,9 +6,16 @@ using System.Threading.Tasks; namespace Serein.Library { + /// + /// 标识一个类中的某些字段需要生成相应代码 + /// [AttributeUsage(AttributeTargets.Class, Inherited = true)] - internal sealed class AutoPropertyAttribute : Attribute + public sealed class AutoPropertyAttribute : Attribute { + /// + /// 属性路径 + /// CustomNode : 自定义节点 + /// public string ValuePath = string.Empty; } @@ -16,10 +23,19 @@ namespace Serein.Library /// 自动生成环境的属性 /// [AttributeUsage(AttributeTargets.Field, Inherited = true)] - internal sealed class PropertyInfoAttribute : Attribute + public sealed class PropertyInfoAttribute : Attribute { + /// + /// 是否通知UI + /// public bool IsNotification = false; + /// + /// 是否使用Console.WriteLine打印 + /// public bool IsPrint = false; + /// + /// 是否禁止参数进行修改(初始化后不能再通过setter修改) + /// public bool IsProtection = false; } diff --git a/Library/FlowNode/MethodDetails.cs b/Library/FlowNode/MethodDetails.cs index d6ce6ea..07cd638 100644 --- a/Library/FlowNode/MethodDetails.cs +++ b/Library/FlowNode/MethodDetails.cs @@ -9,7 +9,7 @@ namespace Serein.Library /// /// 每个节点有独自的MethodDetails实例 /// - [AutoProperty(ValuePath = nameof(MethodDetails))] + [NodeProperty(ValuePath = NodeValuePath.Method)] public partial class MethodDetails { private readonly IFlowEnvironment env; @@ -142,7 +142,7 @@ namespace Serein.Library MethodLockName = this.MethodLockName, IsProtectionParameter = this.IsProtectionParameter, }; - md.ParameterDetailss = this.ParameterDetailss.Select(p => p.CloneOfClone(env, nodeModel)).ToArray(); // 拷贝属于节点方法的新入参描述 + md.ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfClone(env, nodeModel)).ToArray(); // 拷贝属于节点方法的新入参描述 return md; } diff --git a/Library/FlowNode/NodeDebugSetting.cs b/Library/FlowNode/NodeDebugSetting.cs index 8c3dc45..7cfc4fd 100644 --- a/Library/FlowNode/NodeDebugSetting.cs +++ b/Library/FlowNode/NodeDebugSetting.cs @@ -9,7 +9,7 @@ namespace Serein.Library /// /// 节点调试设置,用于中断节点的运行 /// - [AutoProperty(ValuePath = nameof(NodeDebugSetting))] + [NodeProperty(ValuePath = NodeValuePath.DebugSetting)] public partial class NodeDebugSetting { private readonly NodeModelBase nodeModel; @@ -33,6 +33,13 @@ namespace Serein.Library [PropertyInfo] private InterruptClass _interruptClass = InterruptClass.None; + /// + /// 中断级别,暂时停止继续执行后继分支。 + /// + [PropertyInfo(IsNotification = true)] + private bool _isInterrupt = false; + + /// /// 取消中断的回调函数 /// diff --git a/Library/FlowNode/NodeModelBaseData.cs b/Library/FlowNode/NodeModelBaseData.cs index e1f7798..cb92d99 100644 --- a/Library/FlowNode/NodeModelBaseData.cs +++ b/Library/FlowNode/NodeModelBaseData.cs @@ -1,4 +1,5 @@ using Serein.Library.Api; +using Serein.Library.NodeGenerator; using System; using System.Collections.Generic; using System.ComponentModel; @@ -10,7 +11,7 @@ namespace Serein.Library /// /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域 /// - [AutoProperty(ValuePath = nameof(NodeModelBase))] // 是否更名为 NodeProperty? + [NodeProperty(ValuePath = NodeValuePath.None)] public abstract partial class NodeModelBase : IDynamicFlowNode { diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index 54f6fb8..0a3ae20 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -86,7 +86,13 @@ namespace Serein.Library /// public virtual NodeModelBase LoadInfo(NodeInfo nodeInfo) { - this.Guid = nodeInfo.Guid; + this.Guid = nodeInfo.Guid; + + if (nodeInfo.Position is null) + { + nodeInfo.Position = new PositionOfUI(0, 0); + } + this.Position = nodeInfo.Position;// 加载位置信息 if (this.MethodDetails != null) { for (int i = 0; i < nodeInfo.ParameterData.Length; i++) @@ -96,7 +102,6 @@ namespace Serein.Library this.MethodDetails.ParameterDetailss[i].DataValue = pd.Value; } } - this.Position = nodeInfo.Position;// 加载位置信息 return this; } diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index 09116b0..54b25e8 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -11,7 +11,7 @@ namespace Serein.Library /// /// 节点入参参数详情 /// - [AutoProperty(ValuePath = nameof(ParameterDetails))] + [NodeProperty(ValuePath = NodeValuePath.Parameter)] public partial class ParameterDetails { private readonly IFlowEnvironment env; diff --git a/Library/FlowNode/SereinProjectData.cs b/Library/FlowNode/SereinProjectData.cs index 473103b..1dcf25c 100644 --- a/Library/FlowNode/SereinProjectData.cs +++ b/Library/FlowNode/SereinProjectData.cs @@ -11,7 +11,7 @@ namespace Serein.Library { /// - /// 环境信息(远程控制用) + /// 环境信息 /// public class FlowEnvInfo { @@ -27,10 +27,18 @@ namespace Serein.Library // IOC节点对象信息 } + /// + /// 程序集相关的方法信息 + /// public class LibraryMds { + /// + /// 程序集FullName + /// public string LibraryName { get; set; } - + /// + /// 相关的方法详情 + /// public MethodDetailsInfo[] Mds { get; set; } } @@ -70,7 +78,7 @@ namespace Serein.Library } /// - /// 基础 + /// 基础,项目文件相关 /// public class Basic { @@ -87,7 +95,7 @@ namespace Serein.Library public string Versions { get; set; } } /// - /// 画布 + /// 画布信息,项目文件相关 /// public class FlowCanvas { @@ -122,7 +130,7 @@ namespace Serein.Library } /// - /// DLL + /// 项目依赖的程序集,项目文件相关 /// public class Library { @@ -144,7 +152,7 @@ namespace Serein.Library } /// - /// 节点 + /// 节点信息,项目文件相关 /// public class NodeInfo { @@ -215,7 +223,7 @@ namespace Serein.Library } /// - /// 显示参数 + /// 显示参数,项目文件相关 /// public class Parameterdata { diff --git a/Library/Network/WebSocket/Attribute.cs b/Library/Network/WebSocket/Attribute.cs index 940ee2d..995ea75 100644 --- a/Library/Network/WebSocket/Attribute.cs +++ b/Library/Network/WebSocket/Attribute.cs @@ -27,6 +27,7 @@ namespace Serein.Library.Network.WebSocketCommunication { public string ThemeKey; public string DataKey; + public string MsgIdKey; } @@ -71,13 +72,19 @@ namespace Serein.Library.Network.WebSocketCommunication } /// - /// 使用消息DataKey整体数据 + /// 使用 DataKey 整体数据 /// [AttributeUsage(AttributeTargets.Parameter)] - public sealed class UseMsgDataAttribute : Attribute + public sealed class UseDataAttribute : Attribute + { + } + /// + /// 使用 MsgIdKey 整体数据 + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class UseMsgIdAttribute : Attribute { } - internal class SocketHandleModule { diff --git a/Library/Network/WebSocket/Handle/Attribute.cs b/Library/Network/WebSocket/Handle/Attribute.cs index d5f2287..8184f84 100644 --- a/Library/Network/WebSocket/Handle/Attribute.cs +++ b/Library/Network/WebSocket/Handle/Attribute.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace Serein.Library.Network.WebSocketCommunication.Handle @@ -12,4 +13,55 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle public sealed class NeedfulAttribute : Attribute { } + + /// + /// 消息ID生成器 + /// + public class MsgIdHelper + { + private static readonly long _epoch = new DateTime(2023, 1, 1).Ticks; // 自定义起始时间 + private static long _lastTimestamp = -1L; // 上一次生成 ID 的时间戳 + private static long _sequence = 0L; // 序列号 + + /// + /// 获取新的ID + /// + public static long NewId => GenerateId(); + + + /// + /// 生成消息ID + /// + /// + public static long GenerateId() + { + long timestamp = DateTime.UtcNow.Ticks; + + // 如果时间戳是一样的,递增序列号 + if (timestamp == _lastTimestamp) + { + // 使用原子操作增加序列号 + _sequence = Interlocked.Increment(ref _sequence); + if (_sequence > 999999) // 序列号最大值,6位 + { + // 等待下一毫秒 + while (timestamp <= _lastTimestamp) + { + timestamp = DateTime.UtcNow.Ticks; + } + } + } + else + { + _sequence = 0; // 重置序列号 + } + + _lastTimestamp = timestamp; + + // 生成 ID:时间戳和序列号拼接 + return (timestamp - _epoch) * 1000 + _sequence; // 返回 ID + } + } + + } diff --git a/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs b/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs index 87adcbc..5bbc321 100644 --- a/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs +++ b/Library/Network/WebSocket/Handle/JsonMsgHandleConfig.cs @@ -41,7 +41,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle this.OnExceptionTracking = onExceptionTracking; this.ArgNotNull = ArgNotNull; - this.useMsgData = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); + this.useData = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); + this.useMsgId = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); #if NET5_0_OR_GREATER this.IsCheckArgNotNull = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); #endif @@ -101,17 +102,24 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// private readonly Type[] ParameterType; /// - /// 是否使用整体data参数 + /// 是否使Data整体内容作为入参参数 /// - private readonly bool[] useMsgData; + private readonly bool[] useData; + /// + /// 是否使用消息ID作为入参参数 + /// + private readonly bool[] useMsgId; /// /// 是否检查变量为空 /// private readonly bool[] IsCheckArgNotNull; + //private object ConvertArg(Type type, string argName ) + //{ + //} - public async void Handle(Func SendAsync, JObject jsonObject) + public async void Handle(Func SendAsync,string msgId, JObject jsonObject) { object[] args = new object[ParameterType.Length]; bool isCanInvoke = true;; // 表示是否可以调用方法 @@ -119,10 +127,17 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle { var type = ParameterType[i]; var argName = ParameterName[i]; - if (useMsgData[i]) + #region DATA JSON数据 + if (useData[i]) { args[i] = jsonObject.ToObject(type); + } + #endregion + else if (useMsgId[i]) + { + args[i] = msgId; } + #region 值类型参数 else if (type.IsValueType) { var jsonValue = jsonObject.GetValue(argName); @@ -143,7 +158,9 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle break; } } - } + } + #endregion + #region 引用类型参数 else if (type.IsClass) { var jsonValue = jsonObject.GetValue(argName); @@ -164,7 +181,9 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle break; } } - } + } + #endregion + #region 传递消息委托 else if (type.IsGenericType) // 传递SendAsync委托 { if (type.IsAssignableFrom(typeof(Func))) @@ -198,7 +217,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle await SendAsync.Invoke(jsonText); }); } - } + } + #endregion } if (!isCanInvoke) @@ -212,8 +232,6 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func> hasResultTask) { result = await hasResultTask(Instance, args); - //Console.WriteLine(result); - // why not data? } else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func task) { @@ -233,34 +251,30 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle { result = null; await Console.Out.WriteLineAsync(ex.Message); - this.OnExceptionTracking.Invoke(ex, (async data => + this.OnExceptionTracking.Invoke(ex, (async exData => { - - var jsonText = JsonConvert.SerializeObject(data); - await SendAsync.Invoke(jsonText); + await SendAsync.Invoke(exData); })); } //sw.Stop(); //Console.WriteLine($"Emit Invoke:{sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs"); - if(result is null) + + if (Module.IsReturnValue) { - return; - } - else - { - if (Module.IsReturnValue) + if (result is null) { - _ = SendAsync.Invoke(result); + result = "null"; } + _ = SendAsync.Invoke(result); } //if( && result != null && result.GetType().IsClass) //{ // //var reusltJsonText = JsonConvert.SerializeObject(result); - + // //_ = SendAsync.Invoke($"{reusltJsonText}"); //} - + } diff --git a/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs b/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs index cac93a2..4db7922 100644 --- a/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs +++ b/Library/Network/WebSocket/Handle/WebSocketHandleModule.cs @@ -19,21 +19,27 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// Json消息处理模块 /// - public WebSocketHandleModule(string ThemeJsonKey, string DataJsonKey) + public WebSocketHandleModule(string themeJsonKey, string dataJsonKey, string msgIdJsonKey) { - this.ThemeJsonKey = ThemeJsonKey; - this.DataJsonKey = DataJsonKey; + this.ThemeJsonKey = themeJsonKey; + this.DataJsonKey = dataJsonKey; + this.MsgIdJsonKey = msgIdJsonKey; } /// - /// 指示处理模块该使用json中的哪个key作为业务区别字段 + /// 指示处理模块该使用 Json 中的哪个 Key 作为业务区别字段 /// public string ThemeJsonKey { get; } /// - /// 指示处理模块该使用json中的哪个key作为业务数据字段 + /// 指示处理模块该使用 Json 中的哪个 Key 作为业务数据字段 /// public string DataJsonKey { get; } + + /// + /// 指示处理模块该使用 Json 中的哪个 Key 作为业务消息ID字段 + /// + public string MsgIdJsonKey { get; } /// /// 存储处理数据的配置 @@ -88,34 +94,28 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// 处理JSON数据 /// - /// + /// /// - public void HandleSocketMsg(Func tSendAsync, JObject jsonObject) + public void HandleSocketMsg(Func sendAsync, JObject jsonObject) { // 获取到消息 - string themeKeyName = jsonObject.GetValue(ThemeJsonKey)?.ToString(); - if (!MyHandleConfigs.TryGetValue(themeKeyName, out var handldConfig)) + string theme = jsonObject.GetValue(ThemeJsonKey)?.ToString(); + if (!MyHandleConfigs.TryGetValue(theme, out var handldConfig)) { // 没有主题 return; } + string msgId = jsonObject.GetValue(MsgIdJsonKey)?.ToString(); + - Func SendAsync = async (data) => - { - var sendMsg = new - { - theme = themeKeyName, - token = "", - data = data, - }; - var msg = JsonConvert.SerializeObject(sendMsg); - await tSendAsync(msg); - }; try { - JObject dataObj = jsonObject.GetValue(DataJsonKey).ToObject(); - handldConfig.Handle(SendAsync, dataObj); + JObject dataObj = jsonObject.GetValue(DataJsonKey)?.ToObject(); + handldConfig.Handle(async (data) => + { + await this.SendAsync(sendAsync, msgId, theme, data); + }, msgId, dataObj); } catch (Exception ex) @@ -123,12 +123,64 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle Console.WriteLine($"error in ws : {ex.Message}{Environment.NewLine}json value:{jsonObject}"); return; } - - - - } + + /// + /// 发送消息 + /// + /// + /// + /// + /// + /// + public async Task SendAsync(Func sendAsync,string msgId, string theme, object data) + { + JObject jsonData; + + if (data is null) + { + jsonData = new JObject() + { + [MsgIdJsonKey] = msgId, + [ThemeJsonKey] = theme, + }; + } + else + { + + JToken dataToken; + if ((data is System.Collections.IEnumerable || data is Array)) + { + dataToken = JArray.FromObject(data); + } + else + { + dataToken = JObject.FromObject(data); + } + + jsonData = new JObject() + { + [MsgIdJsonKey] = msgId, + [ThemeJsonKey] = theme, + [DataJsonKey] = dataToken + }; + + + } + + var msg = jsonData.ToString(); + //Console.WriteLine(msg); + //Console.WriteLine(); + + await sendAsync.Invoke(msg); + } + + } + + + + } diff --git a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs index e7c6713..1a4bc78 100644 --- a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs +++ b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs @@ -41,13 +41,14 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// /// /// + /// /// - private WebSocketHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName) + private WebSocketHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName, string msgIdKeyName) { var key = (themeKeyName, dataKeyName); if (!MyHandleModuleDict.TryGetValue(key, out var myHandleModule)) { - myHandleModule = new WebSocketHandleModule(themeKeyName, dataKeyName); + myHandleModule = new WebSocketHandleModule(themeKeyName, dataKeyName, msgIdKeyName); MyHandleModuleDict[key] = myHandleModule; } return myHandleModule; @@ -93,8 +94,9 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle var themeKey = moduleAttribute.ThemeKey; var dataKey = moduleAttribute.DataKey; + var msgIdKey = moduleAttribute.MsgIdKey; - var handlemodule = AddMyHandleModule(themeKey, dataKey); + var handleModule = AddMyHandleModule(themeKey, dataKey, msgIdKey); var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .Select(method => { @@ -135,7 +137,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle try { var jsonMsgHandleConfig = new JsonMsgHandleConfig(module, socketControlBase, method, onExceptionTracking, argNotNull); - var result = handlemodule.AddHandleConfigs(module,jsonMsgHandleConfig); + var result = handleModule.AddHandleConfigs(module,jsonMsgHandleConfig); if (!result) { throw new Exception("添加失败,已经添加过相同的配置"); @@ -158,6 +160,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle /// public async Task HandleMsgAsync(Func SendAsync, string message) { + //Console.WriteLine(message); JObject json = JObject.Parse(message); await Task.Run(() => { diff --git a/Library/Network/WebSocket/WebSocketClient.cs b/Library/Network/WebSocket/WebSocketClient.cs index b300a55..23f1af5 100644 --- a/Library/Network/WebSocket/WebSocketClient.cs +++ b/Library/Network/WebSocket/WebSocketClient.cs @@ -96,7 +96,7 @@ namespace Serein.Library.Network.WebSocketCommunication { var completeMessage = receivedMessage.ToString(); _ = MsgHandleHelper.HandleMsgAsync(SendAsync, completeMessage); // 处理消息 - Debug.WriteLine($"Received: {completeMessage}"); + //Debug.WriteLine($"Received: {completeMessage}"); } // 清空 StringBuilder 为下一条消息做准备 diff --git a/Library/Network/WebSocket/WebSocketServer.cs b/Library/Network/WebSocket/WebSocketServer.cs index 1ee4343..e65c58f 100644 --- a/Library/Network/WebSocket/WebSocketServer.cs +++ b/Library/Network/WebSocket/WebSocketServer.cs @@ -180,10 +180,10 @@ namespace Serein.Library.Network.WebSocketCommunication return; } - Func SendAsync = async (text) => - { - await WebSocketServer.SendAsync(webSocket, text); - }; + //Func SendAsync = async (text) => + //{ + // await WebSocketServer.SendAsync(webSocket, text); + //}; var buffer = new byte[1024]; var receivedMessage = new StringBuilder(); // 用于拼接长消息 @@ -209,7 +209,7 @@ namespace Serein.Library.Network.WebSocketCommunication if (result.MessageType == WebSocketMessageType.Close) { - SendAsync = null; + //SendAsync = null; await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); if (IsCheckToken) { @@ -233,7 +233,10 @@ namespace Serein.Library.Network.WebSocketCommunication } // 消息处理 - _ = MsgHandleHelper.HandleMsgAsync(SendAsync, message); // 处理消息 + _ = MsgHandleHelper.HandleMsgAsync(async (text) => + { + await WebSocketServer.SendAsync(webSocket, text); + }, message); // 处理消息 } // 清空 StringBuilder 为下一条消息做准备 diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index a29c9f8..b62df21 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -12,8 +12,8 @@ https://github.com/fhhyyp/serein-flow MIT True + true - true .\obj\g @@ -30,6 +30,10 @@ + + + + diff --git a/Library/Utils/RemoteEnvControl.cs b/Library/Utils/RemoteEnvControl.cs index 643ed78..15e8cab 100644 --- a/Library/Utils/RemoteEnvControl.cs +++ b/Library/Utils/RemoteEnvControl.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Serein.Library.Network.WebSocketCommunication; using System; using System.Collections.Generic; @@ -15,29 +16,52 @@ namespace Serein.Library.Utils public class RemoteEnvControl { /// - /// 配置远程连接IP端口 + /// 远程环境配置 /// - public RemoteEnvControl(string addres, int port, object token) + public class ControlConfiguration { - this.Addres = addres; - this.Port = port; - this.Token = token; + /// + /// 远程环境的网络地址 + /// + public string Addres { get; set; } + + /// + /// 远程环境的对外端口 + /// + public int Port { get; set; } + + /// + /// 登录远程环境必须携带的token(可以为可序列化的JSON对象) + /// + public object Token { get; set; } + + /// + /// 有关消息ID的 Json Key + /// + public string MsgIdJsonKey { get; set; } + /// + /// 有关消息主题的 Json Key + /// + public string ThemeJsonKey { get; set; } + /// + /// 有关数据的 Json Key + /// + public string DataJsonKey { get; set; } } /// - /// 远程环境的网络地址 + /// 配置远程连接IP端口 /// - public string Addres { get; } + public RemoteEnvControl(ControlConfiguration controlConfiguration) + { + Config = controlConfiguration; + } /// - /// 远程环境的对外端口 + /// 配置信息 /// - public int Port { get; } + public ControlConfiguration Config { get; } - /// - /// 登录远程环境必须携带的token(可以为可序列化的JSON对象) - /// - public object Token { get; } @@ -49,8 +73,8 @@ namespace Serein.Library.Utils /// /// 是否连接到了远程环境 /// - public bool IsConnectdRemoteEnv { get => isConnectdRemoteEnv; } - private bool isConnectdRemoteEnv = false; + //public bool IsConnectdRemoteEnv { get => isConnectdRemoteEnv; } + //private bool isConnectdRemoteEnv = false; /// /// 尝试连接到远程环境 @@ -59,12 +83,12 @@ namespace Serein.Library.Utils public async Task ConnectAsync() { // 第2种,WebSocket连接到远程环境,实时接收远程环境的响应? - Console.WriteLine($"准备连接:{Addres}:{Port},{Token}"); + Console.WriteLine($"准备连接:{Config.Addres}:{Config.Port},{Config.Token}"); bool success = false; try { var tcpClient = new TcpClient(); - var result = tcpClient.BeginConnect(Addres, Port, null, null); + var result = tcpClient.BeginConnect(Config.Addres, Config.Port, null, null); success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(3)); } finally @@ -73,14 +97,14 @@ namespace Serein.Library.Utils } if (!success) { - Console.WriteLine($"无法连通远程端口 {Addres}:{Port}"); + Console.WriteLine($"无法连通远程端口 {Config.Addres}:{Config.Port}"); return false; } else { - var url = $"ws://{Addres}:{Port}/"; + var url = $"ws://{Config.Addres}:{Config.Port}/"; var result = await EnvClient.ConnectAsync(url); // 尝试连接远程环境 - this.isConnectdRemoteEnv = result; + //this.isConnectdRemoteEnv = result; return result; } } @@ -90,18 +114,53 @@ namespace Serein.Library.Utils /// /// 发送消息 /// + /// /// /// /// - public async Task SendAsync(string theme, object data) + public async Task SendAsync(string msgId , string theme, object data) { - var sendMsg = new + //var sendMsg = new + //{ + // theme = theme, + // token = this.Token, + // data = data, + //}; + //var msg = JsonConvert.SerializeObject(sendMsg); + JObject jsonData; + + if (data is null) { - theme = theme, - token = this.Token, - data = data, - }; - var msg = JsonConvert.SerializeObject(sendMsg); + jsonData = new JObject() + { + [Config.MsgIdJsonKey] = msgId, + [Config.ThemeJsonKey] = theme, + }; + } + else + { + JToken dataToken; + if (data is System.Collections.IEnumerable || data is Array) + { + dataToken = JArray.FromObject(data); + } + else + { + dataToken = JObject.FromObject(data); + } + + jsonData = new JObject() + { + [Config.MsgIdJsonKey] = msgId, + [Config.ThemeJsonKey] = theme, + [Config.DataJsonKey] = dataToken + }; + } + + var msg = jsonData.ToString(); + //Console.WriteLine(msg); + //Console.WriteLine(); + await EnvClient.SendAsync(msg); } @@ -113,8 +172,6 @@ namespace Serein.Library.Utils - - } diff --git a/NodeFlow/Env/EnvMsgTheme.cs b/NodeFlow/Env/EnvMsgTheme.cs index ee193ce..04fe42a 100644 --- a/NodeFlow/Env/EnvMsgTheme.cs +++ b/NodeFlow/Env/EnvMsgTheme.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.NodeFlow.Env +namespace Serein.NodeFlow.Env { /// /// 消息主题 diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 9a00c63..d23b216 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -1,28 +1,15 @@  using Newtonsoft.Json; -using Newtonsoft.Json.Bson; -using Newtonsoft.Json.Linq; using Serein.Library; using Serein.Library.Api; -using Serein.Library.Network.WebSocketCommunication; using Serein.Library.Utils; using Serein.Library.Utils.SereinExpression; using Serein.NodeFlow.Model; using Serein.NodeFlow.Tool; -using System; -using System.Collections; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Net.Sockets; -using System.Numerics; using System.Reflection; -using System.Security.Cryptography; -using System.Threading; using System.Xml.Linq; using static Serein.Library.Utils.ChannelFlowInterrupt; -using static Serein.NodeFlow.FlowStarter; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Serein.NodeFlow.Env { @@ -41,6 +28,7 @@ namespace Serein.NodeFlow.Env public const string SpaceName = $"{nameof(Serein)}.{nameof(NodeFlow)}.{nameof(Model)}"; public const string ThemeKey = "theme"; public const string DataKey = "data"; + public const string MsgIdKey = "msgid"; /// /// 流程运行环境 @@ -80,10 +68,6 @@ namespace Serein.NodeFlow.Env - - - - /// /// 打开远程管理 /// @@ -112,6 +96,7 @@ namespace Serein.NodeFlow.Env Console.WriteLine("结束远程管理异常:" + ex); } } + #endregion #region 环境运行事件 @@ -347,7 +332,6 @@ namespace Serein.NodeFlow.Env } - /// /// 异步运行 /// @@ -566,8 +550,17 @@ namespace Serein.NodeFlow.Env } else { + MethodDetails? methodDetails = null; + if (!string.IsNullOrEmpty(nodeInfo.MethodName)) + { + MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载项目时尝试获取方法信息 + } + else + { - MethodDetailss.TryGetValue(nodeInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息 + } + + var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点 nodeModel.LoadInfo(nodeInfo); // 创建节点model if (nodeModel is null) @@ -575,6 +568,8 @@ namespace Serein.NodeFlow.Env nodeInfo.Guid = string.Empty; continue; } + + TryAddNode(nodeModel); // 加载项目时将节点加载到环境中 if (nodeInfo.ChildNodeGuids?.Length > 0) { @@ -662,9 +657,6 @@ namespace Serein.NodeFlow.Env } - - - /// /// 加载远程环境 /// @@ -679,7 +671,17 @@ namespace Serein.NodeFlow.Env return (false, null); } // 没有连接远程环境,可以重新连接 - var remoteEnvControl = new RemoteEnvControl(addres, port, token); + + var controlConfiguration = new RemoteEnvControl.ControlConfiguration + { + Addres = addres, + Port = port, + Token = token, + ThemeJsonKey = FlowEnvironment.ThemeKey, + MsgIdJsonKey = FlowEnvironment.MsgIdKey, + DataJsonKey = FlowEnvironment.DataKey, + }; + var remoteEnvControl = new RemoteEnvControl(controlConfiguration); var result = await remoteEnvControl.ConnectAsync(); if (!result) { @@ -805,7 +807,6 @@ namespace Serein.NodeFlow.Env } } - TryAddNode(nodeModel); nodeModel.Position = position; @@ -1199,30 +1200,11 @@ namespace Serein.NodeFlow.Env } NodeValueChangeLogger.Add((nodeGuid, path, value)); var setExp = $"@Set .{path} = {value}"; // 生成 set 表达式 - SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); // 更改对应的数据 - - - - //Console.WriteLine($"本地环境收到数据更改通知:{value}"); //var getExp = $"@Get .{path}"; - ////Console.WriteLine($"取值表达式:{getExp}"); + SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); // 更改对应的数据 //var getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); - ////Console.WriteLine($"原数据 :{getResult}"); - //if (getResult.Equals(value)) - //{ - // Console.WriteLine("无须修改"); - // return; - //} - - - //NodeValueChangeLogger.Add((nodeGuid, path, value)); - - - //var setExp = $"@Set .{path} = {value}"; - ////Console.WriteLine($"设值表达式:{setExp}"); - //SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); - //getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); - //Console.WriteLine($"新数据 :{getResult}"); + //Console.WriteLine($"Set表达式:{setExp},result : {getResult}"); + } diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs index 05255ce..a9848ef 100644 --- a/NodeFlow/Env/FlowEnvironmentDecorator.cs +++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs @@ -1,14 +1,6 @@ using Serein.Library; using Serein.Library.Api; using Serein.Library.Utils; -using Serein.Library.Web; -using Serein.NodeFlow.Tool; -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Serein.NodeFlow.Env { diff --git a/NodeFlow/Env/FlowFunc.cs b/NodeFlow/Env/FlowFunc.cs index cda1add..6173f04 100644 --- a/NodeFlow/Env/FlowFunc.cs +++ b/NodeFlow/Env/FlowFunc.cs @@ -1,12 +1,6 @@ using Serein.Library; using Serein.Library.Api; using Serein.NodeFlow.Model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace Serein.NodeFlow.Env { diff --git a/NodeFlow/Env/MsgControllerOfClient.cs b/NodeFlow/Env/MsgControllerOfClient.cs index 8eaeaec..53548c8 100644 --- a/NodeFlow/Env/MsgControllerOfClient.cs +++ b/NodeFlow/Env/MsgControllerOfClient.cs @@ -1,12 +1,7 @@ -using Newtonsoft.Json; -using Serein.Library; +using Serein.Library; using Serein.Library.Network.WebSocketCommunication; +using Serein.Library.Network.WebSocketCommunication.Handle; using Serein.Library.Utils; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Serein.NodeFlow.Env { @@ -17,33 +12,43 @@ namespace Serein.NodeFlow.Env /// 客户端的消息管理(用于处理服务端的响应) /// - [AutoSocketModule(ThemeKey = FlowEnvironment.ThemeKey, DataKey = FlowEnvironment.DataKey)] + [AutoSocketModule(ThemeKey = FlowEnvironment.ThemeKey, + DataKey = FlowEnvironment.DataKey, + MsgIdKey = FlowEnvironment.MsgIdKey)] public class MsgControllerOfClient : ISocketHandleModule { public Guid HandleGuid => new Guid(); - private readonly Func SendCommandAsync; + + // 消息主题,data - task等待 + private readonly Func SendCommandFunc; private readonly RemoteFlowEnvironment remoteFlowEnvironment; - public MsgControllerOfClient(RemoteFlowEnvironment remoteFlowEnvironment, Func func) + public MsgControllerOfClient(RemoteFlowEnvironment remoteFlowEnvironment, Func func) { this.remoteFlowEnvironment = remoteFlowEnvironment; - SendCommandAsync = func; + SendCommandFunc = func; + } + private async Task SendCommandAsync(string msgId, string theme, object? data) + { + await SendCommandFunc.Invoke(msgId, theme, data); } + /// - /// 发送请求并等待远程环境响应 + /// 发送请求 /// /// /// 超时触发 - public async Task SendAsync(string signal, object? sendData = null, int overtimeInMs = 100) + public async Task SendAsync(string signal, object? data = null, int overtimeInMs = 100) { //Console.WriteLine($"指令[{signal}],value:{JsonConvert.SerializeObject(sendData)}"); if (!DebounceHelper.CanExecute(signal, overtimeInMs)) { return; } - await SendCommandAsync.Invoke(signal, sendData); + var msgId = MsgIdHelper.GenerateId().ToString(); + await SendCommandAsync(msgId, signal, data); } /// @@ -51,11 +56,13 @@ namespace Serein.NodeFlow.Env /// /// /// 超时触发 - public async Task SendAndWaitDataAsync(string signal, object? sendData = null, int overtimeInMs = 50) + public async Task SendAndWaitDataAsync(string theme, object? data = null, int overtimeInMs = 50) { //Console.WriteLine($"指令[{signal}],value:{JsonConvert.SerializeObject(sendData)}"); - _ = SendCommandAsync.Invoke(signal, sendData); - return await remoteFlowEnvironment.WaitData(signal); + + var msgId = MsgIdHelper.GenerateId().ToString(); + _ = SendCommandAsync(msgId, theme, data); + return await remoteFlowEnvironment.WaitData(msgId); //if (DebounceHelper.CanExecute(signal, overtimeInMs)) //{ @@ -86,63 +93,63 @@ namespace Serein.NodeFlow.Env /// /// 远程环境发来项目信息 /// + /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.GetEnvInfo)] - public void GetEnvInfo([UseMsgData] FlowEnvInfo flowEnvInfo) + public void GetEnvInfo([UseMsgId] string msgId, [UseData] FlowEnvInfo flowEnvInfo) { - remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.GetEnvInfo, flowEnvInfo); + remoteFlowEnvironment.TriggerSignal(msgId, flowEnvInfo); } - - /// /// 远程环境发来项目信息 /// + /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.GetProjectInfo)] - public void GetProjectInfo([UseMsgData] SereinProjectData sereinProjectData) + public void GetProjectInfo([UseMsgId] string msgId, [UseData] SereinProjectData sereinProjectData) { - remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.GetProjectInfo, sereinProjectData); + remoteFlowEnvironment.TriggerSignal(msgId, sereinProjectData); } [AutoSocketHandle(ThemeValue = EnvMsgTheme.SetNodeInterrupt)] - public void SetNodeInterrupt() + public void SetNodeInterrupt([UseMsgId] string msgId) { - remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.GetProjectInfo, null); + remoteFlowEnvironment.TriggerSignal(msgId, null); } [AutoSocketHandle(ThemeValue = EnvMsgTheme.AddInterruptExpression)] - public void AddInterruptExpression() + public void AddInterruptExpression([UseMsgId] string msgId) { - remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.AddInterruptExpression, null); + remoteFlowEnvironment.TriggerSignal(msgId, null); } [AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode)] - public void CreateNode([UseMsgData] NodeInfo nodeInfo) + public void CreateNode([UseMsgId] string msgId, [UseData] NodeInfo nodeInfo) { - remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.CreateNode, nodeInfo); + remoteFlowEnvironment.TriggerSignal(msgId, nodeInfo); } [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode)] - public void RemoveNode(bool state) + public void RemoveNode([UseMsgId] string msgId, bool state) { - remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.RemoveNode, state); + remoteFlowEnvironment.TriggerSignal(msgId, state); } [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)] - public void ConnectNode(bool state) + public void ConnectNode([UseMsgId] string msgId, bool state) { - remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.ConnectNode, state); + remoteFlowEnvironment.TriggerSignal(msgId, state); } [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)] - public void RemoveConnect(bool state) + public void RemoveConnect([UseMsgId] string msgId, bool state) { - remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.RemoveConnect, state); + remoteFlowEnvironment.TriggerSignal(msgId, state); } diff --git a/NodeFlow/Env/MsgControllerOfServer.cs b/NodeFlow/Env/MsgControllerOfServer.cs index c838bf6..5597b8b 100644 --- a/NodeFlow/Env/MsgControllerOfServer.cs +++ b/NodeFlow/Env/MsgControllerOfServer.cs @@ -1,22 +1,18 @@ using Newtonsoft.Json.Linq; -using Serein.Library.Api; using Serein.Library; +using Serein.Library.Api; using Serein.Library.Network.WebSocketCommunication; using Serein.Library.Network.WebSocketCommunication.Handle; using Serein.Library.Utils; -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Serein.NodeFlow.Env { /// /// 服务端的消息管理(用于处理客户端的请求) /// - [AutoSocketModule(ThemeKey = FlowEnvironment.ThemeKey, DataKey = FlowEnvironment.DataKey)] + [AutoSocketModule(ThemeKey = FlowEnvironment.ThemeKey, + DataKey = FlowEnvironment.DataKey, + MsgIdKey = FlowEnvironment.MsgIdKey)] public class MsgControllerOfServer : ISocketHandleModule { /// @@ -225,7 +221,7 @@ namespace Serein.NodeFlow.Env /// - /// 获取当前环境信息(远程连接) + /// 获取当前环境信息 /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.GetEnvInfo)] @@ -316,6 +312,7 @@ namespace Serein.NodeFlow.Env var nodeInfo = await environment.CreateNodeAsync(nodeControlType, position, mdInfo); // 监听到客户端创建节点的请求 return nodeInfo; } + /// /// 从远程环境移除节点 /// @@ -464,10 +461,6 @@ namespace Serein.NodeFlow.Env } - - - - } diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs index a15ee0a..9b963f7 100644 --- a/NodeFlow/Env/RemoteFlowEnvironment.cs +++ b/NodeFlow/Env/RemoteFlowEnvironment.cs @@ -3,8 +3,6 @@ using Serein.Library.Api; using Serein.Library.Utils; using Serein.NodeFlow.Tool; using System.Collections.Concurrent; -using System.Threading; -using System.Xml.Linq; namespace Serein.NodeFlow.Env { @@ -104,9 +102,8 @@ namespace Serein.NodeFlow.Env public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) { - Console.WriteLine("远程环境尚未实现的接口:LoadProject"); + //Console.WriteLine("远程环境尚未实现的接口:LoadProject"); - // dll面板 var libmds = flowEnvInfo.LibraryMds; foreach (var lib in libmds) @@ -143,8 +140,11 @@ namespace Serein.NodeFlow.Env else { - MethodDetails? methodDetails; - MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 尝试获取方法信息 + MethodDetails? methodDetails = null; + if (!string.IsNullOrEmpty(nodeInfo.MethodName)) + { + MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载远程环境时尝试获取方法信息 + } var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载远程项目时创建节点 nodeModel.LoadInfo(nodeInfo); // 创建节点model @@ -160,7 +160,6 @@ namespace Serein.NodeFlow.Env { regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids)); UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); - //OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)); } else { @@ -181,7 +180,6 @@ namespace Serein.NodeFlow.Env continue; } // 存在节点 - //OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)); UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid))); } } @@ -252,7 +250,10 @@ namespace Serein.NodeFlow.Env }); SetStartNode(projectData.StartNode); - OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); + UIContextOperation?.Invoke(() => + { + OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); + }); } private bool TryAddNode(NodeModelBase nodeModel) @@ -394,7 +395,10 @@ namespace Serein.NodeFlow.Env public void MoveNode(string nodeGuid, double x, double y) { - OnNodeMoved.Invoke(new NodeMovedEventArgs(nodeGuid, x, y)); + UIContextOperation.Invoke(() => + { + OnNodeMoved.Invoke(new NodeMovedEventArgs(nodeGuid, x, y)); + }); _ = msgClient.SendAsync(EnvMsgTheme.MoveNode, new { @@ -404,13 +408,14 @@ namespace Serein.NodeFlow.Env }); } + public void SetStartNode(string nodeGuid) { _ = msgClient.SendAsync(EnvMsgTheme.SetStartNode, new { nodeGuid }); - // UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid))); + //UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid,nodeGuid))); } public async Task ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType) @@ -440,13 +445,22 @@ namespace Serein.NodeFlow.Env mdInfo = methodDetailsInfo, }); - MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息 + MethodDetails? methodDetails = null; + if (!string.IsNullOrEmpty(nodeInfo.MethodName)) + { + MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载远程环境时尝试获取方法信息 + } + + //MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息 var nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 远程环境下加载节点 nodeModel.LoadInfo(nodeInfo); TryAddNode(nodeModel); // 通知UI更改 - OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position)); + UIContextOperation.Invoke(() => + { + OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position)); + }); return nodeInfo; } @@ -460,10 +474,13 @@ namespace Serein.NodeFlow.Env }); if (result) { - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + UIContextOperation.Invoke(() => + { + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, toNodeGuid, connectionType, NodeConnectChangeEventArgs.ConnectChangeType.Remote)); + }); } return result; } @@ -476,7 +493,10 @@ namespace Serein.NodeFlow.Env }); if (result) { - OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid)); + UIContextOperation.Invoke(() => + { + OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid)); + }); } else { diff --git a/NodeFlow/Model/CompositeConditionNode.cs b/NodeFlow/Model/CompositeConditionNode.cs index 327ebb4..38cdb85 100644 --- a/NodeFlow/Model/CompositeConditionNode.cs +++ b/NodeFlow/Model/CompositeConditionNode.cs @@ -4,20 +4,38 @@ using Serein.Library.Api; namespace Serein.NodeFlow.Model { + /// /// 组合条件节点(用于条件区域) /// - public class CompositeConditionNode : NodeModelBase + [NodeProperty(ValuePath = NodeValuePath.Node)] + public partial class CompositeConditionNode : NodeModelBase + { + /// + /// 条件节点集合 + /// + [PropertyInfo] + private List _conditionNodes; + } + + + /// + /// 组合条件节点(用于条件区域) + /// + public partial class CompositeConditionNode : NodeModelBase { public CompositeConditionNode(IFlowEnvironment environment):base(environment) { } - public List ConditionNodes { get; } = []; public void AddNode(SingleConditionNode node) { + if(ConditionNodes is null) + { + ConditionNodes = new List(); + } ConditionNodes.Add(node); MethodDetails ??= node.MethodDetails; } @@ -93,6 +111,7 @@ namespace Serein.NodeFlow.Model ParameterData = parameterData.ToArray(), ErrorNodes = errorNodes.ToArray(), ChildNodeGuids = ConditionNodes.Select(node => node.Guid).ToArray(), + Position = Position, }; } diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs index a4ad8f5..c9b9738 100644 --- a/NodeFlow/Model/SingleConditionNode.cs +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -1,35 +1,51 @@ using Serein.Library; using Serein.Library.Api; using Serein.Library.Utils.SereinExpression; +using System.ComponentModel; namespace Serein.NodeFlow.Model { /// /// 条件节点(用于条件控件) /// - public class SingleConditionNode : NodeModelBase + [NodeProperty(ValuePath = NodeValuePath.Node)] + public partial class SingleConditionNode : NodeModelBase { - public SingleConditionNode(IFlowEnvironment environment):base(environment) - { - - } /// /// 是否为自定义参数 /// - public bool IsCustomData { get; set; } + [PropertyInfo(IsNotification = true)] + private bool _isCustomData; + /// /// 自定义参数值 /// - public object? CustomData { get; set; } + [PropertyInfo(IsNotification = true)] + + private object? _customData; /// /// 条件表达式 /// + [PropertyInfo(IsNotification = true)] + private string _expression; - public string Expression { get; set; } + } + public partial class SingleConditionNode : NodeModelBase + { + public SingleConditionNode(IFlowEnvironment environment):base(environment) + { + this.IsCustomData = false; + this.CustomData = null; + this.Expression = "PASS"; + } - //public override object? Executing(IDynamicContext context) + /// + /// 重写节点的方法执行 + /// + /// + /// public override Task ExecutingAsync(IDynamicContext context) { // 接收上一节点参数or自定义参数内容 @@ -94,46 +110,20 @@ namespace Serein.NodeFlow.Model public override NodeModelBase LoadInfo(NodeInfo nodeInfo) { var node = this; - if (node != null) + node.Guid = nodeInfo.Guid; + this.Position = nodeInfo.Position;// 加载位置信息 + for (int i = 0; i < nodeInfo.ParameterData.Length; i++) { - node.Guid = nodeInfo.Guid; - for (int i = 0; i < nodeInfo.ParameterData.Length; i++) - { - Parameterdata? pd = nodeInfo.ParameterData[i]; - node.IsCustomData = pd.State; - node.CustomData = pd.Value; - node.Expression = pd.Expression; + Parameterdata? pd = nodeInfo.ParameterData[i]; + node.IsCustomData = pd.State; + node.CustomData = pd.Value; + node.Expression = pd.Expression; - } } return this; } - //public override void Execute(DynamicContext context) - //{ - // CurrentState = Judge(context, base.MethodDetails); - //} - - //private bool Judge(DynamicContext context, MethodDetails md) - //{ - // try - // { - // if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del)) - // { - // object[] parameters = GetParameters(context, md); - // var temp = del.DynamicInvoke(parameters); - // //context.GetData(GetDyPreviousKey()); - // return (bool)temp; - // } - // } - // catch (Exception ex) - // { - // Debug.Write(ex.Message); - // } - // return false; - //} - - + } diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/SingleExpOpNode.cs index d799c42..0af3db8 100644 --- a/NodeFlow/Model/SingleExpOpNode.cs +++ b/NodeFlow/Model/SingleExpOpNode.cs @@ -1,23 +1,32 @@ using Serein.Library; using Serein.Library.Api; using Serein.Library.Utils.SereinExpression; +using System.Reactive; namespace Serein.NodeFlow.Model { /// /// Expression Operation - 表达式操作 /// - public class SingleExpOpNode : NodeModelBase + [NodeProperty(ValuePath = NodeValuePath.Node)] + public partial class SingleExpOpNode : NodeModelBase { - public SingleExpOpNode(IFlowEnvironment environment) : base(environment) - { - - } /// /// 表达式 /// - public string Expression { get; set; } + [PropertyInfo(IsNotification = true)] + private string _expression; + } + + + + public partial class SingleExpOpNode : NodeModelBase + { + public SingleExpOpNode(IFlowEnvironment environment) : base(environment) + { + + } //public override async Task Executing(IDynamicContext context) public override Task ExecutingAsync(IDynamicContext context) @@ -31,7 +40,7 @@ namespace Serein.NodeFlow.Model object? result = null; if (isChange) { - result = newData; + result = newData; } else { @@ -52,7 +61,7 @@ namespace Serein.NodeFlow.Model public override Parameterdata[] GetParameterdatas() { - return [new Parameterdata{ Expression = Expression}]; + return [new Parameterdata { Expression = Expression }]; } @@ -60,13 +69,11 @@ namespace Serein.NodeFlow.Model public override NodeModelBase LoadInfo(NodeInfo nodeInfo) { var node = this; - if (node != null) + this.Position = nodeInfo.Position;// 加载位置信息 + node.Guid = nodeInfo.Guid; + for (int i = 0; i < nodeInfo.ParameterData.Length; i++) { - node.Guid = nodeInfo.Guid; - for (int i = 0; i < nodeInfo.ParameterData.Length; i++) - { - node.Expression = nodeInfo.ParameterData[i].Expression; - } + node.Expression = nodeInfo.ParameterData[i].Expression; } return this; } diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 881f2d1..51e7c4b 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -1,7 +1,7 @@  - 1.0.16 + 1.0.17 net8.0 enable enable @@ -16,7 +16,10 @@ MIT true True - + + true + true + .\obj\g @@ -63,6 +66,8 @@ + + diff --git a/NodeFlow/Tool/ObjDynamicCreateHelper.cs b/NodeFlow/Tool/ObjDynamicCreateHelper.cs index d269cf0..ad1f9bc 100644 --- a/NodeFlow/Tool/ObjDynamicCreateHelper.cs +++ b/NodeFlow/Tool/ObjDynamicCreateHelper.cs @@ -6,7 +6,8 @@ namespace Serein.NodeFlow.Tool { public class ObjDynamicCreateHelper - {// 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式) + { + // 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式) static Dictionary typeCache = new Dictionary(); public static object Resolve(Dictionary properties, string typeName) diff --git a/Serein.Library.MyGenerator/Attribute.cs b/Serein.Library.MyGenerator/Attribute.cs new file mode 100644 index 0000000..6f9bd35 --- /dev/null +++ b/Serein.Library.MyGenerator/Attribute.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +namespace Serein.Library +{ + + /// + /// 通过枚举来区分该怎么生成代码 + /// + public enum NodeValuePath + { + /// + /// 默认值 + /// + None, + /// + /// 节点本身 + /// + Node, + /// + /// 节点对应的方法 + /// + Method, + /// + /// 节点方法对应的入参 + /// + Parameter, + /// + /// 节点的调试设置 + /// + DebugSetting, + + } + + + /// + /// 标识一个类中的某些字段需要生成相应代码 + /// + [AttributeUsage(AttributeTargets.Class, Inherited = true)] + public sealed class NodePropertyAttribute : Attribute + { + /// + /// 属性路径 + /// CustomNode : 自定义节点 + /// + public NodeValuePath ValuePath = NodeValuePath.None; + } + + /// + /// 自动生成环境的属性 + /// + [AttributeUsage(AttributeTargets.Field, Inherited = true)] + public sealed class PropertyInfoAttribute : Attribute + { + /// + /// 是否通知UI + /// + public bool IsNotification = false; + /// + /// 是否使用Console.WriteLine打印 + /// + public bool IsPrint = false; + /// + /// 是否禁止参数进行修改(初始化后不能再通过 Setter 修改) + /// + public bool IsProtection = false; + } + +} diff --git a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs index 0e4cbbf..860ff5b 100644 --- a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs +++ b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; @@ -20,6 +21,8 @@ namespace Serein.Library.NodeGenerator [Generator] public class MyPropertyGenerator : IIncrementalGenerator { + internal static NodePropertyAttribute NodeProperty = new NodePropertyAttribute(); + internal static PropertyInfoAttribute PropertyInfo = new PropertyInfoAttribute(); /// /// 初始化生成器,定义需要执行的生成逻辑。 @@ -48,7 +51,7 @@ namespace Serein.Library.NodeGenerator // 检查类的特性列表,看看是否存在 MyClassAttribute if (classDeclaration.AttributeLists .SelectMany(attrList => attrList.Attributes) - .Any(attr => semanticModel.GetSymbolInfo(attr).Symbol?.ContainingType.Name == "AutoPropertyAttribute")) + .Any(attr => semanticModel.GetSymbolInfo(attr).Symbol?.ContainingType.Name == nameof(NodePropertyAttribute))) { var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration); // 获取类的符号 var classInfo = classSymbol.BuildCacheOfClass(); @@ -127,7 +130,7 @@ namespace Serein.Library.NodeGenerator sb.AppendLine($" public partial class {className} : System.ComponentModel.INotifyPropertyChanged"); sb.AppendLine(" {"); - var path = classInfo["AutoPropertyAttribute"]["ValuePath"]; + var path = classInfo[nameof(NodePropertyAttribute)]["ValuePath"]; // @@ -156,10 +159,10 @@ namespace Serein.Library.NodeGenerator var propertyName = field.ToPropertyName(); // 转为合适的属性名称 var attributeInfo = fieldKV.Value; // 缓存的特性信息 + var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), "true"); // 是否为保护字段 - var isProtection = attributeInfo.Search("PropertyInfo", "IsProtection", "true"); // 是否为保护字段 - - + + // 生成 getter / setter sb.AppendLine(leadingTrivia); sb.AppendLine($" public {fieldType} {propertyName}"); @@ -169,34 +172,40 @@ namespace Serein.Library.NodeGenerator sb.AppendLine(" {"); sb.AppendLine($" if ({fieldName} {(isProtection ? "== default" : "!= value")})"); // 非保护的Setter sb.AppendLine(" {"); - if (attributeInfo.Search("PropertyInfo", "IsPrint", "true")) // 是否打印 + if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsPrint), "true")) // 是否打印 { sb.AddCode(5, $"Console.WriteLine({fieldName});"); } - if (attributeInfo.Search("PropertyInfo", "IsNotification", "true")) // 是否通知 + if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsNotification), "true")) // 是否通知 { - if (classInfo.ExitsPath("NodeModelBase")) + if (classInfo.ExitsPath(nameof(NodeValuePath.Node))) // 节点 or 自定义节点 { - sb.AddCode(5, $"this.env?.NotificationNodeValueChangeAsync(this.Guid, .nameof({propertyName}), value);"); + sb.AddCode(5, $"((NodeModelBase)this).Env?.NotificationNodeValueChangeAsync(this.Guid, nameof({propertyName}), value); // 通知远程环境属性发生改变了"); } - else if (classInfo.ExitsPath("MethodDetails")) + else if (classInfo.ExitsPath(nameof(NodeValuePath.Method))) // 节点方法详情 { - sb.AddCode(5, $"nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"MethodDetails.\"+nameof({propertyName}), value);"); + sb.AddCode(5, $"nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"MethodDetails.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了"); } - else if (classInfo.ExitsPath("ParameterDetails")) + else if (classInfo.ExitsPath(nameof(NodeValuePath.Parameter))) // 节点方法入参参数描述 { - sb.AddCode(5, "nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"MethodDetails.ParameterDetailss[\"+$\"{Index}\"+\"]." + $"\"+nameof({propertyName}),value);"); + sb.AddCode(5, "nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"MethodDetails.ParameterDetailss[\"+$\"{Index}\"+\"]." + $"\"+nameof({propertyName}),value); // 通知远程环境属性发生改变了"); } - else if (classInfo.ExitsPath("NodeDebugSetting")) + else if (classInfo.ExitsPath(nameof(NodeValuePath.DebugSetting))) // 节点的调试信息 { - sb.AddCode(5, $"nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"DebugSetting.\"+nameof({propertyName}), value);"); + sb.AddCode(5, $"nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"DebugSetting.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了"); } } - sb.AppendLine($" {fieldName} = value;"); - sb.AppendLine($" OnPropertyChanged(); // 先更改属性,然后通知属性发生改变了"); + sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); // 通知UI属性发生改变了"); + //sb.AppendLine($" {fieldName} = value;"); + //sb.AppendLine($" OnPropertyChanged(); // 通知UI属性发生改变了"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine(" }"); // 属性的结尾大括号 + //if (!isProtection && field.TryGetDefaultValue(out var defaultValue)) + //{ + + // sb.AppendLine($" }} = {defaultValue}"); + //} } @@ -206,19 +215,29 @@ namespace Serein.Library.NodeGenerator sb.AppendLine(" /// "); sb.AppendLine(" public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;"); - sb.AppendLine(" /// "); - sb.AppendLine(" /// 略"); - sb.AppendLine(" /// 此方法为自动生成"); + sb.AppendLine(" protected void SetProperty(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) "); + sb.AppendLine(" { "); + sb.AppendLine(" if (Equals(storage, value)) "); + sb.AppendLine(" { "); + sb.AppendLine(" return; "); + sb.AppendLine(" } "); + sb.AppendLine(" "); + sb.AppendLine(" storage = value; "); + sb.AppendLine(" OnPropertyChanged(propertyName); "); + sb.AppendLine(" } "); + + sb.AppendLine(" /// "); + sb.AppendLine(" /// 略 "); + sb.AppendLine(" /// 此方法为自动生成 "); sb.AppendLine(" /// "); - sb.AppendLine(" /// "); - sb.AppendLine(" "); - sb.AppendLine(" protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)"); + sb.AppendLine(" /// "); + sb.AppendLine(" "); + sb.AppendLine(" protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) "); sb.AppendLine(" {"); //sb.AppendLine(" Console.WriteLine(\"测试:\"+ propertyName);"); - sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));"); + sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); "); sb.AppendLine(" }"); - } finally { @@ -347,7 +366,12 @@ namespace Serein.Library.NodeGenerator { var key = cata.Key; var value = cata.Value.Value; - if (!attributeInfo.ContainsKey(key)) + if (nameof(NodePropertyAttribute).Equals(attributeName)) + { + string literal = Enum.GetName(typeof(NodeValuePath), cata.Value.Value); + attributeInfo.Add(key, literal); + } + else { attributeInfo.Add(key, value); } @@ -374,6 +398,29 @@ namespace Serein.Library.NodeGenerator var propertyName = fieldName.StartsWith("_") ? char.ToUpper(fieldName[1]) + fieldName.Substring(2) : char.ToUpper(fieldName[0]) + fieldName.Substring(1); // 创建属性名称 return propertyName; } + + /// + /// 判断字段是否有默认值 + /// + /// + /// + /// + public static bool TryGetDefaultValue(this FieldDeclarationSyntax field ,out string defaultValue) + { + if (field.Declaration.Variables.First().Initializer != null) + { + defaultValue = field.Declaration.Variables.First().Initializer.Value.ToString(); + return true; + } + else + { + defaultValue = null; + return false; + } + } + + + /// /// 判断字段是否为只读 /// @@ -500,14 +547,13 @@ namespace Serein.Library.NodeGenerator public static bool ExitsPath(this Dictionary> classInfo, string valuePath) { - // var path = classInfo["AutoPropertyAttribute"]["ValuePath"]; - - if (!classInfo.TryGetValue("AutoPropertyAttribute", out var keyValuePairs)) + + if (!classInfo.TryGetValue(nameof(NodePropertyAttribute), out var keyValuePairs)) { return false; } - if (!keyValuePairs.TryGetValue("ValuePath", out var value)) + if (!keyValuePairs.TryGetValue(nameof(MyPropertyGenerator.NodeProperty.ValuePath), out var value)) { return false; } diff --git a/Serein.Library.MyGenerator/Properties/launchSettings.json b/Serein.Library.MyGenerator/Properties/launchSettings.json index 93ccb52..9e26dfe 100644 --- a/Serein.Library.MyGenerator/Properties/launchSettings.json +++ b/Serein.Library.MyGenerator/Properties/launchSettings.json @@ -1,7 +1 @@ -{ - "profiles": { - "配置文件 2": { - "commandName": "DebugRoslynComponent" - } - } -} \ No newline at end of file +{} \ No newline at end of file diff --git a/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj index d648318..125db7c 100644 --- a/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj +++ b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj @@ -3,6 +3,7 @@ netstandard2.0 true + D:\Project\C#\DynamicControl\SereinFlow\.Output True SereinFow @@ -12,7 +13,6 @@ MIT True true - diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index 2e8b8b3..6903f23 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -10,7 +10,9 @@ namespace Serein.Workbench /// public partial class App : Application { +#if DEBUG +#endif public static SereinProjectData? FlowProjectData { get; set; } public static string FileDataPath { get; set; } = ""; @@ -66,7 +68,7 @@ namespace Serein.Workbench } #if DEBUG - else if(1 == 1) + else if(1 == 11) { //string filePath = @"F:\临时\project\new project.dnf"; diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index e956b7a..759c7e5 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -461,6 +461,11 @@ namespace Serein.Workbench return; } + if(nodeModelBase is null) + { + Console.WriteLine("OnNodeCreateEvent事件接收到意外的返回值"); + return; + } // MethodDetails methodDetailss = eventArgs.MethodDetailss; PositionOfUI position = eventArgs.Position; @@ -577,15 +582,15 @@ namespace Serein.Workbench { string nodeGuid = eventArgs.NodeGuid; if (!TryGetControl(nodeGuid, out var nodeControl)) return; - if (eventArgs.Class == InterruptClass.None) - { - nodeControl.ViewModel.IsInterrupt = false; - } - else - { - nodeControl.ViewModel.IsInterrupt = true; - } + //if (eventArgs.Class == InterruptClass.None) + //{ + // nodeControl.ViewModel.IsInterrupt = false; + //} + //else + //{ + // nodeControl.ViewModel.IsInterrupt = true; + //} foreach (var menuItem in nodeControl.ContextMenu.Items) { @@ -1203,35 +1208,48 @@ namespace Serein.Workbench /// /// /// - private async void FlowChartCanvas_Drop(object sender, DragEventArgs e) + private void FlowChartCanvas_Drop(object sender, DragEventArgs e) { - var canvasDropPosition = e.GetPosition(FlowChartCanvas); // 更新画布落点 - PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y); - if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas)) + try { - if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeData nodeData) + var canvasDropPosition = e.GetPosition(FlowChartCanvas); // 更新画布落点 + PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y); + if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas)) { - await EnvDecorator.CreateNodeAsync(nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象 - } - } - else if (e.Data.GetDataPresent(MouseNodeType.CreateBaseNodeInCanvas)) - { - if (e.Data.GetData(MouseNodeType.CreateBaseNodeInCanvas) is Type droppedType) - { - NodeControlType nodeControlType = droppedType switch + if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeData nodeData) { - Type when typeof(ConditionRegionControl).IsAssignableFrom(droppedType) => NodeControlType.ConditionRegion, // 条件区域 - Type when typeof(ConditionNodeControl).IsAssignableFrom(droppedType) => NodeControlType.ExpCondition, - Type when typeof(ExpOpNodeControl).IsAssignableFrom(droppedType) => NodeControlType.ExpOp, - _ => NodeControlType.None, - }; - if(nodeControlType != NodeControlType.None) - { - await EnvDecorator.CreateNodeAsync(nodeControlType, position); // 创建基础节点对象 + Task.Run(async () => + { + await EnvDecorator.CreateNodeAsync(nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象 + }); } } + else if (e.Data.GetDataPresent(MouseNodeType.CreateBaseNodeInCanvas)) + { + if (e.Data.GetData(MouseNodeType.CreateBaseNodeInCanvas) is Type droppedType) + { + NodeControlType nodeControlType = droppedType switch + { + Type when typeof(ConditionRegionControl).IsAssignableFrom(droppedType) => NodeControlType.ConditionRegion, // 条件区域 + Type when typeof(ConditionNodeControl).IsAssignableFrom(droppedType) => NodeControlType.ExpCondition, + Type when typeof(ExpOpNodeControl).IsAssignableFrom(droppedType) => NodeControlType.ExpOp, + _ => NodeControlType.None, + }; + if (nodeControlType != NodeControlType.None) + { + Task.Run(async () => + { + await EnvDecorator.CreateNodeAsync(nodeControlType, position); // 创建基础节点对象 + }); + } + } + } + e.Handled = true; + } + catch (Exception ex) + { + Console.WriteLine(ex); } - e.Handled = true; } /// @@ -1897,7 +1915,7 @@ namespace Serein.Workbench //Console.WriteLine($"一共选取了{selectNodeControls.Count}个控件"); foreach (var node in selectNodeControls) { - node.ViewModel.IsSelect =true; + //node.ViewModel.IsSelect =true; // node.ViewModel.CancelSelect(); node.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFC700")); node.BorderThickness = new Thickness(4); @@ -1908,7 +1926,7 @@ namespace Serein.Workbench IsSelectControl = false; foreach (var nodeControl in selectNodeControls) { - nodeControl.ViewModel.IsSelect = false; + //nodeControl.ViewModel.IsSelect = false; nodeControl.BorderBrush = Brushes.Black; nodeControl.BorderThickness = new Thickness(0); if (nodeControl.ViewModel.NodeModel.IsStart) diff --git a/WorkBench/MainWindowViewModel.cs b/WorkBench/MainWindowViewModel.cs index e9565db..3a2d45e 100644 --- a/WorkBench/MainWindowViewModel.cs +++ b/WorkBench/MainWindowViewModel.cs @@ -40,6 +40,7 @@ namespace Serein.Workbench else { FlowEnvironment = new FlowEnvironmentDecorator(uIContextOperation); + //_ = FlowEnvironment.StartRemoteServerAsync(); this.window = window; } } diff --git a/WorkBench/Node/NodeControlViewModelBase.cs b/WorkBench/Node/NodeControlViewModelBase.cs index 00890e3..56af0d8 100644 --- a/WorkBench/Node/NodeControlViewModelBase.cs +++ b/WorkBench/Node/NodeControlViewModelBase.cs @@ -9,83 +9,97 @@ namespace Serein.Workbench.Node.ViewModel public NodeControlViewModelBase(NodeModelBase nodeModel) { NodeModel = nodeModel; - MethodDetails = NodeModel.MethodDetails; // 订阅来自 NodeModel 的通知事件 } + + private NodeModelBase _nodeModelBase; /// /// 对应的节点实体类 /// - internal NodeModelBase NodeModel { get; } - - - private bool isSelect; - /// - /// 表示节点控件是否被选中 - /// - internal bool IsSelect + public NodeModelBase NodeModel { - get => isSelect; - set + get => _nodeModelBase; set { - isSelect = value; - OnPropertyChanged(); + if (value != null) + { + _nodeModelBase = value; + OnPropertyChanged(); + } } } + + //private bool isSelect; + ///// + ///// 表示节点控件是否被选中 + ///// + //internal bool IsSelect + //{ + // get => isSelect; + // set + // { + // isSelect = value; + // OnPropertyChanged(); + // } + //} + + + + //private bool isInterrupt; + /////// + /////// 控制中断状态的视觉效果 + /////// + //public bool IsInterrupt + //{ + // get => NodeModel.DebugSetting.IsInterrupt; + // set + // { + // NodeModel.DebugSetting.IsInterrupt = value; + // OnPropertyChanged(); + // } + //} + + public event PropertyChangedEventHandler? PropertyChanged; + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + //Console.WriteLine(propertyName); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + /// /// 使节点获得中断能力(以及是否启用节点) /// - public NodeDebugSetting DebugSetting - { - get => NodeModel.DebugSetting; - set - { - if (value != null) - { - NodeModel.DebugSetting = value; - OnPropertyChanged(); - } - } - } + //public NodeDebugSetting DebugSetting + //{ + // get => Node.DebugSetting; + // set + // { + // if (value != null) + // { + // Node.DebugSetting = value; + // OnPropertyChanged(); + // } + // } + //} /// /// 使节点能够表达方法信息 /// - public MethodDetails MethodDetails - { - get => NodeModel.MethodDetails; - set - { - if(value != null) - { - NodeModel.MethodDetails = value; - OnPropertyChanged(); - } - } - } + //public MethodDetails MethodDetails + //{ + // get => Node.MethodDetails; + // set + // { + // if(value != null) + // { + // Node.MethodDetails = value; + // OnPropertyChanged(); + // } + // } + //} - private bool isInterrupt; - /// - /// 控制中断状态的视觉效果 - /// - public bool IsInterrupt - { - get => isInterrupt; - set - { - isInterrupt = value; - OnPropertyChanged(); - } - } - - public event PropertyChangedEventHandler? PropertyChanged; - protected void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - } } diff --git a/WorkBench/Node/View/ActionNodeControl.xaml b/WorkBench/Node/View/ActionNodeControl.xaml index f6762b4..3cce56b 100644 --- a/WorkBench/Node/View/ActionNodeControl.xaml +++ b/WorkBench/Node/View/ActionNodeControl.xaml @@ -21,17 +21,20 @@ - + - + + + +