diff --git a/FlowStartTool/FlowEnv.cs b/FlowStartTool/FlowEnv.cs index 7faf082..c8667e2 100644 --- a/FlowStartTool/FlowEnv.cs +++ b/FlowStartTool/FlowEnv.cs @@ -31,11 +31,11 @@ namespace Serein.FlowStartTool //} //else //{ - // env = new FlowEnvironmentDecorator(uIContextOperation); + // env = new FlowEnvironment(uIContextOperation); // this.window = window; //} - Env = new FlowEnvironmentDecorator(); + Env = new FlowEnvironment(); Env.SetUIContextOperation(uIContextOperation); Env.LoadProject(new FlowEnvInfo { Project = flowProjectData }, fileDataPath); // 加载项目 diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index d095342..e608640 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -739,6 +739,7 @@ namespace Serein.Library.Api } + /// /// 运行环境 /// @@ -758,7 +759,6 @@ namespace Serein.Library.Api /// string EnvName { get; } - /// /// 项目文件位置 /// @@ -785,11 +785,6 @@ namespace Serein.Library.Api /// RunState FlowState { get; set; } - /// - /// 全局触发器运行状态 - /// - //RunState FlipFlopState { get; set; } - /// /// 表示当前环境 /// @@ -811,8 +806,9 @@ namespace Serein.Library.Api /// /// 输出信息 /// - /// - /// + /// 消息 + /// 输出类型 + /// 输出级别 void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial); /// @@ -840,7 +836,6 @@ namespace Serein.Library.Api /// Task LoadNodeInfosAsync(List nodeInfos); - #endregion #region 远程相关 @@ -906,6 +901,7 @@ namespace Serein.Library.Api /// /// 移动了某个节点(远程插件使用) /// + /// 所在画布 /// /// /// @@ -914,6 +910,7 @@ namespace Serein.Library.Api /// /// 设置流程起点节点 /// + /// 所在画布 /// 尝试设置为起始节点的节点Guid /// 被设置为起始节点的Guid Task SetStartNodeAsync(string canvasGuid, string nodeGuid); @@ -921,6 +918,7 @@ namespace Serein.Library.Api /// /// 在两个节点之间创建连接关系 /// + /// 所在画布 /// 起始节点Guid /// 目标节点Guid /// 起始节点控制点 @@ -936,6 +934,7 @@ namespace Serein.Library.Api /// /// 在两个节点之间创建连接关系 /// + /// 所在画布 /// 起始节点Guid /// 目标节点Guid /// 起始节点控制点 @@ -950,26 +949,32 @@ namespace Serein.Library.Api ConnectionArgSourceType argSourceType, int argIndex); - + /// /// 创建节点 /// + /// 所在画布 /// 控件类型 /// 节点在画布上的位置( /// 节点绑定的方法说明 Task CreateNodeAsync(string canvasGuid, NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null); + /// - /// 将节点放置在容器中 + /// 将节点放置在容器中 /// + /// 所在画布 + /// 需要放置的节点Guid + /// 存放节点的容器Guid /// Task PlaceNodeToContainerAsync(string canvasGuid, string nodeGuid, string containerNodeGuid); /// - /// 将节点从容器中脱离 + /// 将节点放置在容器中 /// - /// + /// 所在画布 + /// 需要取出的节点Guid Task TakeOutNodeToContainerAsync(string canvasGuid, string nodeGuid); @@ -984,7 +989,8 @@ namespace Serein.Library.Api /// /// 移除两个节点之间的方法调用关系 - /// + /// + /// 所在画布 /// 起始节点 /// 目标节点 /// 连接类型 @@ -993,15 +999,16 @@ namespace Serein.Library.Api /// /// 移除连接节点之间参数传递的关系 /// + /// 所在画布 /// 起始节点Guid /// 目标节点Guid /// 连接到第几个参数 - /// 参数来源类型 Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex); /// /// 移除节点/区域/基础控件 /// + /// 所在画布 /// 待移除的节点Guid Task RemoveNodeAsync(string canvasGuid, string nodeGuid); @@ -1089,11 +1096,21 @@ namespace Serein.Library.Api bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del); /// - /// 提供设置UI上下文的能力 + /// 提供设置UI上下文的能力 + /// 提供设置UI上下文的能力,在WinForm/WPF项目中,在UI线程外对UI元素的修改将会导致异常 + /// 需要你提供 /// /// void SetUIContextOperation(UIContextOperation uiContextOperation); + /// + /// 需要你提供一个由你实现的ISereinIOC接口实现类 + /// 当你将流程运行环境集成在你的项目时,并希望流程运行时使用你提供的对象,而非自动创建 + /// 就需要你调用这个方法,用来替换运行环境的IOC容器 + /// + /// + void UseExternalIOC(ISereinIOC ioc); + /// /// 开始运行流程 /// @@ -1101,7 +1118,6 @@ namespace Serein.Library.Api /// Task StartFlowAsync(string[] canvasGuids); - /// /// 从选定的节点开始运行 /// @@ -1142,14 +1158,6 @@ namespace Serein.Library.Api /// 中断类型。0主动监视,1表达式 void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type); - /// - /// 立刻调用某个节点,并获取其返回值 - /// - /// 调用时的上下文 - /// 节点Guid - /// - // Task InvokeNodeAsync(IDynamicContext context, string nodeGuid); - #endregion #region 类库依赖相关 diff --git a/Library/Api/ISereinIoc.cs b/Library/Api/ISereinIoc.cs index 447c320..401c0f4 100644 --- a/Library/Api/ISereinIoc.cs +++ b/Library/Api/ISereinIoc.cs @@ -32,6 +32,13 @@ namespace Serein.Library.Api /// ISereinIOC Register(Type type, Func getInstance); + /// + /// 通过泛型的方式注册实例 + /// + /// 实例类型 + /// + ISereinIOC Register(); + /// /// 通过泛型的方式注册实例 /// @@ -84,6 +91,19 @@ namespace Serein.Library.Api /// /// ISereinIOC Build(); + + + /// + /// 从容器中获取某个类型的实例进行运行 + /// + /// + /// + /// + ISereinIOC Run(Action action); + ISereinIOC Run(Action action); + ISereinIOC Run(Action action); + ISereinIOC Run(Action action); + } } diff --git a/Library/FlowNode/DelegateDetails.cs b/Library/FlowNode/DelegateDetails.cs index 12d732c..0b9841f 100644 --- a/Library/FlowNode/DelegateDetails.cs +++ b/Library/FlowNode/DelegateDetails.cs @@ -25,9 +25,39 @@ namespace Serein.Library var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var emitDelegate); _emitMethodInfo = emitMethodType; _emitDelegate = emitDelegate; + + SetFunc(); } + private void SetFunc() + { + if (_emitDelegate is Func> hasResultTask) + { + this.hasResultTask = hasResultTask; + funcType = 2; + } + else if (_emitDelegate is Func task) + { + this.task = task; + funcType = 1; + } + else if (_emitDelegate is Func func) + { + this.func = func; + funcType = 0; + } + else + { + throw new NotSupportedException(); + } + } + + + public Func> hasResultTask; + public Func task; + public Func func; + /*/// /// 更新委托方法 @@ -42,6 +72,12 @@ namespace Serein.Library private Delegate _emitDelegate; private EmitMethodInfo _emitMethodInfo; + /// + /// 0是普通 + /// 1是异步无返回值 + /// 2是异步有返回值 + /// + private int funcType; /// /// 该Emit委托的相应信息 @@ -92,34 +128,25 @@ namespace Serein.Library instance = null; } object result = null; - if (_emitDelegate is Func> hasResultTask) - { - result = await hasResultTask(instance, args); - } - else if (_emitDelegate is Func task) - { - await task.Invoke(instance, args); - } - else if (_emitDelegate is Func func) + if(funcType == 0) { result = func.Invoke(instance, args); + + } + else if (funcType == 2) + { + result = await hasResultTask(instance, args); + } + else if (funcType == 1) + { + await task(instance, args); + result = null; } else { throw new NotImplementedException("创建了非预期委托(应该不会出现)"); } - - // return result; - - //try - //{ - - //} - //catch - //{ - // throw; - //} } } } diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs index c9a2c83..3fa7b45 100644 --- a/Library/Utils/SereinIoc.cs +++ b/Library/Utils/SereinIoc.cs @@ -12,7 +12,7 @@ namespace Serein.Library.Utils { /// - /// IOC管理容器 + /// 一个轻量级的IOC容器 /// public class SereinIOC : ISereinIOC { @@ -79,6 +79,20 @@ namespace Serein.Library.Utils } + /// + /// 向容器注册类型,并指定其实例成员 + /// + /// 需要注册的类型 + /// 获取实例的回调函数 + /// + public ISereinIOC Register() + { + var type = typeof(T); + RegisterType(type.FullName, type); + return this; + } + + /// /// 向容器注册类型,并指定其实例成员 /// @@ -485,7 +499,7 @@ namespace Serein.Library.Utils continue; } _dependencies[typeName] = value; - OnIOCMembersChanged.Invoke(new IOCMembersChangedEventArgs(typeName, value)); + OnIOCMembersChanged?.Invoke(new IOCMembersChangedEventArgs(typeName, value)); } _typeMappings.Clear(); return this; @@ -577,85 +591,45 @@ namespace Serein.Library.Utils return isPass; } - - private void Run(Action action) + #endregion + + #region 运行 + + public ISereinIOC Run(Action action) { var service = Get(); action(service); + return this; } - private void Run(Action action) + public ISereinIOC Run(Action action) { var service1 = Get(); var service2 = Get(); - action(service1, service2); + action(service1, service2); + return this; } - private void Run(Action action) + public ISereinIOC Run(Action action) { var service1 = Get(); var service2 = Get(); var service3 = Get(); action(service1, service2, service3); + return this; } - private void Run(Action action) + public ISereinIOC Run(Action action) { var service1 = Get(); var service2 = Get(); var service3 = Get(); var service4 = Get(); action(service1, service2, service3, service4); + return this; } - private void Run(Action action) - { - var service1 = Get(); - var service2 = Get(); - var service3 = Get(); - var service4 = Get(); - var service5 = Get(); - action(service1, service2, service3, service4, service5); - } - - private void Run(Action action) - { - var service1 = Get(); - var service2 = Get(); - var service3 = Get(); - var service4 = Get(); - var service5 = Get(); - var service6 = Get(); - action(service1, service2, service3, service4, service5, service6); - } - - private void Run(Action action) - { - var service1 = Get(); - var service2 = Get(); - var service3 = Get(); - var service4 = Get(); - var service5 = Get(); - var service6 = Get(); - var service7 = Get(); - action(service1, service2, service3, service4, service5, service6, service7); - } - - private void Run(Action action) - { - var service1 = Get(); - var service2 = Get(); - var service3 = Get(); - var service4 = Get(); - var service5 = Get(); - var service6 = Get(); - var service7 = Get(); - var service8 = Get(); - action(service1, service2, service3, service4, service5, service6, service7, service8); - } - - #endregion } diff --git a/Library/Utils/UIContextOperation.cs b/Library/Utils/UIContextOperation.cs index 3771379..be0355a 100644 --- a/Library/Utils/UIContextOperation.cs +++ b/Library/Utils/UIContextOperation.cs @@ -45,9 +45,9 @@ namespace Serein.Library.Utils } /// - /// 传入UI线程上下文 + /// 传入获取UI线程上下文的闭包创建 /// - /// 线程上下文 + /// 获取线程上下文的闭包函数 public UIContextOperation(Func getUiContext) { this.getUiContext = getUiContext; diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 7627dd9..d4958e0 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -2,325 +2,423 @@ using Serein.Library.Api; using Serein.Library.FlowNode; using Serein.Library.Utils; -using Serein.Library.Utils.SereinExpression; -using Serein.NodeFlow.Model; using Serein.NodeFlow.Tool; -using System; -using System.Collections.Specialized; -using System.Diagnostics; -using System.Net.Http.Headers; -using System.Net.Mime; -using System.Reactive; using System.Reflection; -using System.Text; namespace Serein.NodeFlow.Env { - - /// - /// 运行环境 + /// 流程运行环境 /// - public class FlowEnvironment : IFlowEnvironment, IFlowEnvironmentEvent , ISereinIOC + public class FlowEnvironment : IFlowEnvironment, IFlowEnvironmentEvent { - /// - /// 节点的命名空间 - /// - 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"; - - /// - /// 流程运行环境 - /// public FlowEnvironment() { - this.sereinIOC = new SereinIOC(); - this.IsGlobalInterrupt = false; - this.flowTaskManagement = null; - this.sereinIOC.OnIOCMembersChanged += e => + flowEnvironment = new LocalFlowEnvironment(); + // 默认使用本地环境 + currentFlowEnvironment = flowEnvironment; + currentFlowEnvironmentEvent = flowEnvironment; + SereinEnv.SetEnv(currentFlowEnvironment); + } + + /// + /// 本地环境 + /// + private readonly LocalFlowEnvironment flowEnvironment; + + /// + /// 远程环境 + /// + private RemoteFlowEnvironment remoteFlowEnvironment; + + /// + /// 本地环境事件 + /// + private readonly IFlowEnvironmentEvent flowEnvironmentEvent; + + /// + /// 远程环境事件 + /// + private IFlowEnvironmentEvent remoteFlowEnvironmentEvent; + + + /// + /// 管理当前环境 + /// + + private IFlowEnvironment currentFlowEnvironment; + + /// + /// 管理当前环境事件 + /// + private IFlowEnvironmentEvent currentFlowEnvironmentEvent; + + private int _loadingProjectFlag = 0; // 使用原子自增代替锁 + + + /// + /// 传入false时,将停止数据通知。传入true时, + /// + /// + public void SetProjectLoadingFlag(bool value) + { + Interlocked.Exchange(ref _loadingProjectFlag, value ? 1 : 0); + } + /// + /// 判断是否正在加载项目 + /// + /// + public bool IsLoadingProject() + { + return Interlocked.CompareExchange(ref _loadingProjectFlag, 1, 1) == 1; + } + + /// + public IFlowEnvironment CurrentEnv { get => currentFlowEnvironment; } + /// + public UIContextOperation UIContextOperation => currentFlowEnvironment.UIContextOperation; + /// + public NodeMVVMManagement NodeMVVMManagement => currentFlowEnvironment.NodeMVVMManagement; + /// + public ISereinIOC IOC => (ISereinIOC)currentFlowEnvironment; + + /// + public string EnvName => currentFlowEnvironment.EnvName; + + /// + public string ProjectFileLocation => currentFlowEnvironment.EnvName; + + /// + public bool IsGlobalInterrupt => currentFlowEnvironment.IsGlobalInterrupt; + + /// + public bool IsControlRemoteEnv => currentFlowEnvironment.IsControlRemoteEnv; + + /// + public InfoClass InfoClass { get => currentFlowEnvironment.InfoClass; set => currentFlowEnvironment.InfoClass = value; } + + /// + public RunState FlowState { get => currentFlowEnvironment.FlowState; set => currentFlowEnvironment.FlowState = value; } + + /// + public event LoadDllHandler OnDllLoad { + add { currentFlowEnvironmentEvent.OnDllLoad += value; } + remove { currentFlowEnvironmentEvent.OnDllLoad -= value; } + } + + /// + public event ProjectLoadedHandler OnProjectLoaded + { + add { currentFlowEnvironmentEvent.OnProjectLoaded += value; } + remove { currentFlowEnvironmentEvent.OnProjectLoaded -= value; } + } + + /// + public event ProjectSavingHandler? OnProjectSaving + { + add { currentFlowEnvironmentEvent.OnProjectSaving += value; } + remove { currentFlowEnvironmentEvent.OnProjectSaving -= value; } + } + + /// + public event NodeConnectChangeHandler OnNodeConnectChange + { + add { currentFlowEnvironmentEvent.OnNodeConnectChange += value; } + remove { currentFlowEnvironmentEvent.OnNodeConnectChange -= value; } + } + + /// + public event CanvasCreateHandler OnCanvasCreate + { + add { currentFlowEnvironmentEvent.OnCanvasCreate += value; } + remove { currentFlowEnvironmentEvent.OnCanvasCreate -= value; } + } + + /// + public event CanvasRemoveHandler OnCanvasRemove + { + add { currentFlowEnvironmentEvent.OnCanvasRemove += value; } + remove { currentFlowEnvironmentEvent.OnCanvasRemove -= value; } + } + + /// + public event NodeCreateHandler OnNodeCreate + { + add { currentFlowEnvironmentEvent.OnNodeCreate += value; } + remove { currentFlowEnvironmentEvent.OnNodeCreate -= value; } + } + + /// + public event NodeRemoveHandler OnNodeRemove + { + add { currentFlowEnvironmentEvent.OnNodeRemove += value; } + remove { currentFlowEnvironmentEvent.OnNodeRemove -= value; } + } + + /// + public event NodePlaceHandler OnNodePlace + { + add { currentFlowEnvironmentEvent.OnNodePlace += value; } + remove { currentFlowEnvironmentEvent.OnNodePlace -= value; } + } + + /// + public event NodeTakeOutHandler OnNodeTakeOut + { + add { currentFlowEnvironmentEvent.OnNodeTakeOut += value; } + remove { currentFlowEnvironmentEvent.OnNodeTakeOut -= value; } + } + + /// + public event StartNodeChangeHandler OnStartNodeChange + { + add { currentFlowEnvironmentEvent.OnStartNodeChange += value; } + remove { currentFlowEnvironmentEvent.OnStartNodeChange -= value; } + } + + /// + public event FlowRunCompleteHandler OnFlowRunComplete + { + add { currentFlowEnvironmentEvent.OnFlowRunComplete += value; } + remove { currentFlowEnvironmentEvent.OnFlowRunComplete -= value; } + } + + /// + public event MonitorObjectChangeHandler OnMonitorObjectChange + { + add { currentFlowEnvironmentEvent.OnMonitorObjectChange += value; } + remove { currentFlowEnvironmentEvent.OnMonitorObjectChange -= value; } + } + + /// + public event NodeInterruptStateChangeHandler OnNodeInterruptStateChange + { + add { currentFlowEnvironmentEvent.OnNodeInterruptStateChange += value; } + remove { currentFlowEnvironmentEvent.OnNodeInterruptStateChange -= value; } + } + + /// + public event ExpInterruptTriggerHandler OnInterruptTrigger + { + add { currentFlowEnvironmentEvent.OnInterruptTrigger += value; } + remove { currentFlowEnvironmentEvent.OnInterruptTrigger -= value; } + } + + /// + public event IOCMembersChangedHandler OnIOCMembersChanged + { + add { currentFlowEnvironmentEvent.OnIOCMembersChanged += value; } + remove { currentFlowEnvironmentEvent.OnIOCMembersChanged -= value; } + } + + /// + public event NodeLocatedHandler OnNodeLocated + { + add { currentFlowEnvironmentEvent.OnNodeLocated += value; } + remove { currentFlowEnvironmentEvent.OnNodeLocated -= value; } + } + + /// + public event NodeMovedHandler OnNodeMoved + { + add { currentFlowEnvironmentEvent.OnNodeMoved += value; } + remove { currentFlowEnvironmentEvent.OnNodeMoved -= value; } + } + + /// + public event EnvOutHandler OnEnvOut + { + add { currentFlowEnvironmentEvent.OnEnvOut += value; } + remove { currentFlowEnvironmentEvent.OnEnvOut -= value; } + } + + + + /// + public void ActivateFlipflopNode(string nodeGuid) + { + currentFlowEnvironment.ActivateFlipflopNode(nodeGuid); + } + + /// + public async Task CreateCanvasAsync(string canvasName, int width, int height) + { + return await currentFlowEnvironment.CreateCanvasAsync(canvasName, width, height); + } + + /// + public async Task RemoveCanvasAsync(string canvasGuid) + { + return await currentFlowEnvironment.RemoveCanvasAsync(canvasGuid); + } + + /// + public async Task ConnectInvokeNodeAsync(string canvasGuid, + string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionInvokeType invokeType) + { + return await currentFlowEnvironment.ConnectInvokeNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, invokeType); + } + + /// + public async Task ConnectArgSourceNodeAsync(string canvasGuid, + string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionArgSourceType argSourceType, + int argIndex) + { + return await currentFlowEnvironment.ConnectArgSourceNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, argSourceType, argIndex); + } + + /// + public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) + { + // 连接成功,切换远程环境 + (var isConnect, var remoteMsgUtil) = await currentFlowEnvironment.ConnectRemoteEnv(addres, port, token); + if (isConnect) { - if (OperatingSystem.IsWindows()) - { - UIContextOperation?.Invoke(() => this?.OnIOCMembersChanged?.Invoke(e)); // 监听IOC容器的注册 - } - }; - this.FlowLibraryManagement = new FlowLibraryManagement(this); // 实例化类库管理 - this.NodeMVVMManagement = new NodeMVVMManagement(); - #region 注册基本节点类型 - NodeMVVMManagement.RegisterModel(NodeControlType.UI, typeof(SingleUINode)); // 动作节点 - - NodeMVVMManagement.RegisterModel(NodeControlType.Action, typeof(SingleActionNode)); // 动作节点 - NodeMVVMManagement.RegisterModel(NodeControlType.Flipflop, typeof(SingleFlipflopNode)); // 触发器节点 - NodeMVVMManagement.RegisterModel(NodeControlType.ExpOp, typeof(SingleExpOpNode)); // 表达式节点 - NodeMVVMManagement.RegisterModel(NodeControlType.ExpCondition, typeof(SingleConditionNode)); // 条件表达式节点 - //NodeMVVMManagement.RegisterModel(NodeControlType.ConditionRegion, typeof(CompositeConditionNode)); // 条件区域 - NodeMVVMManagement.RegisterModel(NodeControlType.GlobalData, typeof(SingleGlobalDataNode)); // 全局数据节点 - NodeMVVMManagement.RegisterModel(NodeControlType.Script, typeof(SingleScriptNode)); // 脚本节点 - NodeMVVMManagement.RegisterModel(NodeControlType.NetScript, typeof(SingleNetScriptNode)); // 脚本节点 - NodeMVVMManagement.RegisterModel(NodeControlType.FlowCall, typeof(SingleFlowCallNode)); // 流程调用节点 - #endregion - - #region 注册基本服务类 - PersistennceInstance.Add(typeof(FlowInterruptTool), new FlowInterruptTool()); // 缓存流程实例 - PersistennceInstance.Add(typeof(IFlowEnvironment), (FlowEnvironment)this); // 缓存流程实例 - PersistennceInstance.Add(typeof(ISereinIOC), this); // 缓存容器服务 - - ReRegisterPersistennceInstance(); - - #endregion + remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteMsgUtil, this.UIContextOperation); + currentFlowEnvironment = remoteFlowEnvironment; + } + return (isConnect, remoteMsgUtil); } - #region 远程管理 - - private MsgControllerOfServer clientMsgManage; - - /// - /// 表示是否正在控制远程 - /// Local control remote env - /// - public bool IsControlRemoteEnv { get; set; } - - /// - /// 打开远程管理 - /// - /// - public async Task StartRemoteServerAsync(int port = 7525) + /// + public async Task LoadNodeInfosAsync(List nodeInfos) { - if (clientMsgManage is null) - { - clientMsgManage = new MsgControllerOfServer(this); - //clientMsgManage = new MsgControllerOfServer(this,"123456"); - } - _ = clientMsgManage.StartRemoteServerAsync(port); + SetProjectLoadingFlag(false); + await currentFlowEnvironment.LoadNodeInfosAsync(nodeInfos); // 装饰器调用 + SetProjectLoadingFlag(true); } - /// - /// 结束远程管理 - /// - public void StopRemoteServer() + /// + public async Task CreateNodeAsync(string canvasGuid, NodeControlType nodeBase, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null) { - try - { - clientMsgManage.StopRemoteServer(); - } - catch (Exception ex) - { - SereinEnv.WriteLine(InfoType.ERROR, "结束远程管理异常:" + ex); - } + SetProjectLoadingFlag(false); + var result = await currentFlowEnvironment.CreateNodeAsync(canvasGuid, nodeBase, position, methodDetailsInfo); // 装饰器调用 + SetProjectLoadingFlag(true); + return result; } - #endregion + /// + public async Task PlaceNodeToContainerAsync(string canvasGuid, string nodeGuid, string containerNodeGuid) + { + SetProjectLoadingFlag(false); + var result = await currentFlowEnvironment.PlaceNodeToContainerAsync(canvasGuid, nodeGuid, containerNodeGuid); // 装饰器调用 + SetProjectLoadingFlag(true); + return result; + } - #region 环境运行事件 - /// - /// 加载Dll - /// - public event LoadDllHandler? OnDllLoad; + /// + public async Task TakeOutNodeToContainerAsync(string canvasGuid, string nodeGuid) + { + SetProjectLoadingFlag(false); + var result = await currentFlowEnvironment.TakeOutNodeToContainerAsync(canvasGuid,nodeGuid); // 装饰器调用 + SetProjectLoadingFlag(true); + return result; + } - /// - /// 移除DLL - /// - public event RemoteDllHandler? OnDllRemote; + /// + public async Task ExitFlowAsync() + { + return await currentFlowEnvironment.ExitFlowAsync(); + } - /// - /// 项目加载完成 - /// - public event ProjectLoadedHandler? OnProjectLoaded; - - /// - /// 项目准备保存 - /// - public event ProjectSavingHandler? OnProjectSaving; - - /// - /// 节点连接属性改变事件 - /// - public event NodeConnectChangeHandler? OnNodeConnectChange; - - /// - /// 节点创建事件 - /// - public event NodeCreateHandler? OnNodeCreate; - - /// - /// 移除节点事件 - /// - public event NodeRemoveHandler? OnNodeRemove; - - /// - /// 节点放置事件 - /// - public event NodePlaceHandler OnNodePlace; - - /// - /// 节点取出事件 - /// - public event NodeTakeOutHandler OnNodeTakeOut; - - /// - /// 起始节点变化事件 - /// - public event StartNodeChangeHandler? OnStartNodeChange; - - /// - /// 流程运行完成事件 - /// - public event FlowRunCompleteHandler? OnFlowRunComplete; - - /// - /// 被监视的对象改变事件 - /// - public event MonitorObjectChangeHandler? OnMonitorObjectChange; - - /// - /// 节点中断状态改变事件 - /// - public event NodeInterruptStateChangeHandler? OnNodeInterruptStateChange; - - /// - /// 节点触发了中断 - /// - public event ExpInterruptTriggerHandler? OnInterruptTrigger; - - /// - /// 容器改变 - /// - public event IOCMembersChangedHandler? OnIOCMembersChanged; - - /// - /// 节点需要定位 - /// - public event NodeLocatedHandler? OnNodeLocated; - - /// - /// 节点移动了(远程插件) - /// - public event NodeMovedHandler? OnNodeMoved; - - /// - /// 运行环境输出 - /// - public event EnvOutHandler? OnEnvOut; - - /// - /// 本地环境添加了画布 - /// - public event CanvasCreateHandler OnCanvasCreate; - - /// - /// 本地环境移除了画布 - /// - public event CanvasRemoveHandler OnCanvasRemove; - - #endregion - - #region 属性 - - /// - /// 当前环境 - /// - public IFlowEnvironment CurrentEnv { get => this; } - - /// - /// UI线程操作类 - /// - public UIContextOperation UIContextOperation { get; private set; } - - /// - /// 节点视图模型管理类 - /// - public NodeMVVMManagement NodeMVVMManagement { get; set; } - - /// - /// 信息输出等级 - /// - public InfoClass InfoClass { get ; set ; } = InfoClass.Trivial; - - /// - /// 如果没有全局触发器,且没有循环分支,流程执行完成后自动为 Completion 。 - /// - public RunState FlowState { get; set; } = RunState.NoStart; - /// - /// 如果全局触发器还在运行,则为 Running 。 - /// - public RunState FlipFlopState { get; set; } = RunState.NoStart; - - /// - /// 环境名称 - /// - public string EnvName { get; set; } = SpaceName; + /// + public void ExitRemoteEnv() + { + currentFlowEnvironment.ExitRemoteEnv(); + } - /// - /// 本地加载的项目文件路径 - /// - public string ProjectFileLocation { get; set; } = string.Empty; + /// + public async Task GetEnvInfoAsync() + { + return await currentFlowEnvironment.GetEnvInfoAsync(); + } - /// - /// 是否全局中断 - /// - public bool IsGlobalInterrupt { get; set; } + /// + public async Task GetProjectInfoAsync() + { + return await currentFlowEnvironment.GetProjectInfoAsync(); + } - ///// - ///// 流程中断器 - ///// - //public ChannelFlowInterrupt ChannelFlowInterrupt { get; set; } + /// + public void LoadLibrary(string dllPath) + { + currentFlowEnvironment.LoadLibrary(dllPath); + } - /// - /// 单例模式IOC容器,内部维护了一个实例字典,默认使用类型的FullName作为Key,如果以“接口-实现类”的方式注册,那么将使用接口类型的FullName作为Key。 - /// 当某个类型注册绑定成功后,将不会因为其它地方尝试注册相同类型的行为导致类型被重新创建。 - /// - public ISereinIOC IOC { get => this; } + /// + public void SaveProject() + { + currentFlowEnvironment.SaveProject(); + } + /// + public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) + { + if (flowEnvInfo is null) return; + SetProjectLoadingFlag(false); + currentFlowEnvironment.LoadProject(flowEnvInfo, filePath); + SetProjectLoadingFlag(true); + } - #endregion + /// + public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) + { + currentFlowEnvironment.MonitorObjectNotification(nodeGuid, monitorData, sourceType); + } - #region 私有变量 + /// + public void MoveNode(string canvasGuid, string nodeGuid, double x, double y) + { + currentFlowEnvironment.MoveNode(canvasGuid, nodeGuid, x, y); + } - /// - /// 通过程序集名称管理动态加载的程序集,用于节点创建提供方法描述,流程运行时提供Emit委托 - /// - private readonly FlowLibraryManagement FlowLibraryManagement; + /// + public void NodeLocated(string nodeGuid) + { + currentFlowEnvironment.NodeLocated(nodeGuid); + } - /// - /// IOC对象容器管理 - /// - private readonly SereinIOC sereinIOC; + /// + public bool TryUnloadLibrary(string assemblyName) + { + return currentFlowEnvironment.TryUnloadLibrary(assemblyName); + } - /// - /// 本地运行环境缓存的持久化实例 - /// - private Dictionary PersistennceInstance { get; } = new Dictionary(); + /// + public async Task SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) + { + return await currentFlowEnvironment.SetConnectPriorityInvoke(fromNodeGuid, toNodeGuid, connectionType); + } - /// - /// 环境加载的节点集合 - /// Node Guid - Node Model - /// - private Dictionary NodeModels { get; } = []; + /// + public async Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) + { + return await currentFlowEnvironment.RemoveConnectInvokeAsync(canvasGuid, fromNodeGuid, toNodeGuid, connectionType); + } - /// - /// 运行环境加载的画布集合 - /// - private Dictionary FlowCanvass { get; } = []; + /// + public async Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex) + { + return await currentFlowEnvironment.RemoveConnectArgSourceAsync(canvasGuid, fromNodeGuid, toNodeGuid, argIndex); + } - /// - /// 存放触发器节点(运行时全部调用) - /// - private List FlipflopNodes { get; } = []; + /// + public async Task RemoveNodeAsync(string canvasGuid, string nodeGuid) + { + return await currentFlowEnvironment.RemoveNodeAsync(canvasGuid, nodeGuid); + } - /// - /// 流程任务管理 - /// - private FlowWorkManagement? flowTaskManagement; - - #endregion - - #region 环境对外接口 - /// /// 输出信息 /// @@ -329,1950 +427,146 @@ namespace Serein.NodeFlow.Env /// 日志级别 public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial) { - if (@class >= this.InfoClass) - { - OnEnvOut?.Invoke(type, message); - } - //Console.WriteLine($"{DateTime.UtcNow} [{type}] : {message}{Environment.NewLine}"); - - } - - /// - /// 异步运行 - /// - /// - public async Task StartFlowAsync(string[] canvasGuids) - { - #region 校验参数 - HashSet guids = new HashSet(); - bool isBreak = false; - foreach (var canvasGuid in canvasGuids) - { - if (guids.Contains(canvasGuid)) - { - SereinEnv.WriteLine(InfoType.WARN, $"画布重复,停止运行。{canvasGuid}"); - isBreak = true; - } - if (!FlowCanvass.ContainsKey(canvasGuid)) - { - SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{canvasGuid}"); - isBreak = true; - } - var count = NodeModels.Values.Count(n => n.CanvasDetails.Guid.Equals(canvasGuid)); - if(count == 0) - { - SereinEnv.WriteLine(InfoType.WARN, $"画布没有节点,停止运行。{canvasGuid}"); - isBreak = true; - } - else - { - guids.Add(canvasGuid); - } - } - if (isBreak) - { - guids.Clear(); - return false; - } - #endregion - - - #region 初始化每个画布的数据,转换为流程任务 - Dictionary flowTasks = []; - foreach (var guid in guids) - { - if (!TryGetCanvasModel(guid, out var canvasModel)) - { - SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{guid}"); - return false; - } - var ft = new FlowTask(); - ft.GetNodes = () => NodeModels.Values.Where(node => node.CanvasDetails.Guid.Equals(guid)).ToList(); - if (canvasModel.StartNode.Guid is null) - { - SereinEnv.WriteLine(InfoType.WARN, $"画布不存在起始节点,将停止运行。{guid}"); - return false; - } - ft.GetStartNode = () => canvasModel.StartNode; - flowTasks.Add(guid, ft); - } - #endregion - - - - IOC.Reset(); - IOC.Register(); // 注册脚本接口 - - var flowTaskOptions = new FlowWorkOptions - { - Environment = this, // 流程 - Flows = flowTasks, - FlowContextPool = new ObjectPool(() => new DynamicContext(this)), // 上下文对象池 - AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(), // 需要自动实例化的类型 - InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init), - LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading), - ExitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit), - }; - - - - flowTaskManagement = new FlowWorkManagement(flowTaskOptions); - var cts = new CancellationTokenSource(); - try - { - var t = await flowTaskManagement.RunAsync(cts.Token); - } - catch (Exception ex) - { - SereinEnv.WriteLine(ex); - } - finally - { - - SereinEnv.WriteLine(InfoType.INFO, $"流程运行完毕{Environment.NewLine}"); ; - } - flowTaskOptions = null; - return true; - - - } - - - /// - /// 从选定节点开始运行 - /// - /// - /// - public async Task StartFlowFromSelectNodeAsync(string startNodeGuid) - { - - if (flowTaskManagement is null) - { - SereinEnv.WriteLine(InfoType.ERROR, "没有启动流程,无法运行单个节点"); - return false; - } - if (true || FlowState == RunState.Running || FlipFlopState == RunState.Running) - { - - if (!TryGetNodeModel(startNodeGuid,out var nodeModel) || nodeModel is SingleFlipflopNode) - { - return false; - } - await flowTaskManagement.StartFlowInSelectNodeAsync(this, nodeModel); - return true; - } - else - { - return false; - } - } - - /*/// - /// 单独运行一个节点 - /// - /// - /// - public async Task InvokeNodeAsync(IDynamicContext context, string nodeGuid) - { - object result = Unit.Default; - if (this.NodeModels.TryGetValue(nodeGuid, out var model)) - { - CancellationTokenSource cts = new CancellationTokenSource(); - result = await model.ExecutingAsync(context, cts.Token); - cts?.Cancel(); - } - return result; - }*/ - - /// - /// 结束流程 - /// - public Task ExitFlowAsync() - { - flowTaskManagement?.Exit(); - UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs())); - IOC.Reset(); - flowTaskManagement = null; - GC.Collect(); - return Task.FromResult(true); - } - - /// - /// 激活全局触发器 - /// - /// - public void ActivateFlipflopNode(string nodeGuid) - { - if(!TryGetNodeModel(nodeGuid, out var nodeModel)) - { - return; - } - if (nodeModel is null) return; - if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 - { - if (FlowState != RunState.Completion - && flipflopNode.NotExitPreviousNode()) // 正在运行,且该触发器没有上游节点 - { - _ = flowTaskManagement.RunGlobalFlipflopAsync(this, flipflopNode);// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中 - - } - } - } - - /// - /// 关闭全局触发器 - /// - /// - public void TerminateFlipflopNode(string nodeGuid) - { - if(!TryGetNodeModel(nodeGuid, out var nodeModel)) - { - return; - } - if (nodeModel is null) return; - if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 - { - flowTaskManagement.TerminateGlobalFlipflopRuning(flipflopNode); - } - } - - /// - /// 获取当前环境信息(远程连接) - /// - /// - public async Task GetEnvInfoAsync() - { - // 获取所有的程序集对应的方法信息(程序集相关的数据) - var libraryMdss = this.FlowLibraryManagement.GetAllLibraryMds().ToArray(); - // 获取当前项目的信息(节点相关的数据) - var project = await GetProjectInfoAsync(); // 远程连接获取远程环境项目信息 - SereinEnv.WriteLine(InfoType.INFO, "已将当前环境信息发送到远程客户端"); - return new FlowEnvInfo - { - Project = project, // 项目信息 - LibraryMds = libraryMdss, // 环境方法 - }; + currentFlowEnvironment.WriteLine(type, message, @class); } - /// - /// 保存项目 - /// - public void SaveProject() + #region MyRegion +#if false + public async Task AddInterruptExpressionAsync(string key, string expression) { - var project = GetProjectInfoAsync().GetAwaiter().GetResult(); - OnProjectSaving?.Invoke(new ProjectSavingEventArgs(project)); - } - - /// - /// 加载项目文件 - /// - /// 环境信息 - /// - public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) - { - this.ProjectFileLocation = filePath; - var projectData = flowEnvInfo.Project; - // 加载项目配置文件 - var dllPaths = projectData.Librarys.Select(it => it.FilePath).ToList(); - List methodDetailss = []; - - // 遍历依赖项中的特性注解,生成方法详情 - foreach (var dllPath in dllPaths) - { - string cleanedRelativePath = dllPath.TrimStart('.', '\\'); - var tmpPath = Path.Combine(filePath, cleanedRelativePath); - var dllFilePath = Path.GetFullPath(tmpPath); - LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集 - } - - - - _ = Task.Run( async () => - { - // 加载画布 - foreach (var canvasInfo in projectData.Canvass) - { - LoadCanvas(canvasInfo); - } - await LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息 - - // 加载画布 - foreach (var canvasInfo in projectData.Canvass) - { - await SetStartNodeAsync(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点 - } - //await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点 - }); - - } - - /// - /// 加载远程环境 - /// - /// 远程环境地址 - /// 远程环境端口 - /// 密码 - public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) - { - if (IsControlRemoteEnv) - { - await Console.Out.WriteLineAsync($"当前已经连接远程环境"); - return (false, null); - } - // 没有连接远程环境,可以重新连接 - - var controlConfiguration = new RemoteMsgUtil.ControlConfiguration - { - Addres = addres, - Port = port, - Token = token, - ThemeJsonKey = FlowEnvironment.ThemeKey, - MsgIdJsonKey = FlowEnvironment.MsgIdKey, - DataJsonKey = FlowEnvironment.DataKey, - }; - var remoteMsgUtil = new RemoteMsgUtil(controlConfiguration); - var result = await remoteMsgUtil.ConnectAsync(); - if (!result) - { - await Console.Out.WriteLineAsync("连接失败,请检查地址与端口是否正确"); - return (false, null); - } - await Console.Out.WriteLineAsync("连接成功,开始验证Token"); - IsControlRemoteEnv = true; - return (true, remoteMsgUtil); - } - - /// - /// 退出远程环境 - /// - public void ExitRemoteEnv() - { - IsControlRemoteEnv = false; - } - - /// - /// 序列化当前项目的依赖信息、节点信息 - /// - /// - public async Task GetProjectInfoAsync() - { - var projectData = new SereinProjectData() - { - Librarys = this.FlowLibraryManagement.GetAllLibraryInfo().ToArray(), - Nodes = NodeModels.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(), - Canvass = FlowCanvass.Values.Select(canvas => canvas.ToInfo()).ToArray(), - //StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid, - }; - - return projectData; + return await currentFlowEnvironment.AddInterruptExpressionAsync(key, expression); } - /// - /// 从文件路径中加载DLL - /// - /// - /// - public void LoadLibrary(string dllPath) + public async Task<(bool, string[])> CheckObjMonitorStateAsync(string key) { - - try - { - #region 检查是否已经加载本地依赖 - var thisAssembly = typeof(IFlowEnvironment).Assembly; - var thisAssemblyName = thisAssembly.GetName().Name; - if (!string.IsNullOrEmpty(thisAssemblyName) && FlowLibraryManagement.GetLibraryMdsOfAssmbly(thisAssemblyName).Count == 0) - { - var tmp = FlowLibraryManagement.LoadLibraryOfPath(thisAssembly.Location); - UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(tmp.Item1, tmp.Item2))); // 通知UI创建dll面板显示 - } - - #endregion - - (var libraryInfo, var mdInfos) = FlowLibraryManagement.LoadLibraryOfPath(dllPath); - if (mdInfos.Count > 0) - { - UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示 - } - } - catch (Exception ex) - { - SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件:{ex}"); - } + return await currentFlowEnvironment.CheckObjMonitorStateAsync(key); + } + public async Task GetOrCreateGlobalInterruptAsync() + { + return await currentFlowEnvironment.InterruptNode(); } - /// - /// 加载本地程序集 - /// - /// - public void LoadLibrary(FlowLibrary flowLibrary) + public void SetMonitorObjState(string key, bool isMonitor) { - try - { - (var libraryInfo, var mdInfos) = FlowLibraryManagement.LoadLibraryOfPath(flowLibrary); - if (mdInfos.Count > 0) - { - UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示 - } - } - catch (Exception ex) - { - SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件:{ex}"); - } - + currentFlowEnvironment.SetMonitorObjState(key, isMonitor); } - - /// - /// 移除DLL - /// - /// - /// - public bool TryUnloadLibrary(string assemblyName) + public async Task SetNodeInterruptAsync(string nodeGuid, bool isInterrupt) { - // 获取与此程序集相关的节点 - var groupedNodes = NodeModels.Values.Where(node => !string.IsNullOrWhiteSpace(node.MethodDetails.AssemblyName) && node.MethodDetails.AssemblyName.Equals(assemblyName)).ToArray(); - if (groupedNodes.Length == 0) - { - var isPass = FlowLibraryManagement.UnloadLibrary(assemblyName); - return isPass; - } - else - { - StringBuilder sb = new StringBuilder(); - sb.AppendLine(); - for (int i = 0; i < groupedNodes.Length; i++) - { - IFlowNode? node = groupedNodes[i]; - sb.AppendLine($"{i} => {node.Guid}"); - } - SereinEnv.WriteLine(InfoType.ERROR, $"无法卸载[{assemblyName}]程序集,因为这些节点依赖于此程序集:{sb.ToString()}"); - - return false; - } - - //var mds = FlowLibraryManagement.GetLibraryMdsOfAssmbly(assemblyName); - //if(mds.Count > 0) - //{ - - - //} - //else - //{ - // return true; - //} - - //var library = LibraryInfos.Values.FirstOrDefault(nl => assemblyName.Equals(nl.AssemblyName)); - //if (library is null) - //{ - // return false; - //} - //var groupedNodes = NodeModels.Values - // .Where(node => node.MethodDetails is not null) - // .ToArray() - // .GroupBy(node => node.MethodDetails?.MethodName) - // .ToDictionary( - // key => key.Key, - // group => group.Count()); - - - //if (NodeModels.Count == 0) - //{ - // return true; // 当前无节点,可以直接删除 - //} - - //if (MethodDetailsOfLibraryInfos.TryGetValue(library, out var mds)) // 存在方法 - //{ - // foreach (var md in mds) - // { - // if (groupedNodes.TryGetValue(md.MethodName, out int count)) - // { - // if (count > 0) - // { - // return false; // 创建过相关的节点,无法移除 - // } - // } - // } - // // 开始移除相关信息 - // foreach (var md in mds) - // { - // MethodDetailss.TryRemove(md.MethodName, out _); - // } - // MethodDetailsOfLibraryInfos.TryRemove(library, out _); - // return true; - //} - //else - //{ - // return true; - //} - } - - private int _addCanvasCount = 0; - - /// - /// 增加画布 - /// - /// 画布名称 - /// 宽度 - /// 高度 - /// - public async Task CreateCanvasAsync(string canvasName, int width, int height) - { - var info = new FlowCanvasDetailsInfo() - { - Guid = Guid.NewGuid().ToString(), - Height = height, - Width = width, - ViewX = 0, - ViewY = 0, - ScaleY = 1, - ScaleX = 1, - Name = !string.IsNullOrWhiteSpace(canvasName) ? canvasName : $"流程图{_addCanvasCount++}", - }; - var model = LoadCanvas(info); - return info; - } - - private FlowCanvasDetails LoadCanvas(FlowCanvasDetailsInfo info) - { - var model = new FlowCanvasDetails(this); - model.LoadInfo(info); - FlowCanvass.Add(model.Guid, model); - UIContextOperation?.Invoke(() => - { - OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model)); - }); - return model; - } - - /// - /// 删除画布 - /// - /// 画布Guid - /// - public async Task RemoveCanvasAsync(string canvasGuid) - { - - if (!FlowCanvass.TryGetValue(canvasGuid, out var model)) - { - return false; - } - var count = NodeModels.Values.Count(node => node.CanvasDetails.Guid.Equals(canvasGuid)); - if(count > 0) - { - SereinEnv.WriteLine(InfoType.WARN, "无法删除具有节点的画布"); - return false; - } - if (FlowCanvass.Remove(canvasGuid)) - { - UIContextOperation?.Invoke(() => - { - OnCanvasRemove.Invoke(new CanvasRemoveEventArgs(canvasGuid)); - }); - return true; - } - - return false; - } - - - /// - /// 从节点信息集合批量加载节点控件 - /// - /// 节点信息 - /// - /// - public async Task LoadNodeInfosAsync(List nodeInfos) - { - #region 从NodeInfo创建NodeModel - // 流程接口节点最后才创建 - - List flowCallNodeInfos = []; - foreach (NodeInfo? nodeInfo in nodeInfos) - { - if(nodeInfo.Type == nameof(NodeControlType.FlowCall)) - { - flowCallNodeInfos.Add(nodeInfo); - } - else - { - if (!CreateNodeFromNodeInfo(nodeInfo)) - { - SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}"); - continue; - } - } - } - - // 创建流程接口节点 - foreach (NodeInfo? nodeInfo in flowCallNodeInfos) - { - if (!CreateNodeFromNodeInfo(nodeInfo)) - { - SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}"); - continue; - } - } - #endregion - - #region 重新放置节点 - - List needPlaceNodeInfos = []; - foreach (NodeInfo? nodeInfo in nodeInfos) - { - if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) && - NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode)) - { - needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点 - } - } - - foreach (NodeInfo nodeInfo in needPlaceNodeInfos) - { - if (NodeModels.TryGetValue(nodeInfo.Guid, out var nodeModel) && - NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var containerNode) - && containerNode is INodeContainer nodeContainer) - { - var result = nodeContainer.PlaceNode(nodeModel); - if (result) - { - UIContextOperation?.Invoke(() => OnNodePlace?.Invoke( - new NodePlaceEventArgs(nodeInfo.CanvasGuid, nodeModel.Guid, containerNode.Guid))); - } - - - } - } - #endregion - - #region 确定节点之间的方法调用关系 - foreach (var nodeInfo in nodeInfos) - { - var canvasGuid = nodeInfo.CanvasGuid; - if (!TryGetNodeModel(nodeInfo.Guid, out var fromNodeModel)) - { - return; - } - if (fromNodeModel is null) continue; - List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), - (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), - (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes), - (ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)]; - foreach ((ConnectionInvokeType connectionType, string[] toNodeGuids) item in allToNodes) - { - // 遍历当前类型分支的节点(确认连接关系) - foreach (var toNodeGuid in item.toNodeGuids) - { - if (!TryGetNodeModel(toNodeGuid, out var toNodeModel)) - { - return; - } - if (toNodeModel is null) { - // 防御性代码,加载正常保存的项目文件不会进入这里 - continue; - }; - var isSuccessful = ConnectInvokeOfNode(canvasGuid, fromNodeModel, toNodeModel, item.connectionType); // 加载时确定节点间的连接关系 - } - } - - - //List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), - // (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), - // (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes), - // (ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)]; - - //List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0) - // .Select(info => (info.connectionType, - // info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid]) - // .ToArray())) - // .ToList(); - // 遍历每种类型的节点分支(四种) - //foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in nodeInfo) - //{ - // // 遍历当前类型分支的节点(确认连接关系) - // foreach (var toNode in item.toNodes) - // { - // _ = ConnectInvokeOfNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系 - // } - //} - } - #endregion - - #region 确定节点之间的参数调用关系 - foreach (var toNode in NodeModels.Values) - { - var canvasGuid = toNode.CanvasDetails.Guid; - if (toNode.MethodDetails.ParameterDetailss == null) - { - continue; - } - for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++) - { - var pd = toNode.MethodDetails.ParameterDetailss[i]; - if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid) - && NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode)) - { - - await ConnectArgSourceOfNodeAsync(canvasGuid, fromNode, toNode, pd.ArgDataSourceType, pd.Index); - } - } - } - #endregion - - - UIContextOperation?.Invoke(() => - { - OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); - }); - - return; - } - - /// - /// 流程正在运行时创建节点 - /// - /// 所属画布 - /// 所属类型 - /// 所处位置 - /// 如果是表达式节点条件节点,该项为null - public Task CreateNodeAsync(string canvasGuid, - NodeControlType nodeControlType, - PositionOfUI position, - MethodDetailsInfo? methodDetailsInfo = null) - { - if (!TryGetCanvasModel(canvasGuid,out var canvasModel)) - { - return Task.FromResult(null); - } - IFlowNode? nodeModel; - if (methodDetailsInfo is null - || string.IsNullOrEmpty(methodDetailsInfo.AssemblyName) - || string.IsNullOrEmpty(methodDetailsInfo.MethodName)) - { - nodeModel = FlowNodeExtension.CreateNode(this, nodeControlType); // 加载基础节点 - } - else - { - if (FlowLibraryManagement.TryGetMethodDetails(methodDetailsInfo.AssemblyName, // 创建节点 - methodDetailsInfo.MethodName, - out var methodDetails)) - { - nodeModel = FlowNodeExtension.CreateNode(this, nodeControlType, methodDetails); // 一般的加载节点方法 - } - else - { - return Task.FromResult(null); - } - } - nodeModel.CanvasDetails = canvasModel; - canvasModel.Nodes.Add(nodeModel); // 节点与画布互相绑定 - TryAddNode(nodeModel); - nodeModel.Position = position; // 设置位置 - - // 通知UI更改 - UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(canvasGuid, nodeModel, position))); - var nodeInfo = nodeModel.ToInfo(); - return Task.FromResult(nodeInfo); - } - - - /// - /// 将节点放置在容器中 - /// - /// - public Task PlaceNodeToContainerAsync(string canvasGuid, - string nodeGuid, string containerNodeGuid) - { - if (!FlowCanvass.ContainsKey(canvasGuid)) - { - return Task.FromResult(false); - } - // 获取目标节点与容器节点 - if (!TryGetNodeModel(nodeGuid, out var nodeModel)) - { - return Task.FromResult(false); - } - if (nodeModel.ContainerNode is INodeContainer tmpContainer) - { - SereinEnv.WriteLine(InfoType.WARN, $"节点放置失败,节点[{nodeGuid}]已经放置于容器节点[{((IFlowNode)tmpContainer).Guid}]"); - return Task.FromResult(false); - } - - if (!TryGetNodeModel(containerNodeGuid, out var containerNode)) - { - return Task.FromResult(false); - } - if (containerNode is not INodeContainer nodeContainer) return Task.FromResult(false); - - var result = nodeContainer.PlaceNode(nodeModel); // 放置在容器节点 - if (result) - { - UIContextOperation?.Invoke(() => - { - OnNodePlace?.Invoke(new NodePlaceEventArgs(canvasGuid, nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置 - }); - } - return Task.FromResult(result); - - } - - /// - /// 将节点从容器节点中脱离 - /// - /// - public Task TakeOutNodeToContainerAsync(string canvasGuid, - string nodeGuid) - { - if (!FlowCanvass.ContainsKey(canvasGuid)) - { - return Task.FromResult(false); - } - // 获取目标节点与容器节点 - if (!TryGetNodeModel(nodeGuid, out var nodeModel)) - { - return Task.FromResult(false); - } - if (nodeModel.ContainerNode is not INodeContainer nodeContainer) - { - return Task.FromResult(false); - } - var result = nodeContainer.TakeOutNode(nodeModel); // 从容器节点取出 - if (result) - { - UIContextOperation?.Invoke(() => - { - OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(canvasGuid, nodeGuid)); // 重新放置在画布上 - }); - } - return Task.FromResult(result); - - - } - - - - /// - /// 移除节点 - /// - /// - /// - public async Task RemoveNodeAsync(string canvasGuid, string nodeGuid) - { - if (!TryGetCanvasModel(canvasGuid,out var canvasModel)) - { - return false; - } - if (!TryGetNodeModel(nodeGuid, out var remoteNode)) - { - return false; - } - - if (remoteNode is SingleFlipflopNode flipflopNode) - { - flowTaskManagement?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被移除的是全局触发器,尝试从启动器移除 - } - - remoteNode.Remove(); // 调用节点的移除方法 - - // 遍历所有前置节点,从那些前置节点中的后继节点集合移除该节点 - foreach (var pnc in remoteNode.PreviousNodes) - { - var pCType = pnc.Key; // 连接类型 - for (int i = 0; i < pnc.Value.Count; i++) - { - IFlowNode? pNode = pnc.Value[i]; - pNode.SuccessorNodes[pCType].Remove(remoteNode); - - UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs( - canvasGuid, - pNode.Guid, - remoteNode.Guid, - JunctionOfConnectionType.Invoke, - pCType, // 对应的连接关系 - NodeConnectChangeEventArgs.ConnectChangeType.Remove))); // 通知UI - - } - } - - - if(remoteNode.ControlType == NodeControlType.FlowCall) - { - - } - else - { - // 遍历所有后继节点,从那些后继节点中的前置节点集合移除该节点 - foreach (var snc in remoteNode.SuccessorNodes) - { - var connectionType = snc.Key; // 连接类型 - for (int i = 0; i < snc.Value.Count; i++) - { - IFlowNode? toNode = snc.Value[i]; - - await RemoteConnectAsync(canvasGuid, remoteNode, toNode, connectionType); - - } - } - - } - - - - // 从集合中移除节点,解除与画布的绑定关系 - NodeModels.Remove(nodeGuid); - UIContextOperation?.Invoke(() => canvasModel.Nodes.Remove(remoteNode)); - - UIContextOperation?.Invoke(() => OnNodeRemove?.Invoke(new NodeRemoveEventArgs(canvasGuid, nodeGuid))); - return true; - } - - /// - /// 连接节点,创建方法调用关系 - /// - /// 起始节点 - /// 目标节点 - /// 起始节点控制点 - /// 目标节点控制点 - /// 连接关系 - public Task ConnectInvokeNodeAsync(string canvasGuid, - string fromNodeGuid, - string toNodeGuid, - JunctionType fromNodeJunctionType, - JunctionType toNodeJunctionType, - ConnectionInvokeType invokeType) - { - - // 获取起始节点与目标节点 - if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) - { - return Task.FromResult(false); - } - - if (fromNode is null || toNode is null) return Task.FromResult(false); - (var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType); - if (!state) - { - SereinEnv.WriteLine(InfoType.WARN, "出现非预期的连接行为"); - return Task.FromResult(false); // 出现不符预期的连接行为,忽略此次连接行为 - } - - if(type == JunctionOfConnectionType.Invoke) - { - if (fromNodeJunctionType == JunctionType.Execute) - { - // 如果 起始控制点 是“方法调用”,需要反转 from to 节点 - (fromNode, toNode) = (toNode, fromNode); - } - // 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点 - state = ConnectInvokeOfNode(canvasGuid, fromNode, toNode, invokeType); // 本地环境进行连接 - } - return Task.FromResult(state); - - } - - - /// - /// 设置两个节点某个类型的方法调用关系为优先调用 - /// - /// 起始节点 - /// 目标节点 - /// 连接关系 - /// 是否成功调用 - public Task SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) - { - // 获取起始节点与目标节点 - if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) - { - return Task.FromResult(false); - } - if (fromNode is null || toNode is null) return Task.FromResult(false); - if ( fromNode.SuccessorNodes.TryGetValue(connectionType, out var nodes)) - { - var idx = nodes.IndexOf(toNode); - if (idx > -1) - { - nodes.RemoveAt(idx); - nodes.Insert(0, toNode); - return Task.FromResult(true); - } - } - return Task.FromResult(false); - } - - /// - /// 移除连接节点之间方法调用的关系 - /// - /// 起始节点Guid - /// 目标节点Guid - /// 连接关系 - /// - public async Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) - { - // 获取起始节点与目标节点 - if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) - { - return false; - } - if (fromNode is null || toNode is null) return false; - - var result = await RemoteConnectAsync(canvasGuid, fromNode, toNode, connectionType); - return result; - } - - /// - /// 创建节点之间的参数来源关系 - /// - /// 起始节点 - /// 目标节点 - /// 起始节点控制点(result控制点) - /// 目标节点控制点(argData控制点) - /// 目标节点的第几个参数 - /// 调用目标节点对应方法时,对应参数来源类型 - /// - public async Task ConnectArgSourceNodeAsync(string canvasGuid, - string fromNodeGuid, - string toNodeGuid, - JunctionType fromNodeJunctionType, - JunctionType toNodeJunctionType, - ConnectionArgSourceType connectionArgSourceType, - int argIndex) - { - - // 获取起始节点与目标节点 - if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) - { - return false; - } - if (fromNode is null || toNode is null) return false; - (var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType); - if (!state) - { - SereinEnv.WriteLine(InfoType.WARN, "出现非预期的连接行为"); - return false; // 出现不符预期的连接行为,忽略此次连接行为 - } - - if (type == JunctionOfConnectionType.Arg) - { - // 从起始节点“返回值”控制点,连接到目标节点“方法入参”控制点 - if (fromNodeJunctionType == JunctionType.ArgData) - { - // 如果 起始控制点 是“方法入参”,需要反转 from to 节点 - (fromNode, toNode) = (toNode, fromNode); - } - - // 确定方法入参关系 - state = await ConnectArgSourceOfNodeAsync(canvasGuid, fromNode, toNode, connectionArgSourceType, argIndex); // 本地环境进行连接 - } - return state; - - } - - - /// - /// 移除连接节点之间参数传递的关系 - /// - /// 起始节点Guid - /// 目标节点Guid - /// 连接到第几个参数 - public async Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex) - { - // 获取起始节点与目标节点 - if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) - { - return false; - } - if (fromNode is null || toNode is null) return false; - var result = await RemoteConnectAsync(canvasGuid, fromNode, toNode, argIndex); - return result; - } - - - /// - /// 获取方法描述 - /// - - public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo? mdInfo) - { - var isPass = FlowLibraryManagement.TryGetMethodDetails(assemblyName, methodName, out var md); - if (!isPass || md is null) - { - mdInfo = null; - return false; - } - else - { - mdInfo = md?.ToInfo(); - return true; - } - } - - /// - /// 通过方法名称获取对应的Emit委托 - /// 方法无入参时需要传入空数组,void方法自动返回null - /// 普通方法:Func<object,object[],object> - /// 异步方法:Func<object,object[],Task> - /// 异步有返回值方法:Func<object,object[],Task<object>> - /// - /// - /// - /// - public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails? delegateDetails) - { - return FlowLibraryManagement.TryGetDelegateDetails(assemblyName, methodName, out delegateDetails); - } - - /// - /// 设置在UI线程操作的线程上下文 - /// - /// - public void SetUIContextOperation(UIContextOperation uiContextOperation) - { - if(uiContextOperation is not null) - { - this.UIContextOperation = uiContextOperation; - PersistennceInstance[typeof(UIContextOperation)] = uiContextOperation; // 缓存封装好的UI线程上下文 - } - - - - } - - /// - /// 移动了某个节点(远程插件使用) - /// - /// - /// - /// - public void MoveNode(string canvasGuid, string nodeGuid, double x, double y) - { - if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(nodeGuid, out var nodeModel)) - { - return; - } - nodeModel.Position.X = x; - nodeModel.Position.Y = y; - UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y))); - - } - - /// - /// 设置起点控件 - /// - /// 画布 - /// 节点Guid - public Task SetStartNodeAsync(string canvasGuid, string newNodeGuid) - { - if (!TryGetCanvasModel(canvasGuid, out var canvasModel) || !TryGetNodeModel(newNodeGuid, out var newStartNodeModel)) - { - return Task.FromResult(string.Empty); - } - SetStartNode(canvasModel, newStartNodeModel); - return Task.FromResult(canvasModel.StartNode.Guid ?? string.Empty); - } - - /// - /// 启动器调用,运行到某个节点时触发了监视对象的更新(对象预览视图将会自动更新) - /// - /// - /// - /// - public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) - { - OnMonitorObjectChange?.Invoke(new MonitorObjectEventArgs(nodeGuid, monitorData, sourceType)); - } - - /// - /// 启动器调用,节点触发了中断。 - /// - /// 节点 - /// 表达式 - /// 类型,0用户主动的中断,1表达式中断 - public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type) - { - OnInterruptTrigger?.Invoke(new InterruptTriggerEventArgs(nodeGuid, expression, type)); - } - - - ///// - ///// 环境执行中断 - ///// - ///// - //public async Task InterruptNode() - //{ - // IsGlobalInterrupt = true; - // var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(EnvName); - // return result; - //} - - /// - /// 记录节点更改数据,防止重复更改 - /// - public HashSet<(string, string, object)> NodeValueChangeLogger = new HashSet<(string, string, object)>(); - - /// - /// 数据更改通知(来自远程) - /// - /// 发生在哪个节点 - /// 属性路径 - /// 变化后的属性值 - /// - public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) - { - // "NodeModel.Path" - if (TryGetNodeModel(nodeGuid, out var nodeModel)) - { - SerinExpressionEvaluator.Evaluate($"@Set .{path} = {value}", nodeModel, out _); // 更改对应的数据 - } - - - return Task.CompletedTask; - //if (NodeValueChangeLogger.Remove((nodeGuid, path, value))) - //{ - // // 说明存在过重复的修改 - // return; - //} - //NodeValueChangeLogger.Add((nodeGuid, path, value)); - - //lock (NodeValueChangeLogger) - //{ - - // Interlocked.Add(ref i, 1); - // Console.WriteLine(i); - // var getExp = $"@Get .{path}"; - // var setExp = $"@Set .{path} = {value}"; // 生成 set 表达式 - // var oldValue = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); - // if(oldValue != value) - // { - // Console.WriteLine($"旧值:{getExp},result : {oldValue}"); - // SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); // 更改对应的数据 - // Console.WriteLine($"新值:{getExp},result : {SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _)}"); - // } - - //} - - - - } - - - /// - /// 改变可选参数的数目 - /// - /// 对应的节点Guid - /// true,增加参数;false,减少参数 - /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数) - /// - public Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex) - { - if (!TryGetNodeModel(nodeGuid, out var nodeModel)) - { - return Task.FromResult(false); - } - if (nodeModel is null) return Task.FromResult(false); - bool isPass; - if (isAdd) - { - isPass = nodeModel.MethodDetails.AddParamsArg(paramIndex); - } - else - { - isPass = nodeModel.MethodDetails.RemoveParamsArg(paramIndex); - } - return Task.FromResult(isPass); - } - - - /// - /// 从Guid获取画布 - /// - /// 节点Guid - /// 节点Model - /// 无法获取节点、Guid/节点为null时报错 - public bool TryGetCanvasModel(string nodeGuid, out FlowCanvasDetails canvasDetails) - { - if (string.IsNullOrEmpty(nodeGuid)) - { - canvasDetails = null; - return false; - } - return FlowCanvass.TryGetValue(nodeGuid, out canvasDetails) && canvasDetails is not null; - - } - - /// - /// 从Guid获取节点 - /// - /// 节点Guid - /// 节点Model - /// 无法获取节点、Guid/节点为null时报错 - public bool TryGetNodeModel(string nodeGuid,out IFlowNode nodeModel) - { - if (string.IsNullOrEmpty(nodeGuid)) - { - nodeModel = null; - return false; - } - return NodeModels.TryGetValue(nodeGuid, out nodeModel) && nodeModel is not null; - - } + return await currentFlowEnvironment.SetNodeInterruptAsync(nodeGuid, isInterrupt); + } +#endif #endregion + /// + public async Task SetStartNodeAsync(string canvasGuid, string nodeGuid) + { + return await currentFlowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid); + } + + /// + public async Task StartFlowAsync(string[] canvasGuids) + { + return await currentFlowEnvironment.StartFlowAsync(canvasGuids); + } + + /// + public async Task StartFlowFromSelectNodeAsync(string startNodeGuid) + { + return await currentFlowEnvironment.StartFlowFromSelectNodeAsync(startNodeGuid); + } + + /// + public async Task StartRemoteServerAsync(int port = 7525) + { + await currentFlowEnvironment.StartRemoteServerAsync(port); + } + + /// + public void StopRemoteServer() + { + currentFlowEnvironment.StopRemoteServer(); + } + + /// + public void TerminateFlipflopNode(string nodeGuid) + { + currentFlowEnvironment.TerminateFlipflopNode(nodeGuid); + } + + /// + public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type) + { + currentFlowEnvironment.TriggerInterrupt(nodeGuid, expression, type); + } + + /// + public void SetUIContextOperation(UIContextOperation uiContextOperation) + { + currentFlowEnvironment.SetUIContextOperation(uiContextOperation); + } + + /// + public void UseExternalIOC(ISereinIOC ioc) + { + currentFlowEnvironment.UseExternalIOC(ioc); + } + + /// + public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel) + { + return currentFlowEnvironment.TryGetNodeModel(nodeGuid, out nodeModel); + } + + /// + public bool TryGetDelegateDetails(string libraryName, string methodName, out DelegateDetails del) + { + return currentFlowEnvironment.TryGetDelegateDetails(libraryName, methodName, out del); + } + + /// + public bool TryGetMethodDetailsInfo(string libraryName, string methodName, out MethodDetailsInfo mdInfo) + { + return currentFlowEnvironment.TryGetMethodDetailsInfo(libraryName, methodName, out mdInfo); + } + + /// + public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) + { + if (!IsLoadingProject()) + { + return; + } + if (currentFlowEnvironment.IsControlRemoteEnv) + { + await currentFlowEnvironment.NotificationNodeValueChangeAsync(nodeGuid, path, value); + } + } + + /// + public async Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex) + { + return await currentFlowEnvironment.ChangeParameter(nodeGuid, isAdd, paramIndex); + } + #region 流程依赖类库的接口 - - /// - /// 运行时加载 - /// - /// 文件名 - /// + /// public bool LoadNativeLibraryOfRuning(string file) { - - return NativeDllHelper.LoadDll(file); + return currentFlowEnvironment.LoadNativeLibraryOfRuning(file); } - /// - /// 运行时加载指定目录下的类库 - /// - /// 目录 - /// 是否递归加载 + /// public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true) { - NativeDllHelper.LoadAllDll(path); + currentFlowEnvironment.LoadAllNativeLibraryOfRuning(path,isRecurrence); } #endregion - #region 私有方法 - - #region 暂时注释 - /* - /// - /// 加载指定路径的DLL文件 - /// - /// - private void LoadDllNodeInfo(string dllPath) - { - - var fileName = Path.GetFileName(dllPath); - AssemblyLoadContext flowAlc = new AssemblyLoadContext(fileName, true); - flowAlc.LoadFromAssemblyPath(dllPath); // 加载指定路径的程序集 - - foreach(var assemblt in flowAlc.Assemblies) - { - (var registerTypes, var mdlist) = LoadAssembly(assemblt); - if (mdlist.Count > 0) - { - var nodeLibraryInfo = new NodeLibraryInfo - { - //Assembly = assembly, - AssemblyName = assemblt.FullName, - FileName = Path.GetFileName(dllPath), - FilePath = dllPath, - }; - - LibraryInfos.TryAdd(nodeLibraryInfo.AssemblyName, nodeLibraryInfo); - MethodDetailsOfLibraryInfos.TryAdd(nodeLibraryInfo, mdlist); - - foreach (var md in mdlist) - { - MethodDetailss.TryAdd(md.MethodName, md); - } - - foreach (var kv in registerTypes) - { - if (!AutoRegisterTypes.TryGetValue(kv.Key, out var types)) - { - types = new List(); - AutoRegisterTypes.Add(kv.Key, types); - } - types.AddRange(kv.Value); - } - var mdInfos = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息 - - if (OperatingSystem.IsWindows()) - { - UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibraryInfo, mdInfos))); // 通知UI创建dll面板显示 - - } - } - - - } - - - - - }*/ - #endregion - - - /// - /// 从节点信息创建节点,并返回状态指示是否创建成功 - /// - /// - /// - private bool CreateNodeFromNodeInfo(NodeInfo nodeInfo) - { - if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType)) - { - return false; - } - - #region 获取方法描述 - MethodDetails? methodDetails; - if (controlType == NodeControlType.FlowCall) - { - if (string.IsNullOrEmpty(nodeInfo.MethodName)) - { - methodDetails = new MethodDetails(); - methodDetails.ParamsArgIndex = 0; - methodDetails.ParameterDetailss = new ParameterDetails[nodeInfo.ParameterData.Length]; - for (int i = 0; i < methodDetails.ParameterDetailss.Length; i++) - { - var pdInfo = nodeInfo.ParameterData[i]; - var t = new ParameterDetailsInfo(); - var pd = new ParameterDetails(pdInfo, i); - methodDetails.ParameterDetailss[i] = pd; - } - } - else - { - // 目标节点可能是方法节点 - FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息 - } - } - else if (controlType.IsBaseNode()) - { - // 加载基础节点 - methodDetails = new MethodDetails(); - - } - else - { - if (string.IsNullOrEmpty(nodeInfo.MethodName)) return false; - // 加载方法节点 - FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息 - } - #endregion - - var nodeModel = FlowNodeExtension.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点 - if (nodeModel is null) - { - nodeInfo.Guid = string.Empty; - return false; - } - if (FlowCanvass.TryGetValue(nodeInfo.CanvasGuid, out var canvasModel)) - { - - // 节点与画布互相绑定 - // 需要在UI线程上进行添加,否则会报 “不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改”异常 - nodeModel.CanvasDetails = canvasModel; - UIContextOperation?.Invoke(() => canvasModel.Nodes.Add(nodeModel)); - - nodeModel.LoadInfo(nodeInfo); // 创建节点model - TryAddNode(nodeModel); // 加载项目时将节点加载到环境中 - } - else - { - SereinEnv.WriteLine(InfoType.ERROR, $"加载节点[{nodeInfo.Guid}]时发生异常,画布[{nodeInfo.CanvasGuid}]不存在"); - return false; - } - - UIContextOperation?.Invoke(() => - OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上 - return true; - } - - - /// - /// 移除连接关系 - /// - /// 起始节点Model - /// 目标节点Model - /// 连接关系 - /// - private async Task RemoteConnectAsync(string canvasGuid, IFlowNode fromNode, IFlowNode toNode, ConnectionInvokeType connectionType) - { - if (!FlowCanvass.ContainsKey(canvasGuid)) - { - return false; - } - fromNode.SuccessorNodes[connectionType].Remove(toNode); - toNode.PreviousNodes[connectionType].Remove(fromNode); - - - if (OperatingSystem.IsWindows()) - { - UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke( - new NodeConnectChangeEventArgs( - canvasGuid, - fromNode.Guid, - toNode.Guid, - JunctionOfConnectionType.Invoke, - connectionType, - NodeConnectChangeEventArgs.ConnectChangeType.Remove))); - } - return true; - } - /// - /// 移除连接关系 - /// - /// 起始节点Model - /// 目标节点Model - /// 连接关系 - /// - private async Task RemoteConnectAsync(string canvasGuid, IFlowNode fromNode, IFlowNode toNode, int argIndex) - { - if (!FlowCanvass.ContainsKey(canvasGuid)) - { - return false; - } - if (string.IsNullOrEmpty(toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid)) - { - return false; - } - toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = null; - toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值 - - if (OperatingSystem.IsWindows()) - { - UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke( - new NodeConnectChangeEventArgs( - canvasGuid, - fromNode.Guid, - toNode.Guid, - JunctionOfConnectionType.Arg, - argIndex, - ConnectionArgSourceType.GetPreviousNodeData, - NodeConnectChangeEventArgs.ConnectChangeType.Remove))); - } - return true; - } - - - /// - /// 创建节点 - /// - /// - private bool TryAddNode(IFlowNode nodeModel) - { - nodeModel.Guid ??= Guid.NewGuid().ToString(); - NodeModels.TryAdd(nodeModel.Guid, nodeModel); - - - // 如果是触发器,则需要添加到专属集合中 - if (nodeModel is SingleFlipflopNode flipflopNode) - { - var guid = flipflopNode.Guid; - if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid))) - { - FlipflopNodes.Add(flipflopNode); - } - } - return true; - } - - /// - /// 检查连接 - /// - /// 发起连接的起始节点 - /// 要连接的目标节点 - /// 发起连接节点的控制点类型 - /// 被连接节点的控制点类型 - /// - public static (JunctionOfConnectionType,bool) CheckConnect(IFlowNode fromNode, - IFlowNode toNode, - JunctionType fromNodeJunctionType, - JunctionType toNodeJunctionType) - { - var type = JunctionOfConnectionType.None; - var state = false; - if (fromNodeJunctionType == JunctionType.Execute) - { - if (toNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid)) - { - // “方法执行”控制点拖拽到“下一节点”控制点,且不是同一个节点, 添加方法执行关系 - type = JunctionOfConnectionType.Invoke; - state = true; - } - //else if (toNodeJunctionType == JunctionType.ArgData && fromNode.Guid.Equals(toNode.Guid)) - //{ - // // “方法执行”控制点拖拽到“方法入参”控制点,且是同一个节点,则添加获取参数关系,表示生成入参参数时自动从该节点的上一节点获取flowdata - // type = JunctionOfConnectionType.Arg; - // state = true; - //} - } - else if (fromNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid)) - { - // “下一节点”控制点只能拖拽到“方法执行”控制点,且不能是同一个节点 - if (toNodeJunctionType == JunctionType.Execute && !fromNode.Guid.Equals(toNode.Guid)) - { - type = JunctionOfConnectionType.Invoke; - state = true; - } - } - else if (fromNodeJunctionType == JunctionType.ArgData) - { - //if (toNodeJunctionType == JunctionType.Execute && fromNode.Guid.Equals(toNode.Guid)) // 添加获取参数关系 - //{ - // // “方法入参”控制点拖拽到“方法执行”控制点,且是同一个节点,则添加获取参数关系,生成入参参数时自动从该节点的上一节点获取flowdata - // type = JunctionOfConnectionType.Arg; - // state = true; - //} - if(toNodeJunctionType == JunctionType.ReturnData && !fromNode.Guid.Equals(toNode.Guid)) - { - // “”控制点拖拽到“方法返回值”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata - type = JunctionOfConnectionType.Arg; - state = true; - } - } - else if (fromNodeJunctionType == JunctionType.ReturnData) - { - if (toNodeJunctionType == JunctionType.ArgData && !fromNode.Guid.Equals(toNode.Guid)) - { - // “方法返回值”控制点拖拽到“方法入参”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata - type = JunctionOfConnectionType.Arg; - state = true; - } - } - // 剩下的情况都是不符预期的连接行为,忽略。 - return (type,state); - } - - /// - /// 连接节点 - /// - /// 起始节点 - /// 目标节点 - /// 连接关系 - private bool ConnectInvokeOfNode(string canvasGuid, IFlowNode fromNode, IFlowNode toNode, ConnectionInvokeType invokeType) - { - if (fromNode.ControlType == NodeControlType.FlowCall) - { - SereinEnv.WriteLine(InfoType.ERROR, $"流程接口节点不可调用下一个节点。" + - $"{Environment.NewLine}流程节点:{fromNode.Guid}"); - return false; - } - if (!FlowCanvass.ContainsKey(canvasGuid)) - { - return false; - } - if (fromNode is null || toNode is null || fromNode == toNode) - { - return false; - } - - var ToExistOnFrom = true; - var FromExistInTo = true; - ConnectionInvokeType[] ct = [ConnectionInvokeType.IsSucceed, - ConnectionInvokeType.IsFail, - ConnectionInvokeType.IsError, - ConnectionInvokeType.Upstream]; - - if (toNode is SingleFlipflopNode flipflopNode) - { - flowTaskManagement?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被连接的是全局触发器,尝试移除 - } - var isOverwriting = false; - ConnectionInvokeType overwritingCt = ConnectionInvokeType.None; - var isPass = false; - foreach (ConnectionInvokeType ctType in ct) - { - var FToTo = fromNode.SuccessorNodes[ctType].Where(it => it.Guid.Equals(toNode.Guid)).ToArray(); - var ToOnF = toNode.PreviousNodes[ctType].Where(it => it.Guid.Equals(fromNode.Guid)).ToArray(); - ToExistOnFrom = FToTo.Length > 0; - FromExistInTo = ToOnF.Length > 0; - if (ToExistOnFrom && FromExistInTo) - { - if(ctType == invokeType) - { - SereinEnv.WriteLine(InfoType.WARN, $"起始节点已与目标节点存在连接。" + - $"{Environment.NewLine}起始节点:{fromNode.Guid}" + - $"{Environment.NewLine}目标节点:{toNode.Guid}"); - return false; - } - isOverwriting = true; - overwritingCt = ctType; - } - else - { - // 检查是否可能存在异常 - if (!ToExistOnFrom && FromExistInTo) - { - SereinEnv.WriteLine(InfoType.ERROR, $"起始节点不是目标节点的父节点,目标节点却是起始节点的子节点。" + - $"{Environment.NewLine}起始节点:{fromNode.Guid}" + - $"{Environment.NewLine}目标节点:{toNode.Guid}"); - isPass = false; - } - else if (ToExistOnFrom && !FromExistInTo) - { - // - SereinEnv.WriteLine(InfoType.ERROR, $"起始节点不是目标节点的父节点,目标节点却是起始节点的子节点。" + - $"{Environment.NewLine}起始节点:{fromNode.Guid}" + - $"{Environment.NewLine}目标节点:{toNode.Guid}" + - $""); - isPass = false; - } - else - { - isPass = true; - } - } - } - if (isPass) - { - if (isOverwriting) // 需要替换 - { - fromNode.SuccessorNodes[overwritingCt].Remove(toNode); // 从起始节点子分支中移除 - toNode.PreviousNodes[overwritingCt].Remove(fromNode); // 从目标节点父分支中移除 - } - fromNode.SuccessorNodes[invokeType].Add(toNode); // 添加到起始节点的子分支 - toNode.PreviousNodes[invokeType].Add(fromNode); // 添加到目标节点的父分支 - if (OperatingSystem.IsWindows()) - { - - UIContextOperation?.Invoke(() => - OnNodeConnectChange?.Invoke( - new NodeConnectChangeEventArgs( - canvasGuid, - fromNode.Guid, // 从哪个节点开始 - toNode.Guid, // 连接到那个节点 - JunctionOfConnectionType.Invoke, - invokeType, // 连接线的样式类型 - NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 - ))); // 通知UI - } - // Invoke - // GetResult - return true; - } - else - { - return false; - } - - - } - - /// - /// 连接节点参数 - /// - /// - /// - /// - /// - /// - private async Task ConnectArgSourceOfNodeAsync(string canvasGuid, - IFlowNode fromNode, - IFlowNode toNode, - ConnectionArgSourceType connectionArgSourceType, - int argIndex) - { - if (!FlowCanvass.ContainsKey(canvasGuid)) - { - return false; - } - - var toNodeArgSourceGuid = toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid; - var toNodeArgSourceType = toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType; - if(fromNode.Guid == toNodeArgSourceGuid && toNodeArgSourceType == connectionArgSourceType) - { - SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系,此次操作将不会执行" + - $"起始节点:{fromNode.Guid}" + - $"目标节点:{toNode.Guid}" + - $"参数索引:{argIndex}" + - $"参数类型:{connectionArgSourceType}"); - UIContextOperation?.Invoke(() => - OnNodeConnectChange?.Invoke( - new NodeConnectChangeEventArgs( - canvasGuid, - fromNode.Guid, // 从哪个节点开始 - toNode.Guid, // 连接到那个节点 - JunctionOfConnectionType.Arg, - argIndex, // 连接线的样式类型 - connectionArgSourceType, - NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 - ))); // 通知UI - - return true; - } - - if (!string.IsNullOrEmpty(toNodeArgSourceGuid) ) - { - await RemoteConnectAsync(canvasGuid, fromNode, toNode, argIndex); - } - - toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = fromNode.Guid; - toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = connectionArgSourceType; - - UIContextOperation?.Invoke(() => - OnNodeConnectChange?.Invoke( - new NodeConnectChangeEventArgs( - canvasGuid, - fromNode.Guid, // 从哪个节点开始 - toNode.Guid, // 连接到那个节点 - JunctionOfConnectionType.Arg, - argIndex, // 连接线的样式类型 - connectionArgSourceType, - NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 - ))); // 通知UI - return true; - } - - - /// - /// 更改起点节点 - /// - /// 节点所在的画布 - /// 起始节点 - private void SetStartNode(FlowCanvasDetails cavnasModel, IFlowNode newStartNode) - { - var oldNodeGuid = cavnasModel.StartNode?.Guid; - /*if(TryGetNodeModel(oldNodeGuid, out var newStartNodeModel)) - { - newStartNode.IsStart = false; - }*/ - cavnasModel.StartNode = newStartNode; - //newStartNode.IsStart = true; - UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode.Guid))); - - } - - /// - /// 向容器登记缓存的持久化实例 - /// - private void ReRegisterPersistennceInstance() - { - lock (PersistennceInstance) - { - foreach (var kvp in PersistennceInstance) - { - IOC.Register(kvp.Key, () => kvp.Value); - } - } - } - - #endregion - - #region 视觉效果 - - /// - /// 定位节点 - /// - /// - public void NodeLocated(string nodeGuid) - { - if (OperatingSystem.IsWindows()) - { - UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid))); - } - - } - - #endregion - - #region IOC容器相关 - ISereinIOC ISereinIOC.Reset() - { - sereinIOC.Reset(); - ReRegisterPersistennceInstance(); // 重置后重新登记 - return this; - } - - ISereinIOC ISereinIOC.Register(Type type) - { - sereinIOC.Register(type); - return this; - } - ISereinIOC ISereinIOC.Register(Type type, Func getInstance) - { - sereinIOC.Register(type, getInstance); - return this; - } - - ISereinIOC ISereinIOC.Register(Func getInstance) - { - sereinIOC.Register(getInstance); - return this; - } - - ISereinIOC ISereinIOC.Register() - { - sereinIOC.Register(); - return this; - } - - ISereinIOC ISereinIOC.Register(Func getInstance) - { - sereinIOC.Register(getInstance); - return this; - } - - //T ISereinIOC.GetOrRegisterInstantiate() - //{ - // return sereinIOC.GetOrRegisterInstantiate(); - - //} - - //object ISereinIOC.GetOrRegisterInstantiate(Type type) - //{ - // return sereinIOC.GetOrRegisterInstantiate(type); - //} - - object ISereinIOC.Get(Type type) - { - return sereinIOC.Get(type); - - } - T ISereinIOC.Get() - { - return (T)sereinIOC.Get(typeof(T)); - } - //T ISereinIOC.Get(string key) - //{ - // return sereinIOC.Get(key); - //} - - - - //bool ISereinIOC.RegisterInstance(string key, object instance) - //{ - // return sereinIOC.RegisterInstance(key, instance); - //} - - - object ISereinIOC.CreateTempObject(Type type) - { - return sereinIOC.CreateTempObject(type); - } - T ISereinIOC.CreateTempObject() - { - return sereinIOC.CreateTempObject(); - } - ISereinIOC ISereinIOC.Build() - { - sereinIOC.Build(); - return this; - } - - - #endregion - - } - - - - - - - - - - - - } diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs deleted file mode 100644 index a583539..0000000 --- a/NodeFlow/Env/FlowEnvironmentDecorator.cs +++ /dev/null @@ -1,710 +0,0 @@ -using Serein.Library; -using Serein.Library.Api; -using Serein.Library.FlowNode; -using Serein.Library.Utils; -using Serein.NodeFlow.Tool; -using System.Reflection; - -namespace Serein.NodeFlow.Env -{ - /// - /// 自动管理本地与远程的环境 - /// - public class FlowEnvironmentDecorator : IFlowEnvironment, IFlowEnvironmentEvent, ISereinIOC - { - public FlowEnvironmentDecorator() - { - flowEnvironment = new FlowEnvironment(); - // 默认使用本地环境 - currentFlowEnvironment = flowEnvironment; - currentFlowEnvironmentEvent = flowEnvironment; - SereinEnv.SetEnv(currentFlowEnvironment); - } - - /// - /// 本地环境 - /// - private readonly FlowEnvironment flowEnvironment; - - /// - /// 远程环境 - /// - private RemoteFlowEnvironment remoteFlowEnvironment; - - /// - /// 本地环境事件 - /// - private readonly IFlowEnvironmentEvent flowEnvironmentEvent; - - /// - /// 远程环境事件 - /// - private IFlowEnvironmentEvent remoteFlowEnvironmentEvent; - - - /// - /// 管理当前环境 - /// - - private IFlowEnvironment currentFlowEnvironment; - - /// - /// 管理当前环境事件 - /// - private IFlowEnvironmentEvent currentFlowEnvironmentEvent; - - - - private int _loadingProjectFlag = 0; // 使用原子自增代替锁 - - - /// - /// 传入false时,将停止数据通知。传入true时, - /// - /// - public void SetProjectLoadingFlag(bool value) - { - Interlocked.Exchange(ref _loadingProjectFlag, value ? 1 : 0); - } - /// - /// 判断是否正在加载项目 - /// - /// - public bool IsLoadingProject() - { - return Interlocked.CompareExchange(ref _loadingProjectFlag, 1, 1) == 1; - } - - - - - /// - /// 当前环境,用于切换远程与本地环境 - /// - public IFlowEnvironment CurrentEnv { get => currentFlowEnvironment; } - - public UIContextOperation UIContextOperation => currentFlowEnvironment.UIContextOperation; - /// - /// 节点视图模型管理类 - /// - public NodeMVVMManagement NodeMVVMManagement => currentFlowEnvironment.NodeMVVMManagement; - - public ISereinIOC IOC => (ISereinIOC)currentFlowEnvironment; - - - public string EnvName => currentFlowEnvironment.EnvName; - public string ProjectFileLocation => currentFlowEnvironment.EnvName; - - public bool IsGlobalInterrupt => currentFlowEnvironment.IsGlobalInterrupt; - - public bool IsControlRemoteEnv => currentFlowEnvironment.IsControlRemoteEnv; - - /// - /// 信息输出等级 - /// - public InfoClass InfoClass { get => currentFlowEnvironment.InfoClass; set => currentFlowEnvironment.InfoClass = value; } - public RunState FlowState { get => currentFlowEnvironment.FlowState; set => currentFlowEnvironment.FlowState = value; } - //public RunState FlipFlopState { get => currentFlowEnvironment.FlipFlopState; set => currentFlowEnvironment.FlipFlopState = value; } - - public event LoadDllHandler OnDllLoad { - add { currentFlowEnvironmentEvent.OnDllLoad += value; } - remove { currentFlowEnvironmentEvent.OnDllLoad -= value; } - } - - public event ProjectLoadedHandler OnProjectLoaded - { - add { currentFlowEnvironmentEvent.OnProjectLoaded += value; } - remove { currentFlowEnvironmentEvent.OnProjectLoaded -= value; } - } - - /// - /// 项目准备保存 - /// - public event ProjectSavingHandler? OnProjectSaving - { - add { currentFlowEnvironmentEvent.OnProjectSaving += value; } - remove { currentFlowEnvironmentEvent.OnProjectSaving -= value; } - } - - - public event NodeConnectChangeHandler OnNodeConnectChange - { - add { currentFlowEnvironmentEvent.OnNodeConnectChange += value; } - remove { currentFlowEnvironmentEvent.OnNodeConnectChange -= value; } - } - - - public event CanvasCreateHandler OnCanvasCreate - { - add { currentFlowEnvironmentEvent.OnCanvasCreate += value; } - remove { currentFlowEnvironmentEvent.OnCanvasCreate -= value; } - } - public event CanvasRemoveHandler OnCanvasRemove - { - add { currentFlowEnvironmentEvent.OnCanvasRemove += value; } - remove { currentFlowEnvironmentEvent.OnCanvasRemove -= value; } - } - - public event NodeCreateHandler OnNodeCreate - { - add { currentFlowEnvironmentEvent.OnNodeCreate += value; } - remove { currentFlowEnvironmentEvent.OnNodeCreate -= value; } - } - - public event NodeRemoveHandler OnNodeRemove - { - add { currentFlowEnvironmentEvent.OnNodeRemove += value; } - remove { currentFlowEnvironmentEvent.OnNodeRemove -= value; } - } - - public event NodePlaceHandler OnNodePlace - { - add { currentFlowEnvironmentEvent.OnNodePlace += value; } - remove { currentFlowEnvironmentEvent.OnNodePlace -= value; } - } - - public event NodeTakeOutHandler OnNodeTakeOut - { - add { currentFlowEnvironmentEvent.OnNodeTakeOut += value; } - remove { currentFlowEnvironmentEvent.OnNodeTakeOut -= value; } - } - - public event StartNodeChangeHandler OnStartNodeChange - { - add { currentFlowEnvironmentEvent.OnStartNodeChange += value; } - remove { currentFlowEnvironmentEvent.OnStartNodeChange -= value; } - } - - public event FlowRunCompleteHandler OnFlowRunComplete - { - add { currentFlowEnvironmentEvent.OnFlowRunComplete += value; } - remove { currentFlowEnvironmentEvent.OnFlowRunComplete -= value; } - } - - public event MonitorObjectChangeHandler OnMonitorObjectChange - { - add { currentFlowEnvironmentEvent.OnMonitorObjectChange += value; } - remove { currentFlowEnvironmentEvent.OnMonitorObjectChange -= value; } - } - - public event NodeInterruptStateChangeHandler OnNodeInterruptStateChange - { - add { currentFlowEnvironmentEvent.OnNodeInterruptStateChange += value; } - remove { currentFlowEnvironmentEvent.OnNodeInterruptStateChange -= value; } - } - - public event ExpInterruptTriggerHandler OnInterruptTrigger - { - add { currentFlowEnvironmentEvent.OnInterruptTrigger += value; } - remove { currentFlowEnvironmentEvent.OnInterruptTrigger -= value; } - } - - public event IOCMembersChangedHandler OnIOCMembersChanged - { - add { currentFlowEnvironmentEvent.OnIOCMembersChanged += value; } - remove { currentFlowEnvironmentEvent.OnIOCMembersChanged -= value; } - } - - public event NodeLocatedHandler OnNodeLocated - { - add { currentFlowEnvironmentEvent.OnNodeLocated += value; } - remove { currentFlowEnvironmentEvent.OnNodeLocated -= value; } - } - - public event NodeMovedHandler OnNodeMoved - { - add { currentFlowEnvironmentEvent.OnNodeMoved += value; } - remove { currentFlowEnvironmentEvent.OnNodeMoved -= value; } - } - - public event EnvOutHandler OnEnvOut - { - add { currentFlowEnvironmentEvent.OnEnvOut += value; } - remove { currentFlowEnvironmentEvent.OnEnvOut -= value; } - } - - - - - public void ActivateFlipflopNode(string nodeGuid) - { - currentFlowEnvironment.ActivateFlipflopNode(nodeGuid); - } - - /// - /// 增加画布 - /// - /// 画布名称 - /// 宽度 - /// 高度 - /// - public async Task CreateCanvasAsync(string canvasName, int width, int height) - { - return await currentFlowEnvironment.CreateCanvasAsync(canvasName, width, height); - } - - /// - /// 删除画布 - /// - /// 画布Guid - /// - public async Task RemoveCanvasAsync(string canvasGuid) - { - return await currentFlowEnvironment.RemoveCanvasAsync(canvasGuid); - } - - - /// - /// 在两个节点之间创建连接关系 - /// - /// 起始节点Guid - /// 目标节点Guid - /// 起始节点控制点 - /// 目标节点控制点 - /// 决定了方法执行后的后继行为 - public async Task ConnectInvokeNodeAsync(string canvasGuid, - string fromNodeGuid, - string toNodeGuid, - JunctionType fromNodeJunctionType, - JunctionType toNodeJunctionType, - ConnectionInvokeType invokeType) - { - return await currentFlowEnvironment.ConnectInvokeNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, invokeType); - } - - - /// - /// 在两个节点之间创建连接关系 - /// - /// 起始节点Guid - /// 目标节点Guid - /// 起始节点控制点 - /// 目标节点控制点 - /// 决定了方法参数来源 - /// 设置第几个参数 - public async Task ConnectArgSourceNodeAsync(string canvasGuid, - string fromNodeGuid, - string toNodeGuid, - JunctionType fromNodeJunctionType, - JunctionType toNodeJunctionType, - ConnectionArgSourceType argSourceType, - int argIndex) - { - return await currentFlowEnvironment.ConnectArgSourceNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, argSourceType, argIndex); - } - - - /// - /// 连接远程环境并自动切换环境 - /// - /// - /// - /// - /// - public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) - { - // 连接成功,切换远程环境 - (var isConnect, var remoteMsgUtil) = await currentFlowEnvironment.ConnectRemoteEnv(addres, port, token); - if (isConnect) - { - - remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteMsgUtil, this.UIContextOperation); - currentFlowEnvironment = remoteFlowEnvironment; - } - return (isConnect, remoteMsgUtil); - } - - /// - /// 从节点信息集合批量加载节点控件 - /// - /// 节点信息 - /// 需要加载的位置 - /// - public async Task LoadNodeInfosAsync(List nodeInfos) - { - SetProjectLoadingFlag(false); - await currentFlowEnvironment.LoadNodeInfosAsync(nodeInfos); // 装饰器调用 - SetProjectLoadingFlag(true); - } - - public async Task CreateNodeAsync(string canvasGuid, NodeControlType nodeBase, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null) - { - SetProjectLoadingFlag(false); - var result = await currentFlowEnvironment.CreateNodeAsync(canvasGuid, nodeBase, position, methodDetailsInfo); // 装饰器调用 - SetProjectLoadingFlag(true); - return result; - } - - - /// - /// 将节点放置在容器中 - /// - /// - public async Task PlaceNodeToContainerAsync(string canvasGuid, string nodeGuid, string containerNodeGuid) - { - SetProjectLoadingFlag(false); - var result = await currentFlowEnvironment.PlaceNodeToContainerAsync(canvasGuid, nodeGuid, containerNodeGuid); // 装饰器调用 - SetProjectLoadingFlag(true); - return result; - } - - /// - /// 将节点从容器中脱离 - /// - /// - public async Task TakeOutNodeToContainerAsync(string canvasGuid, string nodeGuid) - { - SetProjectLoadingFlag(false); - var result = await currentFlowEnvironment.TakeOutNodeToContainerAsync(canvasGuid,nodeGuid); // 装饰器调用 - SetProjectLoadingFlag(true); - return result; - } - - - public async Task ExitFlowAsync() - { - return await currentFlowEnvironment.ExitFlowAsync(); - } - - public void ExitRemoteEnv() - { - currentFlowEnvironment.ExitRemoteEnv(); - } - - - public async Task GetEnvInfoAsync() - { - return await currentFlowEnvironment.GetEnvInfoAsync(); - } - - - public async Task GetProjectInfoAsync() - { - return await currentFlowEnvironment.GetProjectInfoAsync(); - } - - - public void LoadLibrary(string dllPath) - { - currentFlowEnvironment.LoadLibrary(dllPath); - } - - /// - /// 保存项目 - /// - public void SaveProject() - { - currentFlowEnvironment.SaveProject(); - } - - public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) - { - if (flowEnvInfo is null) return; - SetProjectLoadingFlag(false); - currentFlowEnvironment.LoadProject(flowEnvInfo, filePath); - SetProjectLoadingFlag(true); - } - - public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) - { - currentFlowEnvironment.MonitorObjectNotification(nodeGuid, monitorData, sourceType); - } - - public void MoveNode(string canvasGuid, string nodeGuid, double x, double y) - { - currentFlowEnvironment.MoveNode(canvasGuid, nodeGuid, x, y); - } - - public void NodeLocated(string nodeGuid) - { - currentFlowEnvironment.NodeLocated(nodeGuid); - } - - - public bool TryUnloadLibrary(string assemblyName) - { - return currentFlowEnvironment.TryUnloadLibrary(assemblyName); - } - - /// - /// 设置两个节点某个类型的方法调用关系为优先调用 - /// - /// 起始节点 - /// 目标节点 - /// 连接关系 - /// 是否成功调用 - public async Task SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) - { - return await currentFlowEnvironment.SetConnectPriorityInvoke(fromNodeGuid, toNodeGuid, connectionType); - } - /// - /// 移除方法调用关系 - /// - /// - /// - /// - /// - public async Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) - { - return await currentFlowEnvironment.RemoveConnectInvokeAsync(canvasGuid, fromNodeGuid, toNodeGuid, connectionType); - } - - /// - /// 移除连接节点之间参数传递的关系 - /// - /// 起始节点Guid - /// 目标节点Guid - /// 连接到第几个参数 - /// 参数来源类型 - public async Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex) - { - return await currentFlowEnvironment.RemoveConnectArgSourceAsync(canvasGuid, fromNodeGuid, toNodeGuid, argIndex); - } - - public async Task RemoveNodeAsync(string canvasGuid, string nodeGuid) - { - return await currentFlowEnvironment.RemoveNodeAsync(canvasGuid, nodeGuid); - } - - - //public void SetConsoleOut() - //{ - // currentFlowEnvironment.SetConsoleOut(); - //} - - /// - /// 输出信息 - /// - /// 日志内容 - /// 日志类别 - /// 日志级别 - public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial) - { - currentFlowEnvironment.WriteLine(type, message, @class); - } - - - #region MyRegion -#if false - public async Task AddInterruptExpressionAsync(string key, string expression) - { - return await currentFlowEnvironment.AddInterruptExpressionAsync(key, expression); - } - - - public async Task<(bool, string[])> CheckObjMonitorStateAsync(string key) - { - return await currentFlowEnvironment.CheckObjMonitorStateAsync(key); - } - public async Task GetOrCreateGlobalInterruptAsync() - { - return await currentFlowEnvironment.InterruptNode(); - } - - public void SetMonitorObjState(string key, bool isMonitor) - { - currentFlowEnvironment.SetMonitorObjState(key, isMonitor); - } - - public async Task SetNodeInterruptAsync(string nodeGuid, bool isInterrupt) - { - return await currentFlowEnvironment.SetNodeInterruptAsync(nodeGuid, isInterrupt); - } -#endif - - #endregion - public async Task SetStartNodeAsync(string canvasGuid, string nodeGuid) - { - return await currentFlowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid); - } - - public async Task StartFlowAsync(string[] canvasGuids) - { - return await currentFlowEnvironment.StartFlowAsync(canvasGuids); - } - - - - public async Task StartFlowFromSelectNodeAsync(string startNodeGuid) - { - return await currentFlowEnvironment.StartFlowFromSelectNodeAsync(startNodeGuid); - } - - - - public async Task StartRemoteServerAsync(int port = 7525) - { - await currentFlowEnvironment.StartRemoteServerAsync(port); - } - - public void StopRemoteServer() - { - currentFlowEnvironment.StopRemoteServer(); - } - - public void TerminateFlipflopNode(string nodeGuid) - { - currentFlowEnvironment.TerminateFlipflopNode(nodeGuid); - } - - public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type) - { - currentFlowEnvironment.TriggerInterrupt(nodeGuid, expression, type); - } - /// - /// 设置在UI线程操作的线程上下文 - /// - /// - public void SetUIContextOperation(UIContextOperation uiContextOperation) - { - currentFlowEnvironment.SetUIContextOperation(uiContextOperation); - } - - public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel) - { - return currentFlowEnvironment.TryGetNodeModel(nodeGuid, out nodeModel); - } - public bool TryGetDelegateDetails(string libraryName, string methodName, out DelegateDetails del) - { - return currentFlowEnvironment.TryGetDelegateDetails(libraryName, methodName, out del); - } - - public bool TryGetMethodDetailsInfo(string libraryName, string methodName, out MethodDetailsInfo mdInfo) - { - return currentFlowEnvironment.TryGetMethodDetailsInfo(libraryName, methodName, out mdInfo); - } - - //public void WriteLineObjToJson(object obj) - //{ - // currentFlowEnvironment.WriteLineObjToJson(obj); - //} - - /// - /// (用于远程)通知节点属性变更 - /// - /// 节点Guid - /// 属性路径 - /// 属性值 - /// - public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) - { - if (!IsLoadingProject()) - { - return; - } - if (currentFlowEnvironment.IsControlRemoteEnv) - { - await currentFlowEnvironment.NotificationNodeValueChangeAsync(nodeGuid, path, value); - } - } - - - /// - /// 改变可选参数的数目 - /// - /// 对应的节点Guid - /// true,增加参数;false,减少参数 - /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数) - /// - public async Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex) - { - return await currentFlowEnvironment.ChangeParameter(nodeGuid, isAdd, paramIndex); - } - - #region 流程依赖类库的接口 - - - /// - /// 运行时加载 - /// - /// 文件名 - /// - public bool LoadNativeLibraryOfRuning(string file) - { - return currentFlowEnvironment.LoadNativeLibraryOfRuning(file); - } - - /// - /// 运行时加载指定目录下的类库 - /// - /// 目录 - /// 是否递归加载 - public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true) - { - currentFlowEnvironment.LoadAllNativeLibraryOfRuning(path,isRecurrence); - } - - #endregion - - #region IOC容器 - public ISereinIOC Build() - { - return IOC.Build(); - } - - //public bool RegisterInstance(string key, object instance) - //{ - // return IOC.RegisterInstance(key, instance); - //} - - public object Get(Type type) - { - return IOC.Get(type); - } - - public T Get() - { - return IOC.Get(); - } - - //public T Get(string key) - //{ - // return IOC.Get(key); - //} - - public object CreateTempObject(Type type) - { - return IOC.CreateTempObject(type); - } - - public T CreateTempObject() - { - return IOC.CreateTempObject(); - } - - public ISereinIOC Register(Type type) - { - return IOC.Register(type); - } - - public ISereinIOC Register(Type type, Func getInstance) - { - return IOC.Register(type, getInstance); - } - - public ISereinIOC Register(Func getInstance) - { - return IOC.Register(getInstance); - } - - public ISereinIOC Register(Func getInstance) where TImplementation : TService - { - return IOC.Register(getInstance); - } - - public ISereinIOC Register() where TImplementation : TService - { - return IOC.Register(); - } - - public ISereinIOC Reset() - { - return IOC.Reset(); - } - - - #endregion - - - } -} diff --git a/NodeFlow/Env/LocalFlowEnvironment.cs b/NodeFlow/Env/LocalFlowEnvironment.cs new file mode 100644 index 0000000..210c7d7 --- /dev/null +++ b/NodeFlow/Env/LocalFlowEnvironment.cs @@ -0,0 +1,2213 @@ +using Serein.Library; +using Serein.Library.Api; +using Serein.Library.FlowNode; +using Serein.Library.Utils; +using Serein.Library.Utils.SereinExpression; +using Serein.NodeFlow.Model; +using Serein.NodeFlow.Tool; +using System; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Reactive; +using System.Reflection; +using System.Text; + +namespace Serein.NodeFlow.Env +{ + + + /// + /// 运行环境 + /// + public class LocalFlowEnvironment : IFlowEnvironment, IFlowEnvironmentEvent + { + /// + /// 节点的命名空间 + /// + 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"; + + /// + /// 流程运行环境 + /// + public LocalFlowEnvironment() + { + this.sereinIOC = new SereinIOC(); + this.IsGlobalInterrupt = false; + this.flowTaskManagement = null; + this.sereinIOC.OnIOCMembersChanged += e => + { + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => this?.OnIOCMembersChanged?.Invoke(e)); // 监听IOC容器的注册 + } + + }; + this.FlowLibraryManagement = new FlowLibraryManagement(this); // 实例化类库管理 + this.NodeMVVMManagement = new NodeMVVMManagement(); + #region 注册基本节点类型 + NodeMVVMManagement.RegisterModel(NodeControlType.UI, typeof(SingleUINode)); // 动作节点 + + NodeMVVMManagement.RegisterModel(NodeControlType.Action, typeof(SingleActionNode)); // 动作节点 + NodeMVVMManagement.RegisterModel(NodeControlType.Flipflop, typeof(SingleFlipflopNode)); // 触发器节点 + NodeMVVMManagement.RegisterModel(NodeControlType.ExpOp, typeof(SingleExpOpNode)); // 表达式节点 + NodeMVVMManagement.RegisterModel(NodeControlType.ExpCondition, typeof(SingleConditionNode)); // 条件表达式节点 + //NodeMVVMManagement.RegisterModel(NodeControlType.ConditionRegion, typeof(CompositeConditionNode)); // 条件区域 + NodeMVVMManagement.RegisterModel(NodeControlType.GlobalData, typeof(SingleGlobalDataNode)); // 全局数据节点 + NodeMVVMManagement.RegisterModel(NodeControlType.Script, typeof(SingleScriptNode)); // 脚本节点 + NodeMVVMManagement.RegisterModel(NodeControlType.NetScript, typeof(SingleNetScriptNode)); // 脚本节点 + NodeMVVMManagement.RegisterModel(NodeControlType.FlowCall, typeof(SingleFlowCallNode)); // 流程调用节点 + #endregion + + #region 注册基本服务类 + PersistennceInstance.Add(typeof(FlowInterruptTool), new FlowInterruptTool()); // 缓存流程实例 + PersistennceInstance.Add(typeof(IFlowEnvironment), (LocalFlowEnvironment)this); // 缓存流程实例 + PersistennceInstance.Add(typeof(ISereinIOC), this); // 缓存容器服务 + + ReRegisterPersistennceInstance(); + + #endregion + } + + #region 远程管理 + + private MsgControllerOfServer clientMsgManage; + + /// + /// 表示是否正在控制远程 + /// Local control remote env + /// + public bool IsControlRemoteEnv { get; set; } + + /// + /// 打开远程管理 + /// + /// + public async Task StartRemoteServerAsync(int port = 7525) + { + if (clientMsgManage is null) + { + clientMsgManage = new MsgControllerOfServer(this); + //clientMsgManage = new MsgControllerOfServer(this,"123456"); + } + _ = clientMsgManage.StartRemoteServerAsync(port); + } + + /// + /// 结束远程管理 + /// + public void StopRemoteServer() + { + try + { + clientMsgManage.StopRemoteServer(); + } + catch (Exception ex) + { + SereinEnv.WriteLine(InfoType.ERROR, "结束远程管理异常:" + ex); + } + } + + #endregion + + #region 环境运行事件 + /// + /// 加载Dll + /// + public event LoadDllHandler? OnDllLoad; + + /// + /// 移除DLL + /// + public event RemoteDllHandler? OnDllRemote; + + /// + /// 项目加载完成 + /// + public event ProjectLoadedHandler? OnProjectLoaded; + + /// + /// 项目准备保存 + /// + public event ProjectSavingHandler? OnProjectSaving; + + /// + /// 节点连接属性改变事件 + /// + public event NodeConnectChangeHandler? OnNodeConnectChange; + + /// + /// 节点创建事件 + /// + public event NodeCreateHandler? OnNodeCreate; + + /// + /// 移除节点事件 + /// + public event NodeRemoveHandler? OnNodeRemove; + + /// + /// 节点放置事件 + /// + public event NodePlaceHandler OnNodePlace; + + /// + /// 节点取出事件 + /// + public event NodeTakeOutHandler OnNodeTakeOut; + + /// + /// 起始节点变化事件 + /// + public event StartNodeChangeHandler? OnStartNodeChange; + + /// + /// 流程运行完成事件 + /// + public event FlowRunCompleteHandler? OnFlowRunComplete; + + /// + /// 被监视的对象改变事件 + /// + public event MonitorObjectChangeHandler? OnMonitorObjectChange; + + /// + /// 节点中断状态改变事件 + /// + public event NodeInterruptStateChangeHandler? OnNodeInterruptStateChange; + + /// + /// 节点触发了中断 + /// + public event ExpInterruptTriggerHandler? OnInterruptTrigger; + + /// + /// 容器改变 + /// + public event IOCMembersChangedHandler? OnIOCMembersChanged; + + /// + /// 节点需要定位 + /// + public event NodeLocatedHandler? OnNodeLocated; + + /// + /// 节点移动了(远程插件) + /// + public event NodeMovedHandler? OnNodeMoved; + + /// + /// 运行环境输出 + /// + public event EnvOutHandler? OnEnvOut; + + /// + /// 本地环境添加了画布 + /// + public event CanvasCreateHandler OnCanvasCreate; + + /// + /// 本地环境移除了画布 + /// + public event CanvasRemoveHandler OnCanvasRemove; + + #endregion + + #region 属性 + + /// + /// 当前环境 + /// + public IFlowEnvironment CurrentEnv { get => this; } + + /// + /// UI线程操作类 + /// + public UIContextOperation UIContextOperation { get; private set; } + + /// + /// 节点视图模型管理类 + /// + public NodeMVVMManagement NodeMVVMManagement { get; set; } + + /// + /// 信息输出等级 + /// + public InfoClass InfoClass { get ; set ; } = InfoClass.Trivial; + + /// + /// 如果没有全局触发器,且没有循环分支,流程执行完成后自动为 Completion 。 + /// + public RunState FlowState { get; set; } = RunState.NoStart; + /// + /// 如果全局触发器还在运行,则为 Running 。 + /// + public RunState FlipFlopState { get; set; } = RunState.NoStart; + + /// + /// 环境名称 + /// + public string EnvName { get; set; } = SpaceName; + + + /// + /// 本地加载的项目文件路径 + /// + public string ProjectFileLocation { get; set; } = string.Empty; + + /// + /// 是否全局中断 + /// + public bool IsGlobalInterrupt { get; set; } + + ///// + ///// 流程中断器 + ///// + //public ChannelFlowInterrupt ChannelFlowInterrupt { get; set; } + + /// + /// 单例模式IOC容器,内部维护了一个实例字典,默认使用类型的FullName作为Key,如果以“接口-实现类”的方式注册,那么将使用接口类型的FullName作为Key。 + /// 当某个类型注册绑定成功后,将不会因为其它地方尝试注册相同类型的行为导致类型被重新创建。 + /// + public ISereinIOC IOC + { + get + { + if(ioc is null) + { + ioc = new SereinIOC(); + } + return ioc; + } + } + + #endregion + + #region 私有变量 + + /// + /// IOC容器 + /// + private ISereinIOC ioc; + + /// + /// 通过程序集名称管理动态加载的程序集,用于节点创建提供方法描述,流程运行时提供Emit委托 + /// + private readonly FlowLibraryManagement FlowLibraryManagement; + + /// + /// IOC对象容器管理 + /// + private readonly SereinIOC sereinIOC; + + /// + /// 本地运行环境缓存的持久化实例 + /// + private Dictionary PersistennceInstance { get; } = new Dictionary(); + + /// + /// 环境加载的节点集合 + /// Node Guid - Node Model + /// + private Dictionary NodeModels { get; } = []; + + /// + /// 运行环境加载的画布集合 + /// + private Dictionary FlowCanvass { get; } = []; + + /// + /// 存放触发器节点(运行时全部调用) + /// + private List FlipflopNodes { get; } = []; + + + /// + /// 流程任务管理 + /// + private FlowWorkManagement? flowTaskManagement; + + #endregion + + #region 环境对外接口 + + /// + /// 输出信息 + /// + /// 日志内容 + /// 日志类别 + /// 日志级别 + public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial) + { + if (@class >= this.InfoClass) + { + OnEnvOut?.Invoke(type, message); + } + //Console.WriteLine($"{DateTime.UtcNow} [{type}] : {message}{Environment.NewLine}"); + + } + + /// + /// 异步运行 + /// + /// + public async Task StartFlowAsync(string[] canvasGuids) + { + #region 校验参数 + HashSet guids = new HashSet(); + bool isBreak = false; + foreach (var canvasGuid in canvasGuids) + { + if (guids.Contains(canvasGuid)) + { + SereinEnv.WriteLine(InfoType.WARN, $"画布重复,停止运行。{canvasGuid}"); + isBreak = true; + } + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{canvasGuid}"); + isBreak = true; + } + var count = NodeModels.Values.Count(n => n.CanvasDetails.Guid.Equals(canvasGuid)); + if(count == 0) + { + SereinEnv.WriteLine(InfoType.WARN, $"画布没有节点,停止运行。{canvasGuid}"); + isBreak = true; + } + else + { + guids.Add(canvasGuid); + } + } + if (isBreak) + { + guids.Clear(); + return false; + } + #endregion + + + #region 初始化每个画布的数据,转换为流程任务 + Dictionary flowTasks = []; + foreach (var guid in guids) + { + if (!TryGetCanvasModel(guid, out var canvasModel)) + { + SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{guid}"); + return false; + } + var ft = new FlowTask(); + ft.GetNodes = () => NodeModels.Values.Where(node => node.CanvasDetails.Guid.Equals(guid)).ToList(); + if (canvasModel.StartNode.Guid is null) + { + SereinEnv.WriteLine(InfoType.WARN, $"画布不存在起始节点,将停止运行。{guid}"); + return false; + } + ft.GetStartNode = () => canvasModel.StartNode; + flowTasks.Add(guid, ft); + } + #endregion + + + + IOC.Reset(); + IOC.Register(); // 注册脚本接口 + + var flowTaskOptions = new FlowWorkOptions + { + Environment = this, // 流程 + Flows = flowTasks, + FlowContextPool = new ObjectPool(() => new DynamicContext(this)), // 上下文对象池 + AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(), // 需要自动实例化的类型 + InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init), + LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading), + ExitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit), + }; + + + + flowTaskManagement = new FlowWorkManagement(flowTaskOptions); + var cts = new CancellationTokenSource(); + try + { + var t = await flowTaskManagement.RunAsync(cts.Token); + } + catch (Exception ex) + { + SereinEnv.WriteLine(ex); + } + finally + { + + SereinEnv.WriteLine(InfoType.INFO, $"流程运行完毕{Environment.NewLine}"); ; + } + flowTaskOptions = null; + return true; + + + } + + + /// + /// 从选定节点开始运行 + /// + /// + /// + public async Task StartFlowFromSelectNodeAsync(string startNodeGuid) + { + + if (flowTaskManagement is null) + { + SereinEnv.WriteLine(InfoType.ERROR, "没有启动流程,无法运行单个节点"); + return false; + } + if (true || FlowState == RunState.Running || FlipFlopState == RunState.Running) + { + + if (!TryGetNodeModel(startNodeGuid,out var nodeModel) || nodeModel is SingleFlipflopNode) + { + return false; + } + await flowTaskManagement.StartFlowInSelectNodeAsync(this, nodeModel); + return true; + } + else + { + return false; + } + } + + /*/// + /// 单独运行一个节点 + /// + /// + /// + public async Task InvokeNodeAsync(IDynamicContext context, string nodeGuid) + { + object result = Unit.Default; + if (this.NodeModels.TryGetValue(nodeGuid, out var model)) + { + CancellationTokenSource cts = new CancellationTokenSource(); + result = await model.ExecutingAsync(context, cts.Token); + cts?.Cancel(); + } + return result; + }*/ + + /// + /// 结束流程 + /// + public Task ExitFlowAsync() + { + flowTaskManagement?.Exit(); + UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs())); + IOC.Reset(); + flowTaskManagement = null; + GC.Collect(); + return Task.FromResult(true); + } + + /// + /// 激活全局触发器 + /// + /// + public void ActivateFlipflopNode(string nodeGuid) + { + if(!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return; + } + if (nodeModel is null) return; + if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 + { + if (FlowState != RunState.Completion + && flipflopNode.NotExitPreviousNode()) // 正在运行,且该触发器没有上游节点 + { + _ = flowTaskManagement.RunGlobalFlipflopAsync(this, flipflopNode);// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中 + + } + } + } + + /// + /// 关闭全局触发器 + /// + /// + public void TerminateFlipflopNode(string nodeGuid) + { + if(!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return; + } + if (nodeModel is null) return; + if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 + { + flowTaskManagement.TerminateGlobalFlipflopRuning(flipflopNode); + } + } + + /// + /// 获取当前环境信息(远程连接) + /// + /// + public async Task GetEnvInfoAsync() + { + // 获取所有的程序集对应的方法信息(程序集相关的数据) + var libraryMdss = this.FlowLibraryManagement.GetAllLibraryMds().ToArray(); + // 获取当前项目的信息(节点相关的数据) + var project = await GetProjectInfoAsync(); // 远程连接获取远程环境项目信息 + SereinEnv.WriteLine(InfoType.INFO, "已将当前环境信息发送到远程客户端"); + return new FlowEnvInfo + { + Project = project, // 项目信息 + LibraryMds = libraryMdss, // 环境方法 + }; + } + + + /// + /// 保存项目 + /// + public void SaveProject() + { + var project = GetProjectInfoAsync().GetAwaiter().GetResult(); + OnProjectSaving?.Invoke(new ProjectSavingEventArgs(project)); + } + + /// + /// 加载项目文件 + /// + /// 环境信息 + /// + public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) + { + this.ProjectFileLocation = filePath; + var projectData = flowEnvInfo.Project; + // 加载项目配置文件 + var dllPaths = projectData.Librarys.Select(it => it.FilePath).ToList(); + List methodDetailss = []; + + // 遍历依赖项中的特性注解,生成方法详情 + foreach (var dllPath in dllPaths) + { + string cleanedRelativePath = dllPath.TrimStart('.', '\\'); + var tmpPath = Path.Combine(filePath, cleanedRelativePath); + var dllFilePath = Path.GetFullPath(tmpPath); + LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集 + } + + + + _ = Task.Run( async () => + { + // 加载画布 + foreach (var canvasInfo in projectData.Canvass) + { + LoadCanvas(canvasInfo); + } + await LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息 + + // 加载画布 + foreach (var canvasInfo in projectData.Canvass) + { + await SetStartNodeAsync(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点 + } + //await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点 + }); + + } + + /// + /// 加载远程环境 + /// + /// 远程环境地址 + /// 远程环境端口 + /// 密码 + public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) + { + if (IsControlRemoteEnv) + { + await Console.Out.WriteLineAsync($"当前已经连接远程环境"); + return (false, null); + } + // 没有连接远程环境,可以重新连接 + + var controlConfiguration = new RemoteMsgUtil.ControlConfiguration + { + Addres = addres, + Port = port, + Token = token, + ThemeJsonKey = LocalFlowEnvironment.ThemeKey, + MsgIdJsonKey = LocalFlowEnvironment.MsgIdKey, + DataJsonKey = LocalFlowEnvironment.DataKey, + }; + var remoteMsgUtil = new RemoteMsgUtil(controlConfiguration); + var result = await remoteMsgUtil.ConnectAsync(); + if (!result) + { + await Console.Out.WriteLineAsync("连接失败,请检查地址与端口是否正确"); + return (false, null); + } + await Console.Out.WriteLineAsync("连接成功,开始验证Token"); + IsControlRemoteEnv = true; + return (true, remoteMsgUtil); + } + + /// + /// 退出远程环境 + /// + public void ExitRemoteEnv() + { + IsControlRemoteEnv = false; + } + + /// + /// 序列化当前项目的依赖信息、节点信息 + /// + /// + public async Task GetProjectInfoAsync() + { + var projectData = new SereinProjectData() + { + Librarys = this.FlowLibraryManagement.GetAllLibraryInfo().ToArray(), + Nodes = NodeModels.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(), + Canvass = FlowCanvass.Values.Select(canvas => canvas.ToInfo()).ToArray(), + //StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid, + }; + + return projectData; + } + + + /// + /// 从文件路径中加载DLL + /// + /// + /// + public void LoadLibrary(string dllPath) + { + + try + { + #region 检查是否已经加载本地依赖 + var thisAssembly = typeof(IFlowEnvironment).Assembly; + var thisAssemblyName = thisAssembly.GetName().Name; + if (!string.IsNullOrEmpty(thisAssemblyName) && FlowLibraryManagement.GetLibraryMdsOfAssmbly(thisAssemblyName).Count == 0) + { + var tmp = FlowLibraryManagement.LoadLibraryOfPath(thisAssembly.Location); + UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(tmp.Item1, tmp.Item2))); // 通知UI创建dll面板显示 + } + + #endregion + + (var libraryInfo, var mdInfos) = FlowLibraryManagement.LoadLibraryOfPath(dllPath); + if (mdInfos.Count > 0) + { + UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示 + } + } + catch (Exception ex) + { + SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件:{ex}"); + } + } + + /// + /// 加载本地程序集 + /// + /// + public void LoadLibrary(FlowLibrary flowLibrary) + { + try + { + (var libraryInfo, var mdInfos) = FlowLibraryManagement.LoadLibraryOfPath(flowLibrary); + if (mdInfos.Count > 0) + { + UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示 + } + } + catch (Exception ex) + { + SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件:{ex}"); + } + + } + + + /// + /// 移除DLL + /// + /// + /// + public bool TryUnloadLibrary(string assemblyName) + { + // 获取与此程序集相关的节点 + var groupedNodes = NodeModels.Values.Where(node => !string.IsNullOrWhiteSpace(node.MethodDetails.AssemblyName) && node.MethodDetails.AssemblyName.Equals(assemblyName)).ToArray(); + if (groupedNodes.Length == 0) + { + var isPass = FlowLibraryManagement.UnloadLibrary(assemblyName); + return isPass; + } + else + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(); + for (int i = 0; i < groupedNodes.Length; i++) + { + IFlowNode? node = groupedNodes[i]; + sb.AppendLine($"{i} => {node.Guid}"); + } + SereinEnv.WriteLine(InfoType.ERROR, $"无法卸载[{assemblyName}]程序集,因为这些节点依赖于此程序集:{sb.ToString()}"); + + return false; + } + + //var mds = FlowLibraryManagement.GetLibraryMdsOfAssmbly(assemblyName); + //if(mds.Count > 0) + //{ + + + //} + //else + //{ + // return true; + //} + + //var library = LibraryInfos.Values.FirstOrDefault(nl => assemblyName.Equals(nl.AssemblyName)); + //if (library is null) + //{ + // return false; + //} + //var groupedNodes = NodeModels.Values + // .Where(node => node.MethodDetails is not null) + // .ToArray() + // .GroupBy(node => node.MethodDetails?.MethodName) + // .ToDictionary( + // key => key.Key, + // group => group.Count()); + + + //if (NodeModels.Count == 0) + //{ + // return true; // 当前无节点,可以直接删除 + //} + + //if (MethodDetailsOfLibraryInfos.TryGetValue(library, out var mds)) // 存在方法 + //{ + // foreach (var md in mds) + // { + // if (groupedNodes.TryGetValue(md.MethodName, out int count)) + // { + // if (count > 0) + // { + // return false; // 创建过相关的节点,无法移除 + // } + // } + // } + // // 开始移除相关信息 + // foreach (var md in mds) + // { + // MethodDetailss.TryRemove(md.MethodName, out _); + // } + // MethodDetailsOfLibraryInfos.TryRemove(library, out _); + // return true; + //} + //else + //{ + // return true; + //} + } + + private int _addCanvasCount = 0; + + /// + /// 增加画布 + /// + /// 画布名称 + /// 宽度 + /// 高度 + /// + public async Task CreateCanvasAsync(string canvasName, int width, int height) + { + var info = new FlowCanvasDetailsInfo() + { + Guid = Guid.NewGuid().ToString(), + Height = height, + Width = width, + ViewX = 0, + ViewY = 0, + ScaleY = 1, + ScaleX = 1, + Name = !string.IsNullOrWhiteSpace(canvasName) ? canvasName : $"流程图{_addCanvasCount++}", + }; + var model = LoadCanvas(info); + return info; + } + + private FlowCanvasDetails LoadCanvas(FlowCanvasDetailsInfo info) + { + var model = new FlowCanvasDetails(this); + model.LoadInfo(info); + FlowCanvass.Add(model.Guid, model); + UIContextOperation?.Invoke(() => + { + OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model)); + }); + return model; + } + + /// + /// 删除画布 + /// + /// 画布Guid + /// + public async Task RemoveCanvasAsync(string canvasGuid) + { + + if (!FlowCanvass.TryGetValue(canvasGuid, out var model)) + { + return false; + } + var count = NodeModels.Values.Count(node => node.CanvasDetails.Guid.Equals(canvasGuid)); + if(count > 0) + { + SereinEnv.WriteLine(InfoType.WARN, "无法删除具有节点的画布"); + return false; + } + if (FlowCanvass.Remove(canvasGuid)) + { + UIContextOperation?.Invoke(() => + { + OnCanvasRemove.Invoke(new CanvasRemoveEventArgs(canvasGuid)); + }); + return true; + } + + return false; + } + + + /// + /// 从节点信息集合批量加载节点控件 + /// + /// 节点信息 + /// + /// + public async Task LoadNodeInfosAsync(List nodeInfos) + { + #region 从NodeInfo创建NodeModel + // 流程接口节点最后才创建 + + List flowCallNodeInfos = []; + foreach (NodeInfo? nodeInfo in nodeInfos) + { + if(nodeInfo.Type == nameof(NodeControlType.FlowCall)) + { + flowCallNodeInfos.Add(nodeInfo); + } + else + { + if (!CreateNodeFromNodeInfo(nodeInfo)) + { + SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}"); + continue; + } + } + } + + // 创建流程接口节点 + foreach (NodeInfo? nodeInfo in flowCallNodeInfos) + { + if (!CreateNodeFromNodeInfo(nodeInfo)) + { + SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}"); + continue; + } + } + #endregion + + #region 重新放置节点 + + List needPlaceNodeInfos = []; + foreach (NodeInfo? nodeInfo in nodeInfos) + { + if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) && + NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode)) + { + needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点 + } + } + + foreach (NodeInfo nodeInfo in needPlaceNodeInfos) + { + if (NodeModels.TryGetValue(nodeInfo.Guid, out var nodeModel) && + NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var containerNode) + && containerNode is INodeContainer nodeContainer) + { + var result = nodeContainer.PlaceNode(nodeModel); + if (result) + { + UIContextOperation?.Invoke(() => OnNodePlace?.Invoke( + new NodePlaceEventArgs(nodeInfo.CanvasGuid, nodeModel.Guid, containerNode.Guid))); + } + + + } + } + #endregion + + #region 确定节点之间的方法调用关系 + foreach (var nodeInfo in nodeInfos) + { + var canvasGuid = nodeInfo.CanvasGuid; + if (!TryGetNodeModel(nodeInfo.Guid, out var fromNodeModel)) + { + return; + } + if (fromNodeModel is null) continue; + List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), + (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), + (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes), + (ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)]; + foreach ((ConnectionInvokeType connectionType, string[] toNodeGuids) item in allToNodes) + { + // 遍历当前类型分支的节点(确认连接关系) + foreach (var toNodeGuid in item.toNodeGuids) + { + if (!TryGetNodeModel(toNodeGuid, out var toNodeModel)) + { + return; + } + if (toNodeModel is null) { + // 防御性代码,加载正常保存的项目文件不会进入这里 + continue; + }; + var isSuccessful = ConnectInvokeOfNode(canvasGuid, fromNodeModel, toNodeModel, item.connectionType); // 加载时确定节点间的连接关系 + } + } + + + //List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), + // (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), + // (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes), + // (ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)]; + + //List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0) + // .Select(info => (info.connectionType, + // info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid]) + // .ToArray())) + // .ToList(); + // 遍历每种类型的节点分支(四种) + //foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in nodeInfo) + //{ + // // 遍历当前类型分支的节点(确认连接关系) + // foreach (var toNode in item.toNodes) + // { + // _ = ConnectInvokeOfNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系 + // } + //} + } + #endregion + + #region 确定节点之间的参数调用关系 + foreach (var toNode in NodeModels.Values) + { + var canvasGuid = toNode.CanvasDetails.Guid; + if (toNode.MethodDetails.ParameterDetailss == null) + { + continue; + } + for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++) + { + var pd = toNode.MethodDetails.ParameterDetailss[i]; + if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid) + && NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode)) + { + + await ConnectArgSourceOfNodeAsync(canvasGuid, fromNode, toNode, pd.ArgDataSourceType, pd.Index); + } + } + } + #endregion + + + UIContextOperation?.Invoke(() => + { + OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); + }); + + return; + } + + /// + /// 流程正在运行时创建节点 + /// + /// 所属画布 + /// 所属类型 + /// 所处位置 + /// 如果是表达式节点条件节点,该项为null + public Task CreateNodeAsync(string canvasGuid, + NodeControlType nodeControlType, + PositionOfUI position, + MethodDetailsInfo? methodDetailsInfo = null) + { + if (!TryGetCanvasModel(canvasGuid,out var canvasModel)) + { + return Task.FromResult(null); + } + IFlowNode? nodeModel; + if (methodDetailsInfo is null + || string.IsNullOrEmpty(methodDetailsInfo.AssemblyName) + || string.IsNullOrEmpty(methodDetailsInfo.MethodName)) + { + nodeModel = FlowNodeExtension.CreateNode(this, nodeControlType); // 加载基础节点 + } + else + { + if (FlowLibraryManagement.TryGetMethodDetails(methodDetailsInfo.AssemblyName, // 创建节点 + methodDetailsInfo.MethodName, + out var methodDetails)) + { + nodeModel = FlowNodeExtension.CreateNode(this, nodeControlType, methodDetails); // 一般的加载节点方法 + } + else + { + return Task.FromResult(null); + } + } + nodeModel.CanvasDetails = canvasModel; + canvasModel.Nodes.Add(nodeModel); // 节点与画布互相绑定 + TryAddNode(nodeModel); + nodeModel.Position = position; // 设置位置 + + // 通知UI更改 + UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(canvasGuid, nodeModel, position))); + var nodeInfo = nodeModel.ToInfo(); + return Task.FromResult(nodeInfo); + } + + + /// + /// 将节点放置在容器中 + /// + /// + public Task PlaceNodeToContainerAsync(string canvasGuid, + string nodeGuid, string containerNodeGuid) + { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return Task.FromResult(false); + } + // 获取目标节点与容器节点 + if (!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return Task.FromResult(false); + } + if (nodeModel.ContainerNode is INodeContainer tmpContainer) + { + SereinEnv.WriteLine(InfoType.WARN, $"节点放置失败,节点[{nodeGuid}]已经放置于容器节点[{((IFlowNode)tmpContainer).Guid}]"); + return Task.FromResult(false); + } + + if (!TryGetNodeModel(containerNodeGuid, out var containerNode)) + { + return Task.FromResult(false); + } + if (containerNode is not INodeContainer nodeContainer) return Task.FromResult(false); + + var result = nodeContainer.PlaceNode(nodeModel); // 放置在容器节点 + if (result) + { + UIContextOperation?.Invoke(() => + { + OnNodePlace?.Invoke(new NodePlaceEventArgs(canvasGuid, nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置 + }); + } + return Task.FromResult(result); + + } + + /// + /// 将节点从容器节点中脱离 + /// + /// + public Task TakeOutNodeToContainerAsync(string canvasGuid, + string nodeGuid) + { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return Task.FromResult(false); + } + // 获取目标节点与容器节点 + if (!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return Task.FromResult(false); + } + if (nodeModel.ContainerNode is not INodeContainer nodeContainer) + { + return Task.FromResult(false); + } + var result = nodeContainer.TakeOutNode(nodeModel); // 从容器节点取出 + if (result) + { + UIContextOperation?.Invoke(() => + { + OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(canvasGuid, nodeGuid)); // 重新放置在画布上 + }); + } + return Task.FromResult(result); + + + } + + + + /// + /// 移除节点 + /// + /// + /// + public async Task RemoveNodeAsync(string canvasGuid, string nodeGuid) + { + if (!TryGetCanvasModel(canvasGuid,out var canvasModel)) + { + return false; + } + if (!TryGetNodeModel(nodeGuid, out var remoteNode)) + { + return false; + } + + if (remoteNode is SingleFlipflopNode flipflopNode) + { + flowTaskManagement?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被移除的是全局触发器,尝试从启动器移除 + } + + remoteNode.Remove(); // 调用节点的移除方法 + + // 遍历所有前置节点,从那些前置节点中的后继节点集合移除该节点 + foreach (var pnc in remoteNode.PreviousNodes) + { + var pCType = pnc.Key; // 连接类型 + for (int i = 0; i < pnc.Value.Count; i++) + { + IFlowNode? pNode = pnc.Value[i]; + pNode.SuccessorNodes[pCType].Remove(remoteNode); + + UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs( + canvasGuid, + pNode.Guid, + remoteNode.Guid, + JunctionOfConnectionType.Invoke, + pCType, // 对应的连接关系 + NodeConnectChangeEventArgs.ConnectChangeType.Remove))); // 通知UI + + } + } + + + if(remoteNode.ControlType == NodeControlType.FlowCall) + { + + } + else + { + // 遍历所有后继节点,从那些后继节点中的前置节点集合移除该节点 + foreach (var snc in remoteNode.SuccessorNodes) + { + var connectionType = snc.Key; // 连接类型 + for (int i = 0; i < snc.Value.Count; i++) + { + IFlowNode? toNode = snc.Value[i]; + + await RemoteConnectAsync(canvasGuid, remoteNode, toNode, connectionType); + + } + } + + } + + + + // 从集合中移除节点,解除与画布的绑定关系 + NodeModels.Remove(nodeGuid); + UIContextOperation?.Invoke(() => canvasModel.Nodes.Remove(remoteNode)); + + UIContextOperation?.Invoke(() => OnNodeRemove?.Invoke(new NodeRemoveEventArgs(canvasGuid, nodeGuid))); + return true; + } + + /// + /// 连接节点,创建方法调用关系 + /// + /// 起始节点 + /// 目标节点 + /// 起始节点控制点 + /// 目标节点控制点 + /// 连接关系 + public Task ConnectInvokeNodeAsync(string canvasGuid, + string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionInvokeType invokeType) + { + + // 获取起始节点与目标节点 + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return Task.FromResult(false); + } + + if (fromNode is null || toNode is null) return Task.FromResult(false); + (var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType); + if (!state) + { + SereinEnv.WriteLine(InfoType.WARN, "出现非预期的连接行为"); + return Task.FromResult(false); // 出现不符预期的连接行为,忽略此次连接行为 + } + + if(type == JunctionOfConnectionType.Invoke) + { + if (fromNodeJunctionType == JunctionType.Execute) + { + // 如果 起始控制点 是“方法调用”,需要反转 from to 节点 + (fromNode, toNode) = (toNode, fromNode); + } + // 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点 + state = ConnectInvokeOfNode(canvasGuid, fromNode, toNode, invokeType); // 本地环境进行连接 + } + return Task.FromResult(state); + + } + + + /// + /// 设置两个节点某个类型的方法调用关系为优先调用 + /// + /// 起始节点 + /// 目标节点 + /// 连接关系 + /// 是否成功调用 + public Task SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) + { + // 获取起始节点与目标节点 + if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return Task.FromResult(false); + } + if (fromNode is null || toNode is null) return Task.FromResult(false); + if ( fromNode.SuccessorNodes.TryGetValue(connectionType, out var nodes)) + { + var idx = nodes.IndexOf(toNode); + if (idx > -1) + { + nodes.RemoveAt(idx); + nodes.Insert(0, toNode); + return Task.FromResult(true); + } + } + return Task.FromResult(false); + } + + /// + /// 移除连接节点之间方法调用的关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 连接关系 + /// + public async Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) + { + // 获取起始节点与目标节点 + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return false; + } + if (fromNode is null || toNode is null) return false; + + var result = await RemoteConnectAsync(canvasGuid, fromNode, toNode, connectionType); + return result; + } + + /// + /// 创建节点之间的参数来源关系 + /// + /// 起始节点 + /// 目标节点 + /// 起始节点控制点(result控制点) + /// 目标节点控制点(argData控制点) + /// 目标节点的第几个参数 + /// 调用目标节点对应方法时,对应参数来源类型 + /// + public async Task ConnectArgSourceNodeAsync(string canvasGuid, + string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionArgSourceType connectionArgSourceType, + int argIndex) + { + + // 获取起始节点与目标节点 + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return false; + } + if (fromNode is null || toNode is null) return false; + (var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType); + if (!state) + { + SereinEnv.WriteLine(InfoType.WARN, "出现非预期的连接行为"); + return false; // 出现不符预期的连接行为,忽略此次连接行为 + } + + if (type == JunctionOfConnectionType.Arg) + { + // 从起始节点“返回值”控制点,连接到目标节点“方法入参”控制点 + if (fromNodeJunctionType == JunctionType.ArgData) + { + // 如果 起始控制点 是“方法入参”,需要反转 from to 节点 + (fromNode, toNode) = (toNode, fromNode); + } + + // 确定方法入参关系 + state = await ConnectArgSourceOfNodeAsync(canvasGuid, fromNode, toNode, connectionArgSourceType, argIndex); // 本地环境进行连接 + } + return state; + + } + + + /// + /// 移除连接节点之间参数传递的关系 + /// + /// 起始节点Guid + /// 目标节点Guid + /// 连接到第几个参数 + public async Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex) + { + // 获取起始节点与目标节点 + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + { + return false; + } + if (fromNode is null || toNode is null) return false; + var result = await RemoteConnectAsync(canvasGuid, fromNode, toNode, argIndex); + return result; + } + + + /// + /// 获取方法描述 + /// + + public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo? mdInfo) + { + var isPass = FlowLibraryManagement.TryGetMethodDetails(assemblyName, methodName, out var md); + if (!isPass || md is null) + { + mdInfo = null; + return false; + } + else + { + mdInfo = md?.ToInfo(); + return true; + } + } + + /// + /// 通过方法名称获取对应的Emit委托 + /// 方法无入参时需要传入空数组,void方法自动返回null + /// 普通方法:Func<object,object[],object> + /// 异步方法:Func<object,object[],Task> + /// 异步有返回值方法:Func<object,object[],Task<object>> + /// + /// + /// + /// + public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails? delegateDetails) + { + return FlowLibraryManagement.TryGetDelegateDetails(assemblyName, methodName, out delegateDetails); + } + + /// + /// 设置在UI线程操作的线程上下文 + /// + /// + public void SetUIContextOperation(UIContextOperation uiContextOperation) + { + if(uiContextOperation is not null) + { + this.UIContextOperation = uiContextOperation; + PersistennceInstance[typeof(UIContextOperation)] = uiContextOperation; // 缓存封装好的UI线程上下文 + } + + + + } + + /// + public void UseExternalIOC(ISereinIOC ioc) + { + this.ioc = ioc; // 设置IOC容器 + } + + /// + /// 移动了某个节点(远程插件使用) + /// + /// + /// + /// + public void MoveNode(string canvasGuid, string nodeGuid, double x, double y) + { + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return; + } + nodeModel.Position.X = x; + nodeModel.Position.Y = y; + UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y))); + + } + + /// + /// 设置起点控件 + /// + /// 画布 + /// 节点Guid + public Task SetStartNodeAsync(string canvasGuid, string newNodeGuid) + { + if (!TryGetCanvasModel(canvasGuid, out var canvasModel) || !TryGetNodeModel(newNodeGuid, out var newStartNodeModel)) + { + return Task.FromResult(string.Empty); + } + SetStartNode(canvasModel, newStartNodeModel); + return Task.FromResult(canvasModel.StartNode.Guid ?? string.Empty); + } + + /// + /// 启动器调用,运行到某个节点时触发了监视对象的更新(对象预览视图将会自动更新) + /// + /// + /// + /// + public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) + { + OnMonitorObjectChange?.Invoke(new MonitorObjectEventArgs(nodeGuid, monitorData, sourceType)); + } + + /// + /// 启动器调用,节点触发了中断。 + /// + /// 节点 + /// 表达式 + /// 类型,0用户主动的中断,1表达式中断 + public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type) + { + OnInterruptTrigger?.Invoke(new InterruptTriggerEventArgs(nodeGuid, expression, type)); + } + + + ///// + ///// 环境执行中断 + ///// + ///// + //public async Task InterruptNode() + //{ + // IsGlobalInterrupt = true; + // var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(EnvName); + // return result; + //} + + /// + /// 记录节点更改数据,防止重复更改 + /// + public HashSet<(string, string, object)> NodeValueChangeLogger = new HashSet<(string, string, object)>(); + + /// + /// 数据更改通知(来自远程) + /// + /// 发生在哪个节点 + /// 属性路径 + /// 变化后的属性值 + /// + public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) + { + // "NodeModel.Path" + if (TryGetNodeModel(nodeGuid, out var nodeModel)) + { + SerinExpressionEvaluator.Evaluate($"@Set .{path} = {value}", nodeModel, out _); // 更改对应的数据 + } + + + return Task.CompletedTask; + //if (NodeValueChangeLogger.Remove((nodeGuid, path, value))) + //{ + // // 说明存在过重复的修改 + // return; + //} + //NodeValueChangeLogger.Add((nodeGuid, path, value)); + + //lock (NodeValueChangeLogger) + //{ + + // Interlocked.Add(ref i, 1); + // Console.WriteLine(i); + // var getExp = $"@Get .{path}"; + // var setExp = $"@Set .{path} = {value}"; // 生成 set 表达式 + // var oldValue = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); + // if(oldValue != value) + // { + // Console.WriteLine($"旧值:{getExp},result : {oldValue}"); + // SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); // 更改对应的数据 + // Console.WriteLine($"新值:{getExp},result : {SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _)}"); + // } + + //} + + + + } + + + /// + /// 改变可选参数的数目 + /// + /// 对应的节点Guid + /// true,增加参数;false,减少参数 + /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数) + /// + public Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex) + { + if (!TryGetNodeModel(nodeGuid, out var nodeModel)) + { + return Task.FromResult(false); + } + if (nodeModel is null) return Task.FromResult(false); + bool isPass; + if (isAdd) + { + isPass = nodeModel.MethodDetails.AddParamsArg(paramIndex); + } + else + { + isPass = nodeModel.MethodDetails.RemoveParamsArg(paramIndex); + } + return Task.FromResult(isPass); + } + + + /// + /// 从Guid获取画布 + /// + /// 节点Guid + /// 节点Model + /// 无法获取节点、Guid/节点为null时报错 + public bool TryGetCanvasModel(string nodeGuid, out FlowCanvasDetails canvasDetails) + { + if (string.IsNullOrEmpty(nodeGuid)) + { + canvasDetails = null; + return false; + } + return FlowCanvass.TryGetValue(nodeGuid, out canvasDetails) && canvasDetails is not null; + + } + + /// + /// 从Guid获取节点 + /// + /// 节点Guid + /// 节点Model + /// 无法获取节点、Guid/节点为null时报错 + public bool TryGetNodeModel(string nodeGuid,out IFlowNode nodeModel) + { + if (string.IsNullOrEmpty(nodeGuid)) + { + nodeModel = null; + return false; + } + return NodeModels.TryGetValue(nodeGuid, out nodeModel) && nodeModel is not null; + + } + + #endregion + + #region 流程依赖类库的接口 + + + /// + /// 运行时加载 + /// + /// 文件名 + /// + public bool LoadNativeLibraryOfRuning(string file) + { + + return NativeDllHelper.LoadDll(file); + } + + /// + /// 运行时加载指定目录下的类库 + /// + /// 目录 + /// 是否递归加载 + public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true) + { + NativeDllHelper.LoadAllDll(path); + } + + #endregion + + #region 私有方法 + + #region 暂时注释 + /* + /// + /// 加载指定路径的DLL文件 + /// + /// + private void LoadDllNodeInfo(string dllPath) + { + + var fileName = Path.GetFileName(dllPath); + AssemblyLoadContext flowAlc = new AssemblyLoadContext(fileName, true); + flowAlc.LoadFromAssemblyPath(dllPath); // 加载指定路径的程序集 + + foreach(var assemblt in flowAlc.Assemblies) + { + (var registerTypes, var mdlist) = LoadAssembly(assemblt); + if (mdlist.Count > 0) + { + var nodeLibraryInfo = new NodeLibraryInfo + { + //Assembly = assembly, + AssemblyName = assemblt.FullName, + FileName = Path.GetFileName(dllPath), + FilePath = dllPath, + }; + + LibraryInfos.TryAdd(nodeLibraryInfo.AssemblyName, nodeLibraryInfo); + MethodDetailsOfLibraryInfos.TryAdd(nodeLibraryInfo, mdlist); + + foreach (var md in mdlist) + { + MethodDetailss.TryAdd(md.MethodName, md); + } + + foreach (var kv in registerTypes) + { + if (!AutoRegisterTypes.TryGetValue(kv.Key, out var types)) + { + types = new List(); + AutoRegisterTypes.Add(kv.Key, types); + } + types.AddRange(kv.Value); + } + var mdInfos = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息 + + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibraryInfo, mdInfos))); // 通知UI创建dll面板显示 + + } + } + + + } + + + + + }*/ + #endregion + + + /// + /// 从节点信息创建节点,并返回状态指示是否创建成功 + /// + /// + /// + private bool CreateNodeFromNodeInfo(NodeInfo nodeInfo) + { + if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType)) + { + return false; + } + + #region 获取方法描述 + MethodDetails? methodDetails; + if (controlType == NodeControlType.FlowCall) + { + if (string.IsNullOrEmpty(nodeInfo.MethodName)) + { + methodDetails = new MethodDetails(); + methodDetails.ParamsArgIndex = 0; + methodDetails.ParameterDetailss = new ParameterDetails[nodeInfo.ParameterData.Length]; + for (int i = 0; i < methodDetails.ParameterDetailss.Length; i++) + { + var pdInfo = nodeInfo.ParameterData[i]; + var t = new ParameterDetailsInfo(); + var pd = new ParameterDetails(pdInfo, i); + methodDetails.ParameterDetailss[i] = pd; + } + } + else + { + // 目标节点可能是方法节点 + FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息 + } + } + else if (controlType.IsBaseNode()) + { + // 加载基础节点 + methodDetails = new MethodDetails(); + + } + else + { + if (string.IsNullOrEmpty(nodeInfo.MethodName)) return false; + // 加载方法节点 + FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息 + } + #endregion + + var nodeModel = FlowNodeExtension.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点 + if (nodeModel is null) + { + nodeInfo.Guid = string.Empty; + return false; + } + if (FlowCanvass.TryGetValue(nodeInfo.CanvasGuid, out var canvasModel)) + { + + // 节点与画布互相绑定 + // 需要在UI线程上进行添加,否则会报 “不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改”异常 + nodeModel.CanvasDetails = canvasModel; + UIContextOperation?.Invoke(() => canvasModel.Nodes.Add(nodeModel)); + + nodeModel.LoadInfo(nodeInfo); // 创建节点model + TryAddNode(nodeModel); // 加载项目时将节点加载到环境中 + } + else + { + SereinEnv.WriteLine(InfoType.ERROR, $"加载节点[{nodeInfo.Guid}]时发生异常,画布[{nodeInfo.CanvasGuid}]不存在"); + return false; + } + + UIContextOperation?.Invoke(() => + OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上 + return true; + } + + + /// + /// 移除连接关系 + /// + /// 起始节点Model + /// 目标节点Model + /// 连接关系 + /// + private async Task RemoteConnectAsync(string canvasGuid, IFlowNode fromNode, IFlowNode toNode, ConnectionInvokeType connectionType) + { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return false; + } + fromNode.SuccessorNodes[connectionType].Remove(toNode); + toNode.PreviousNodes[connectionType].Remove(fromNode); + + + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke( + new NodeConnectChangeEventArgs( + canvasGuid, + fromNode.Guid, + toNode.Guid, + JunctionOfConnectionType.Invoke, + connectionType, + NodeConnectChangeEventArgs.ConnectChangeType.Remove))); + } + return true; + } + /// + /// 移除连接关系 + /// + /// 起始节点Model + /// 目标节点Model + /// 连接关系 + /// + private async Task RemoteConnectAsync(string canvasGuid, IFlowNode fromNode, IFlowNode toNode, int argIndex) + { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return false; + } + if (string.IsNullOrEmpty(toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid)) + { + return false; + } + toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = null; + toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值 + + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke( + new NodeConnectChangeEventArgs( + canvasGuid, + fromNode.Guid, + toNode.Guid, + JunctionOfConnectionType.Arg, + argIndex, + ConnectionArgSourceType.GetPreviousNodeData, + NodeConnectChangeEventArgs.ConnectChangeType.Remove))); + } + return true; + } + + + /// + /// 创建节点 + /// + /// + private bool TryAddNode(IFlowNode nodeModel) + { + nodeModel.Guid ??= Guid.NewGuid().ToString(); + NodeModels.TryAdd(nodeModel.Guid, nodeModel); + + + // 如果是触发器,则需要添加到专属集合中 + if (nodeModel is SingleFlipflopNode flipflopNode) + { + var guid = flipflopNode.Guid; + if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid))) + { + FlipflopNodes.Add(flipflopNode); + } + } + return true; + } + + /// + /// 检查连接 + /// + /// 发起连接的起始节点 + /// 要连接的目标节点 + /// 发起连接节点的控制点类型 + /// 被连接节点的控制点类型 + /// + public static (JunctionOfConnectionType,bool) CheckConnect(IFlowNode fromNode, + IFlowNode toNode, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType) + { + var type = JunctionOfConnectionType.None; + var state = false; + if (fromNodeJunctionType == JunctionType.Execute) + { + if (toNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid)) + { + // “方法执行”控制点拖拽到“下一节点”控制点,且不是同一个节点, 添加方法执行关系 + type = JunctionOfConnectionType.Invoke; + state = true; + } + //else if (toNodeJunctionType == JunctionType.ArgData && fromNode.Guid.Equals(toNode.Guid)) + //{ + // // “方法执行”控制点拖拽到“方法入参”控制点,且是同一个节点,则添加获取参数关系,表示生成入参参数时自动从该节点的上一节点获取flowdata + // type = JunctionOfConnectionType.Arg; + // state = true; + //} + } + else if (fromNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid)) + { + // “下一节点”控制点只能拖拽到“方法执行”控制点,且不能是同一个节点 + if (toNodeJunctionType == JunctionType.Execute && !fromNode.Guid.Equals(toNode.Guid)) + { + type = JunctionOfConnectionType.Invoke; + state = true; + } + } + else if (fromNodeJunctionType == JunctionType.ArgData) + { + //if (toNodeJunctionType == JunctionType.Execute && fromNode.Guid.Equals(toNode.Guid)) // 添加获取参数关系 + //{ + // // “方法入参”控制点拖拽到“方法执行”控制点,且是同一个节点,则添加获取参数关系,生成入参参数时自动从该节点的上一节点获取flowdata + // type = JunctionOfConnectionType.Arg; + // state = true; + //} + if(toNodeJunctionType == JunctionType.ReturnData && !fromNode.Guid.Equals(toNode.Guid)) + { + // “”控制点拖拽到“方法返回值”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata + type = JunctionOfConnectionType.Arg; + state = true; + } + } + else if (fromNodeJunctionType == JunctionType.ReturnData) + { + if (toNodeJunctionType == JunctionType.ArgData && !fromNode.Guid.Equals(toNode.Guid)) + { + // “方法返回值”控制点拖拽到“方法入参”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata + type = JunctionOfConnectionType.Arg; + state = true; + } + } + // 剩下的情况都是不符预期的连接行为,忽略。 + return (type,state); + } + + /// + /// 连接节点 + /// + /// 起始节点 + /// 目标节点 + /// 连接关系 + private bool ConnectInvokeOfNode(string canvasGuid, IFlowNode fromNode, IFlowNode toNode, ConnectionInvokeType invokeType) + { + if (fromNode.ControlType == NodeControlType.FlowCall) + { + SereinEnv.WriteLine(InfoType.ERROR, $"流程接口节点不可调用下一个节点。" + + $"{Environment.NewLine}流程节点:{fromNode.Guid}"); + return false; + } + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return false; + } + if (fromNode is null || toNode is null || fromNode == toNode) + { + return false; + } + + var ToExistOnFrom = true; + var FromExistInTo = true; + ConnectionInvokeType[] ct = [ConnectionInvokeType.IsSucceed, + ConnectionInvokeType.IsFail, + ConnectionInvokeType.IsError, + ConnectionInvokeType.Upstream]; + + if (toNode is SingleFlipflopNode flipflopNode) + { + flowTaskManagement?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被连接的是全局触发器,尝试移除 + } + var isOverwriting = false; + ConnectionInvokeType overwritingCt = ConnectionInvokeType.None; + var isPass = false; + foreach (ConnectionInvokeType ctType in ct) + { + var FToTo = fromNode.SuccessorNodes[ctType].Where(it => it.Guid.Equals(toNode.Guid)).ToArray(); + var ToOnF = toNode.PreviousNodes[ctType].Where(it => it.Guid.Equals(fromNode.Guid)).ToArray(); + ToExistOnFrom = FToTo.Length > 0; + FromExistInTo = ToOnF.Length > 0; + if (ToExistOnFrom && FromExistInTo) + { + if(ctType == invokeType) + { + SereinEnv.WriteLine(InfoType.WARN, $"起始节点已与目标节点存在连接。" + + $"{Environment.NewLine}起始节点:{fromNode.Guid}" + + $"{Environment.NewLine}目标节点:{toNode.Guid}"); + return false; + } + isOverwriting = true; + overwritingCt = ctType; + } + else + { + // 检查是否可能存在异常 + if (!ToExistOnFrom && FromExistInTo) + { + SereinEnv.WriteLine(InfoType.ERROR, $"起始节点不是目标节点的父节点,目标节点却是起始节点的子节点。" + + $"{Environment.NewLine}起始节点:{fromNode.Guid}" + + $"{Environment.NewLine}目标节点:{toNode.Guid}"); + isPass = false; + } + else if (ToExistOnFrom && !FromExistInTo) + { + // + SereinEnv.WriteLine(InfoType.ERROR, $"起始节点不是目标节点的父节点,目标节点却是起始节点的子节点。" + + $"{Environment.NewLine}起始节点:{fromNode.Guid}" + + $"{Environment.NewLine}目标节点:{toNode.Guid}" + + $""); + isPass = false; + } + else + { + isPass = true; + } + } + } + if (isPass) + { + if (isOverwriting) // 需要替换 + { + fromNode.SuccessorNodes[overwritingCt].Remove(toNode); // 从起始节点子分支中移除 + toNode.PreviousNodes[overwritingCt].Remove(fromNode); // 从目标节点父分支中移除 + } + fromNode.SuccessorNodes[invokeType].Add(toNode); // 添加到起始节点的子分支 + toNode.PreviousNodes[invokeType].Add(fromNode); // 添加到目标节点的父分支 + if (OperatingSystem.IsWindows()) + { + + UIContextOperation?.Invoke(() => + OnNodeConnectChange?.Invoke( + new NodeConnectChangeEventArgs( + canvasGuid, + fromNode.Guid, // 从哪个节点开始 + toNode.Guid, // 连接到那个节点 + JunctionOfConnectionType.Invoke, + invokeType, // 连接线的样式类型 + NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 + ))); // 通知UI + } + // Invoke + // GetResult + return true; + } + else + { + return false; + } + + + } + + /// + /// 连接节点参数 + /// + /// + /// + /// + /// + /// + private async Task ConnectArgSourceOfNodeAsync(string canvasGuid, + IFlowNode fromNode, + IFlowNode toNode, + ConnectionArgSourceType connectionArgSourceType, + int argIndex) + { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return false; + } + + var toNodeArgSourceGuid = toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid; + var toNodeArgSourceType = toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType; + if(fromNode.Guid == toNodeArgSourceGuid && toNodeArgSourceType == connectionArgSourceType) + { + SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系,此次操作将不会执行" + + $"起始节点:{fromNode.Guid}" + + $"目标节点:{toNode.Guid}" + + $"参数索引:{argIndex}" + + $"参数类型:{connectionArgSourceType}"); + UIContextOperation?.Invoke(() => + OnNodeConnectChange?.Invoke( + new NodeConnectChangeEventArgs( + canvasGuid, + fromNode.Guid, // 从哪个节点开始 + toNode.Guid, // 连接到那个节点 + JunctionOfConnectionType.Arg, + argIndex, // 连接线的样式类型 + connectionArgSourceType, + NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 + ))); // 通知UI + + return true; + } + + if (!string.IsNullOrEmpty(toNodeArgSourceGuid) ) + { + await RemoteConnectAsync(canvasGuid, fromNode, toNode, argIndex); + } + + toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = fromNode.Guid; + toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = connectionArgSourceType; + + UIContextOperation?.Invoke(() => + OnNodeConnectChange?.Invoke( + new NodeConnectChangeEventArgs( + canvasGuid, + fromNode.Guid, // 从哪个节点开始 + toNode.Guid, // 连接到那个节点 + JunctionOfConnectionType.Arg, + argIndex, // 连接线的样式类型 + connectionArgSourceType, + NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 + ))); // 通知UI + return true; + } + + + /// + /// 更改起点节点 + /// + /// 节点所在的画布 + /// 起始节点 + private void SetStartNode(FlowCanvasDetails cavnasModel, IFlowNode newStartNode) + { + var oldNodeGuid = cavnasModel.StartNode?.Guid; + /*if(TryGetNodeModel(oldNodeGuid, out var newStartNodeModel)) + { + newStartNode.IsStart = false; + }*/ + cavnasModel.StartNode = newStartNode; + //newStartNode.IsStart = true; + UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode.Guid))); + + } + + /// + /// 向容器登记缓存的持久化实例 + /// + private void ReRegisterPersistennceInstance() + { + lock (PersistennceInstance) + { + foreach (var kvp in PersistennceInstance) + { + IOC.Register(kvp.Key, () => kvp.Value); + } + } + } + + #endregion + + #region 视觉效果 + + /// + /// 定位节点 + /// + /// + public void NodeLocated(string nodeGuid) + { + if (OperatingSystem.IsWindows()) + { + UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid))); + } + + } + + #endregion + + + + + } + + + + + + + + + + + + +} diff --git a/NodeFlow/Env/MsgControllerOfClient.cs b/NodeFlow/Env/MsgControllerOfClient.cs index 030136c..d32d908 100644 --- a/NodeFlow/Env/MsgControllerOfClient.cs +++ b/NodeFlow/Env/MsgControllerOfClient.cs @@ -10,9 +10,9 @@ namespace Serein.NodeFlow.Env /// 客户端的消息管理(用于处理服务端的响应) /// - [AutoSocketModule(ThemeKey = FlowEnvironment.ThemeKey, - DataKey = FlowEnvironment.DataKey, - MsgIdKey = FlowEnvironment.MsgIdKey)] + [AutoSocketModule(ThemeKey = LocalFlowEnvironment.ThemeKey, + DataKey = LocalFlowEnvironment.DataKey, + MsgIdKey = LocalFlowEnvironment.MsgIdKey)] public class MsgControllerOfClient : ISocketHandleModule { public Guid HandleGuid => new Guid(); diff --git a/NodeFlow/Env/MsgControllerOfServer.cs b/NodeFlow/Env/MsgControllerOfServer.cs index 005fce0..9bad06d 100644 --- a/NodeFlow/Env/MsgControllerOfServer.cs +++ b/NodeFlow/Env/MsgControllerOfServer.cs @@ -11,9 +11,9 @@ namespace Serein.NodeFlow.Env /// /// 服务端的消息管理(用于处理客户端的请求) /// - [AutoSocketModule(ThemeKey = FlowEnvironment.ThemeKey, - DataKey = FlowEnvironment.DataKey, - MsgIdKey = FlowEnvironment.MsgIdKey)] + [AutoSocketModule(ThemeKey = LocalFlowEnvironment.ThemeKey, + DataKey = LocalFlowEnvironment.DataKey, + MsgIdKey = LocalFlowEnvironment.MsgIdKey)] public class MsgControllerOfServer : ISocketHandleModule { /// @@ -156,8 +156,8 @@ namespace Serein.NodeFlow.Env { JObject sendJson = new JObject { - [FlowEnvironment.ThemeKey] = theme, - [FlowEnvironment.DataKey] = JObject.FromObject(data), + [LocalFlowEnvironment.ThemeKey] = theme, + [LocalFlowEnvironment.DataKey] = JObject.FromObject(data), }; var msg = sendJson.ToString(); await SendAsync(msg); diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs index a7a2c28..c2e9371 100644 --- a/NodeFlow/Env/RemoteFlowEnvironment.cs +++ b/NodeFlow/Env/RemoteFlowEnvironment.cs @@ -65,7 +65,7 @@ namespace Serein.NodeFlow.Env public ISereinIOC IOC => throw new NotImplementedException(); - public string EnvName => FlowEnvironment.SpaceName; + public string EnvName => LocalFlowEnvironment.SpaceName; /// /// 远程项目的网络位置(WebSocket + IP + 端口 : 远程主机的文件路径) @@ -1424,6 +1424,11 @@ namespace Serein.NodeFlow.Env this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:LoadAllNativeLibraryOfRuning"); } + public void UseExternalIOC(ISereinIOC ioc) + { + throw new NotImplementedException(); + } + #endregion diff --git a/Workbench/ServiceCollectionExtensions.cs b/Workbench/ServiceCollectionExtensions.cs index d5bb639..36d29a0 100644 --- a/Workbench/ServiceCollectionExtensions.cs +++ b/Workbench/ServiceCollectionExtensions.cs @@ -22,19 +22,19 @@ namespace Serein.Workbench /// public static void AddViewModelServices(this IServiceCollection collection) { - collection.AddSingleton(); // 主窗体 + collection.AddSingleton(); // 视图模型路由 - collection.AddSingleton(); - collection.AddSingleton(); - collection.AddSingleton(); - collection.AddSingleton(); - collection.AddSingleton(); - collection.AddSingleton(); - collection.AddSingleton(); - collection.AddSingleton(); + collection.AddSingleton(); + collection.AddSingleton(); // 菜单栏视图模型 + collection.AddSingleton(); // 工作台视图模型 + collection.AddSingleton(); // 基础节点视图模型 + collection.AddSingleton(); // 流程已加载依赖视图模型 + collection.AddSingleton(); // 流程画布编辑器视图模型 + collection.AddSingleton(); // 节点信息视图模型 + collection.AddSingleton(); // 方法信息视图模型 + collection.AddSingleton(); // 画布视图模型 collection.AddTransient(); // 画布 - collection.AddTransient(); // 画布节点树视图 } public static void AddWorkbenchServices(this IServiceCollection collection) @@ -54,7 +54,7 @@ namespace Serein.Workbench public static void AddFlowServices(this IServiceCollection collection) { #region 创建实例 - Func getSyncContext = null; + Func? getSyncContext = null; Dispatcher.CurrentDispatcher.Invoke(() => { var uiContext = SynchronizationContext.Current; // 在UI线程上获取UI线程上下文信息 @@ -62,16 +62,14 @@ namespace Serein.Workbench { getSyncContext = () => uiContext; } - }); - UIContextOperation? uIContextOperation = null; uIContextOperation = new UIContextOperation(getSyncContext); // 封装一个调用UI线程的工具类 - var flowEnvironmentDecorator = new FlowEnvironmentDecorator(); - flowEnvironmentDecorator.SetUIContextOperation(uIContextOperation); + var flowEnvironment = new FlowEnvironment(); + flowEnvironment.SetUIContextOperation(uIContextOperation); collection.AddSingleton(uIContextOperation); // 注册UI线程操作上下文 - collection.AddSingleton(flowEnvironmentDecorator); // 注册运行环境 - collection.AddSingleton(flowEnvironmentDecorator); // 注册运行环境事件 + collection.AddSingleton(flowEnvironment); // 注册运行环境 + collection.AddSingleton(flowEnvironment); // 注册运行环境事件 #endregion diff --git a/Workbench/Services/FlowEEForwardingService.cs b/Workbench/Services/FlowEEForwardingService.cs index 2bfe3f5..6670986 100644 --- a/Workbench/Services/FlowEEForwardingService.cs +++ b/Workbench/Services/FlowEEForwardingService.cs @@ -130,7 +130,7 @@ namespace Serein.Workbench.Services private void InitFlowEnvironmentEvent() { flowEnvironmentEvent.OnDllLoad += FlowEnvironment_DllLoadEvent; - flowEnvironmentEvent.OnProjectSaving += EnvDecorator_OnProjectSaving; + flowEnvironmentEvent.OnProjectSaving += FlowEnvironment_OnProjectSaving; flowEnvironmentEvent.OnProjectLoaded += FlowEnvironment_OnProjectLoaded; flowEnvironmentEvent.OnCanvasCreate += FlowEnvironmentEvent_OnCanvasCreate; flowEnvironmentEvent.OnCanvasRemove += FlowEnvironmentEvent_OnCanvasRemove; @@ -138,8 +138,8 @@ namespace Serein.Workbench.Services flowEnvironmentEvent.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt; flowEnvironmentEvent.OnNodeCreate += FlowEnvironment_NodeCreateEvent; flowEnvironmentEvent.OnNodeRemove += FlowEnvironment_NodeRemoveEvent; - flowEnvironmentEvent.OnNodePlace += EnvDecorator_OnNodePlaceEvent; - flowEnvironmentEvent.OnNodeTakeOut += EnvDecorator_OnNodeTakeOutEvent; + flowEnvironmentEvent.OnNodePlace += FlowEnvironment_OnNodePlaceEvent; + flowEnvironmentEvent.OnNodeTakeOut += FlowEnvironment_OnNodeTakeOutEvent; flowEnvironmentEvent.OnFlowRunComplete += FlowEnvironment_OnFlowRunCompleteEvent; flowEnvironmentEvent.OnMonitorObjectChange += FlowEnvironment_OnMonitorObjectChangeEvent; @@ -158,14 +158,14 @@ namespace Serein.Workbench.Services private void ResetFlowEnvironmentEvent() { flowEnvironmentEvent.OnDllLoad -= FlowEnvironment_DllLoadEvent; - flowEnvironmentEvent.OnProjectSaving -= EnvDecorator_OnProjectSaving; + flowEnvironmentEvent.OnProjectSaving -= FlowEnvironment_OnProjectSaving; flowEnvironmentEvent.OnProjectLoaded -= FlowEnvironment_OnProjectLoaded; flowEnvironmentEvent.OnStartNodeChange -= FlowEnvironment_StartNodeChangeEvent; flowEnvironmentEvent.OnNodeConnectChange -= FlowEnvironment_NodeConnectChangeEvemt; flowEnvironmentEvent.OnNodeCreate -= FlowEnvironment_NodeCreateEvent; flowEnvironmentEvent.OnNodeRemove -= FlowEnvironment_NodeRemoveEvent; - flowEnvironmentEvent.OnNodePlace -= EnvDecorator_OnNodePlaceEvent; - flowEnvironmentEvent.OnNodeTakeOut -= EnvDecorator_OnNodeTakeOutEvent; + flowEnvironmentEvent.OnNodePlace -= FlowEnvironment_OnNodePlaceEvent; + flowEnvironmentEvent.OnNodeTakeOut -= FlowEnvironment_OnNodeTakeOutEvent; flowEnvironmentEvent.OnFlowRunComplete -= FlowEnvironment_OnFlowRunCompleteEvent; @@ -201,7 +201,7 @@ namespace Serein.Workbench.Services /// /// /// - private void EnvDecorator_OnProjectSaving(ProjectSavingEventArgs eventArgs) + private void FlowEnvironment_OnProjectSaving(ProjectSavingEventArgs eventArgs) { OnProjectSaving?.Invoke(eventArgs); } @@ -289,7 +289,7 @@ namespace Serein.Workbench.Services /// /// /// - private void EnvDecorator_OnNodePlaceEvent(NodePlaceEventArgs eventArgs) + private void FlowEnvironment_OnNodePlaceEvent(NodePlaceEventArgs eventArgs) { OnNodePlace?.Invoke(eventArgs); } @@ -298,7 +298,7 @@ namespace Serein.Workbench.Services /// 取出一个节点 /// /// - private void EnvDecorator_OnNodeTakeOutEvent(NodeTakeOutEventArgs eventArgs) + private void FlowEnvironment_OnNodeTakeOutEvent(NodeTakeOutEventArgs eventArgs) { OnNodeTakeOut?.Invoke(eventArgs);