mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-19 16:06:33 +08:00
尝试使用源生成器规范NodeModel代码逻辑
This commit is contained in:
88
NodeFlow/Env/EnvMsgTheme.cs
Normal file
88
NodeFlow/Env/EnvMsgTheme.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Env
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息主题
|
||||
/// </summary>
|
||||
public static class EnvMsgTheme
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取远程环境信息
|
||||
/// </summary>
|
||||
public const string GetEnvInfo = nameof(GetEnvInfo);
|
||||
/// <summary>
|
||||
/// 尝试开始流程
|
||||
/// </summary>
|
||||
public const string StartFlow = nameof(StartFlow);
|
||||
/// <summary>
|
||||
/// 尝试从指定节点开始运行
|
||||
/// </summary>
|
||||
public const string StartFlowInSelectNode = nameof(StartFlowInSelectNode);
|
||||
/// <summary>
|
||||
/// 尝试结束流程运行
|
||||
/// </summary>
|
||||
public const string ExitFlow = nameof(ExitFlow);
|
||||
/// <summary>
|
||||
/// 尝试移动某个节点
|
||||
/// </summary>
|
||||
public const string MoveNode = nameof(MoveNode);
|
||||
/// <summary>
|
||||
/// 尝试设置流程起点
|
||||
/// </summary>
|
||||
public const string SetStartNode = nameof(SetStartNode);
|
||||
/// <summary>
|
||||
/// 尝试连接两个节点
|
||||
/// </summary>
|
||||
public const string ConnectNode = nameof(ConnectNode);
|
||||
/// <summary>
|
||||
/// 尝试创建节点
|
||||
/// </summary>
|
||||
public const string CreateNode = nameof(CreateNode);
|
||||
/// <summary>
|
||||
/// 尝试移除节点之间的连接关系
|
||||
/// </summary>
|
||||
public const string RemoveConnect = nameof(RemoveConnect);
|
||||
/// <summary>
|
||||
/// 尝试移除节点
|
||||
/// </summary>
|
||||
public const string RemoveNode = nameof(RemoveNode);
|
||||
/// <summary>
|
||||
/// 激活一个触发器
|
||||
/// </summary>
|
||||
public const string ActivateFlipflopNode = nameof(ActivateFlipflopNode);
|
||||
/// <summary>
|
||||
/// 终结一个触发器
|
||||
/// </summary>
|
||||
public const string TerminateFlipflopNode = nameof(TerminateFlipflopNode);
|
||||
|
||||
/// <summary>
|
||||
/// 属性通知
|
||||
/// </summary>
|
||||
public const string ValueNotification = nameof(ValueNotification);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取项目信息
|
||||
/// </summary>
|
||||
public const string GetProjectInfo = nameof(GetProjectInfo);
|
||||
/// <summary>
|
||||
/// 尝试设置节点中断
|
||||
/// </summary>
|
||||
public const string SetNodeInterrupt = nameof(SetNodeInterrupt);
|
||||
/// <summary>
|
||||
/// 尝试添加中断表达式
|
||||
/// </summary>
|
||||
public const string AddInterruptExpression = nameof(AddInterruptExpression);
|
||||
/// <summary>
|
||||
/// 尝试设置节点/对象监视状态
|
||||
/// </summary>
|
||||
public const string SetMonitor = nameof(SetMonitor);
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
470
NodeFlow/Env/FlowEnvironmentDecorator.cs
Normal file
470
NodeFlow/Env/FlowEnvironmentDecorator.cs
Normal file
@@ -0,0 +1,470 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Web;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Env
|
||||
{
|
||||
/// <summary>
|
||||
/// 自动管理本地与远程的环境
|
||||
/// </summary>
|
||||
public class FlowEnvironmentDecorator : IFlowEnvironment, ISereinIOC
|
||||
{
|
||||
public FlowEnvironmentDecorator(UIContextOperation uiContextOperation)
|
||||
{
|
||||
flowEnvironment = new FlowEnvironment(uiContextOperation);
|
||||
// 默认使用本地环境
|
||||
currentFlowEnvironment = flowEnvironment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地环境
|
||||
/// </summary>
|
||||
private readonly FlowEnvironment flowEnvironment;
|
||||
|
||||
/// <summary>
|
||||
/// 远程环境
|
||||
/// </summary>
|
||||
private RemoteFlowEnvironment remoteFlowEnvironment;
|
||||
|
||||
/// <summary>
|
||||
/// 管理当前环境
|
||||
/// </summary>
|
||||
|
||||
private IFlowEnvironment currentFlowEnvironment;
|
||||
|
||||
|
||||
private int _flag = 0; // 使用原子自增代替锁
|
||||
/// <summary>
|
||||
/// 传入false时,将停止数据通知。传入true时,
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public void SetFlag(bool value)
|
||||
{
|
||||
Interlocked.Exchange(ref _flag, value ? 1 : 0);
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsFlagSet()
|
||||
{
|
||||
return Interlocked.CompareExchange(ref _flag, 1, 1) == 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 当前环境,用于切换远程与本地环境
|
||||
/// </summary>
|
||||
public IFlowEnvironment CurrentEnv { get => currentFlowEnvironment; }
|
||||
|
||||
|
||||
public ISereinIOC IOC => (ISereinIOC)currentFlowEnvironment;
|
||||
|
||||
public string EnvName => currentFlowEnvironment.EnvName;
|
||||
|
||||
public bool IsGlobalInterrupt => currentFlowEnvironment.IsGlobalInterrupt;
|
||||
|
||||
public bool IsLcR => currentFlowEnvironment.IsLcR;
|
||||
|
||||
public bool IsRcL => currentFlowEnvironment.IsRcL;
|
||||
|
||||
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 { currentFlowEnvironment.OnDllLoad += value; }
|
||||
remove { currentFlowEnvironment.OnDllLoad -= value; }
|
||||
}
|
||||
public event ProjectLoadedHandler OnProjectLoaded
|
||||
{
|
||||
add { currentFlowEnvironment.OnProjectLoaded += value; }
|
||||
remove { currentFlowEnvironment.OnProjectLoaded -= value; }
|
||||
}
|
||||
|
||||
public event NodeConnectChangeHandler OnNodeConnectChange
|
||||
{
|
||||
add { currentFlowEnvironment.OnNodeConnectChange += value; }
|
||||
remove { currentFlowEnvironment.OnNodeConnectChange -= value; }
|
||||
}
|
||||
|
||||
public event NodeCreateHandler OnNodeCreate
|
||||
{
|
||||
add { currentFlowEnvironment.OnNodeCreate += value; }
|
||||
remove { currentFlowEnvironment.OnNodeCreate -= value; }
|
||||
}
|
||||
|
||||
public event NodeRemoteHandler OnNodeRemote
|
||||
{
|
||||
add { currentFlowEnvironment.OnNodeRemote += value; }
|
||||
remove { currentFlowEnvironment.OnNodeRemote -= value; }
|
||||
}
|
||||
|
||||
public event StartNodeChangeHandler OnStartNodeChange
|
||||
{
|
||||
add { currentFlowEnvironment.OnStartNodeChange += value; }
|
||||
remove { currentFlowEnvironment.OnStartNodeChange -= value; }
|
||||
}
|
||||
|
||||
public event FlowRunCompleteHandler OnFlowRunComplete
|
||||
{
|
||||
add { currentFlowEnvironment.OnFlowRunComplete += value; }
|
||||
remove { currentFlowEnvironment.OnFlowRunComplete -= value; }
|
||||
}
|
||||
|
||||
public event MonitorObjectChangeHandler OnMonitorObjectChange
|
||||
{
|
||||
add { currentFlowEnvironment.OnMonitorObjectChange += value; }
|
||||
remove { currentFlowEnvironment.OnMonitorObjectChange -= value; }
|
||||
}
|
||||
|
||||
public event NodeInterruptStateChangeHandler OnNodeInterruptStateChange
|
||||
{
|
||||
add { currentFlowEnvironment.OnNodeInterruptStateChange += value; }
|
||||
remove { currentFlowEnvironment.OnNodeInterruptStateChange -= value; }
|
||||
}
|
||||
|
||||
public event ExpInterruptTriggerHandler OnInterruptTrigger
|
||||
{
|
||||
add { currentFlowEnvironment.OnInterruptTrigger += value; }
|
||||
remove { currentFlowEnvironment.OnInterruptTrigger -= value; }
|
||||
}
|
||||
|
||||
public event IOCMembersChangedHandler OnIOCMembersChanged
|
||||
{
|
||||
add { currentFlowEnvironment.OnIOCMembersChanged += value; }
|
||||
remove { currentFlowEnvironment.OnIOCMembersChanged -= value; }
|
||||
}
|
||||
|
||||
public event NodeLocatedHandler OnNodeLocated
|
||||
{
|
||||
add { currentFlowEnvironment.OnNodeLocated += value; }
|
||||
remove { currentFlowEnvironment.OnNodeLocated -= value; }
|
||||
}
|
||||
|
||||
public event NodeMovedHandler OnNodeMoved
|
||||
{
|
||||
add { currentFlowEnvironment.OnNodeMoved += value; }
|
||||
remove { currentFlowEnvironment.OnNodeMoved -= value; }
|
||||
}
|
||||
|
||||
public event EnvOutHandler OnEnvOut
|
||||
{
|
||||
add { currentFlowEnvironment.OnEnvOut += value; }
|
||||
remove { currentFlowEnvironment.OnEnvOut -= value; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void ActivateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
currentFlowEnvironment.ActivateFlipflopNode(nodeGuid);
|
||||
}
|
||||
|
||||
public async Task<bool> 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 void ClearAll()
|
||||
{
|
||||
currentFlowEnvironment.ClearAll();
|
||||
}
|
||||
|
||||
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
||||
{
|
||||
return await currentFlowEnvironment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, connectionType);
|
||||
}
|
||||
|
||||
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
|
||||
{
|
||||
// 连接成功,切换远程环境
|
||||
(var isConnect, var remoteEnvControl) = await currentFlowEnvironment.ConnectRemoteEnv(addres, port, token);
|
||||
if (isConnect)
|
||||
{
|
||||
remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteEnvControl);
|
||||
currentFlowEnvironment = remoteFlowEnvironment;
|
||||
}
|
||||
return (isConnect, remoteEnvControl);
|
||||
}
|
||||
|
||||
public async Task<NodeInfo> CreateNodeAsync(NodeControlType nodeBase, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null)
|
||||
{
|
||||
SetFlag(false);
|
||||
var result = await currentFlowEnvironment.CreateNodeAsync(nodeBase, position, methodDetailsInfo); // 装饰器调用
|
||||
SetFlag(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void ExitFlow()
|
||||
{
|
||||
currentFlowEnvironment.ExitFlow();
|
||||
}
|
||||
|
||||
public void ExitRemoteEnv()
|
||||
{
|
||||
currentFlowEnvironment.ExitRemoteEnv();
|
||||
}
|
||||
|
||||
|
||||
public async Task<FlowEnvInfo> GetEnvInfoAsync()
|
||||
{
|
||||
return await currentFlowEnvironment.GetEnvInfoAsync();
|
||||
}
|
||||
|
||||
public async Task<ChannelFlowInterrupt.CancelType> GetOrCreateGlobalInterruptAsync()
|
||||
{
|
||||
return await currentFlowEnvironment.GetOrCreateGlobalInterruptAsync();
|
||||
}
|
||||
|
||||
public async Task<SereinProjectData> GetProjectInfoAsync()
|
||||
{
|
||||
return await currentFlowEnvironment.GetProjectInfoAsync();
|
||||
}
|
||||
|
||||
|
||||
public void LoadDll(string dllPath)
|
||||
{
|
||||
currentFlowEnvironment.LoadDll(dllPath);
|
||||
}
|
||||
|
||||
public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath)
|
||||
{
|
||||
if (flowEnvInfo is null) return;
|
||||
SetFlag(false);
|
||||
currentFlowEnvironment.LoadProject(flowEnvInfo, filePath);
|
||||
SetFlag(true);
|
||||
}
|
||||
|
||||
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
|
||||
{
|
||||
currentFlowEnvironment.MonitorObjectNotification(nodeGuid, monitorData, sourceType);
|
||||
}
|
||||
|
||||
public void MoveNode(string nodeGuid, double x, double y)
|
||||
{
|
||||
currentFlowEnvironment.MoveNode(nodeGuid, x, y);
|
||||
}
|
||||
|
||||
public void NodeLocated(string nodeGuid)
|
||||
{
|
||||
currentFlowEnvironment.NodeLocated(nodeGuid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool RemoteDll(string assemblyFullName)
|
||||
{
|
||||
return currentFlowEnvironment.RemoteDll(assemblyFullName);
|
||||
}
|
||||
|
||||
public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
||||
{
|
||||
currentFlowEnvironment.RemoveConnect(fromNodeGuid, toNodeGuid, connectionType);
|
||||
}
|
||||
|
||||
public void RemoveNode(string nodeGuid)
|
||||
{
|
||||
currentFlowEnvironment.RemoveNode(nodeGuid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void SetConsoleOut()
|
||||
{
|
||||
currentFlowEnvironment.SetConsoleOut();
|
||||
}
|
||||
|
||||
public void SetMonitorObjState(string key, bool isMonitor)
|
||||
{
|
||||
currentFlowEnvironment.SetMonitorObjState(key, isMonitor);
|
||||
}
|
||||
|
||||
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass)
|
||||
{
|
||||
return await currentFlowEnvironment.SetNodeInterruptAsync(nodeGuid, interruptClass);
|
||||
}
|
||||
|
||||
public void SetStartNode(string nodeGuid)
|
||||
{
|
||||
currentFlowEnvironment.SetStartNode(nodeGuid);
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
await currentFlowEnvironment.StartAsync();
|
||||
}
|
||||
|
||||
public async Task StartAsyncInSelectNode(string startNodeGuid)
|
||||
{
|
||||
await currentFlowEnvironment.StartAsyncInSelectNode(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 bool TryGetDelegateDetails(string methodName, out DelegateDetails del)
|
||||
{
|
||||
return currentFlowEnvironment.TryGetDelegateDetails(methodName, out del);
|
||||
}
|
||||
|
||||
public bool TryGetMethodDetailsInfo(string methodName, out MethodDetailsInfo mdInfo)
|
||||
{
|
||||
return currentFlowEnvironment.TryGetMethodDetailsInfo(methodName, out mdInfo);
|
||||
}
|
||||
|
||||
public void WriteLineObjToJson(object obj)
|
||||
{
|
||||
currentFlowEnvironment.WriteLineObjToJson(obj);
|
||||
}
|
||||
|
||||
|
||||
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
||||
{
|
||||
if (!IsFlagSet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
await currentFlowEnvironment.NotificationNodeValueChangeAsync(nodeGuid, path, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#region IOC容器
|
||||
public ISereinIOC Build()
|
||||
{
|
||||
return IOC.Build();
|
||||
}
|
||||
|
||||
public bool CustomRegisterInstance(string key, object instance, bool needInjectProperty = true)
|
||||
{
|
||||
return IOC.CustomRegisterInstance(key, instance, needInjectProperty);
|
||||
}
|
||||
|
||||
public object Get(Type type)
|
||||
{
|
||||
return IOC.Get(type);
|
||||
}
|
||||
|
||||
public T Get<T>()
|
||||
{
|
||||
return IOC.Get<T>();
|
||||
}
|
||||
|
||||
public T Get<T>(string key)
|
||||
{
|
||||
return IOC.Get<T>(key);
|
||||
}
|
||||
|
||||
public object Instantiate(Type type)
|
||||
{
|
||||
return IOC.Instantiate(type);
|
||||
}
|
||||
|
||||
public T Instantiate<T>()
|
||||
{
|
||||
return IOC.Instantiate<T>();
|
||||
}
|
||||
|
||||
public ISereinIOC Register(Type type, params object[] parameters)
|
||||
{
|
||||
return IOC.Register(type, parameters);
|
||||
}
|
||||
|
||||
public ISereinIOC Register<T>(params object[] parameters)
|
||||
{
|
||||
return IOC.Register<T>(parameters);
|
||||
}
|
||||
|
||||
public ISereinIOC Register<TService, TImplementation>(params object[] parameters) where TImplementation : TService
|
||||
{
|
||||
return IOC.Register<TService, TImplementation>(parameters);
|
||||
}
|
||||
|
||||
public ISereinIOC Reset()
|
||||
{
|
||||
return IOC.Reset();
|
||||
}
|
||||
|
||||
public ISereinIOC Run<T>(Action<T> action)
|
||||
{
|
||||
return IOC.Run(action);
|
||||
}
|
||||
|
||||
public ISereinIOC Run<T1, T2>(Action<T1, T2> action)
|
||||
{
|
||||
return IOC.Run(action);
|
||||
}
|
||||
|
||||
public ISereinIOC Run<T1, T2, T3>(Action<T1, T2, T3> action)
|
||||
{
|
||||
return IOC.Run(action);
|
||||
}
|
||||
|
||||
public ISereinIOC Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
|
||||
{
|
||||
return IOC.Run(action);
|
||||
}
|
||||
|
||||
public ISereinIOC Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action)
|
||||
{
|
||||
return IOC.Run(action);
|
||||
}
|
||||
|
||||
public ISereinIOC Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action)
|
||||
{
|
||||
return IOC.Run(action);
|
||||
}
|
||||
|
||||
public ISereinIOC Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||
{
|
||||
return IOC.Run(action);
|
||||
}
|
||||
|
||||
public ISereinIOC Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||
{
|
||||
return IOC.Run(action);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
187
NodeFlow/Env/FlowFunc.cs
Normal file
187
NodeFlow/Env/FlowFunc.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Env
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 流程环境需要的扩展方法
|
||||
/// </summary>
|
||||
public static class FlowFunc
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 创建节点
|
||||
/// </summary>
|
||||
/// <param name="env">运行环境</param>
|
||||
/// <param name="nodeControlType">节点类型</param>
|
||||
/// <param name="methodDetails">方法描述</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static NodeModelBase CreateNode(IFlowEnvironment env, NodeControlType nodeControlType,
|
||||
MethodDetails? methodDetails = null)
|
||||
{
|
||||
// 确定创建的节点类型
|
||||
Type? nodeType = nodeControlType switch
|
||||
{
|
||||
NodeControlType.Action => typeof(SingleActionNode),
|
||||
NodeControlType.Flipflop => typeof(SingleFlipflopNode),
|
||||
|
||||
NodeControlType.ExpOp => typeof(SingleExpOpNode),
|
||||
NodeControlType.ExpCondition => typeof(SingleConditionNode),
|
||||
NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (nodeType is null)
|
||||
{
|
||||
throw new Exception($"节点类型错误[{nodeControlType}]");
|
||||
}
|
||||
// 生成实例
|
||||
var nodeObj = Activator.CreateInstance(nodeType, env);
|
||||
if (nodeObj is not NodeModelBase nodeModel)
|
||||
{
|
||||
throw new Exception($"无法创建目标节点类型的实例[{nodeControlType}]");
|
||||
}
|
||||
|
||||
// 配置基础的属性
|
||||
nodeModel.ControlType = nodeControlType;
|
||||
if (methodDetails == null) // 不存在方法描述时,可能是基础节点(表达式节点、条件表达式节点)
|
||||
{
|
||||
methodDetails = new MethodDetails();
|
||||
}
|
||||
var md = methodDetails.CloneOfNode(nodeModel.Env, nodeModel);
|
||||
nodeModel.DisplayName = md.MethodTips;
|
||||
nodeModel.MethodDetails = md;
|
||||
|
||||
|
||||
return nodeModel;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从节点信息读取节点类型
|
||||
/// </summary>
|
||||
/// <param name="nodeInfo"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public static NodeControlType GetNodeControlType(NodeInfo nodeInfo)
|
||||
{
|
||||
// 创建控件实例
|
||||
NodeControlType controlType = nodeInfo.Type switch
|
||||
{
|
||||
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" => NodeControlType.Action,// 动作节点控件
|
||||
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" => NodeControlType.Flipflop, // 触发器节点控件
|
||||
|
||||
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" => NodeControlType.ExpCondition,// 条件表达式控件
|
||||
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" => NodeControlType.ExpOp, // 操作表达式控件
|
||||
|
||||
$"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" => NodeControlType.ConditionRegion, // 条件区域控件
|
||||
_ => NodeControlType.None,
|
||||
};
|
||||
|
||||
return controlType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 程序集封装依赖
|
||||
/// </summary>
|
||||
/// <param name="library"></param>
|
||||
/// <returns></returns>
|
||||
public static Library.Library ToLibrary(this Library.NodeLibrary library)
|
||||
{
|
||||
var tmp = library.Assembly.ManifestModule.Name;
|
||||
return new Library.Library
|
||||
{
|
||||
AssemblyName = library.Assembly.GetName().Name,
|
||||
FileName = library.FileName,
|
||||
FilePath = library.FilePath,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发器运行后状态转为对应的后继分支类别
|
||||
/// </summary>
|
||||
/// <param name="flowStateType"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public static ConnectionType ToContentType(this FlipflopStateType flowStateType)
|
||||
{
|
||||
return flowStateType switch
|
||||
{
|
||||
FlipflopStateType.Succeed => ConnectionType.IsSucceed,
|
||||
FlipflopStateType.Fail => ConnectionType.IsFail,
|
||||
FlipflopStateType.Error => ConnectionType.IsError,
|
||||
FlipflopStateType.Cancel => ConnectionType.None,
|
||||
_ => throw new NotImplementedException("未定义的流程状态")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断 触发器节点 是否存在上游分支
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
public static bool NotExitPreviousNode(this SingleFlipflopNode node)
|
||||
{
|
||||
ConnectionType[] ct = [ConnectionType.IsSucceed,
|
||||
ConnectionType.IsFail,
|
||||
ConnectionType.IsError,
|
||||
ConnectionType.Upstream];
|
||||
foreach (ConnectionType ctType in ct)
|
||||
{
|
||||
if (node.PreviousNodes[ctType].Count > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 从节点类型枚举中转为对应的 Model 类型
|
||||
///// </summary>
|
||||
///// <param name="nodeControlType"></param>
|
||||
///// <returns></returns>
|
||||
//public static Type? ControlTypeToModel(this NodeControlType nodeControlType)
|
||||
//{
|
||||
// // 确定创建的节点类型
|
||||
// Type? nodeType = nodeControlType switch
|
||||
// {
|
||||
// NodeControlType.Action => typeof(SingleActionNode),
|
||||
// NodeControlType.Flipflop => typeof(SingleFlipflopNode),
|
||||
|
||||
// NodeControlType.ExpOp => typeof(SingleExpOpNode),
|
||||
// NodeControlType.ExpCondition => typeof(SingleConditionNode),
|
||||
// NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
|
||||
// _ => null
|
||||
// };
|
||||
// return nodeType;
|
||||
//}
|
||||
//public static NodeControlType ModelToControlType(this NodeControlType nodeControlType)
|
||||
//{
|
||||
// var type = nodeControlType.GetType();
|
||||
// NodeControlType controlType = type switch
|
||||
// {
|
||||
// Type when type == typeof(SingleActionNode) => NodeControlType.Action,
|
||||
// Type when type == typeof(SingleFlipflopNode) => NodeControlType.Flipflop,
|
||||
|
||||
// Type when type == typeof(SingleExpOpNode) => NodeControlType.ExpOp,
|
||||
// Type when type == typeof(SingleConditionNode) => NodeControlType.ExpCondition,
|
||||
// Type when type == typeof(CompositeConditionNode) => NodeControlType.ConditionRegion,
|
||||
// _ => NodeControlType.None,
|
||||
// };
|
||||
// return controlType;
|
||||
//}
|
||||
}
|
||||
|
||||
}
|
||||
129
NodeFlow/Env/MsgControllerOfClient.cs
Normal file
129
NodeFlow/Env/MsgControllerOfClient.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Network.WebSocketCommunication;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Env
|
||||
{
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 客户端的消息管理(用于处理服务端的响应)
|
||||
/// </summary>
|
||||
|
||||
[AutoSocketModule(ThemeKey = FlowEnvironment.ThemeKey, DataKey = FlowEnvironment.DataKey)]
|
||||
public class MsgControllerOfClient : ISocketHandleModule
|
||||
{
|
||||
public Guid HandleGuid => new Guid();
|
||||
private readonly Func<string, object?, Task> SendCommandAsync;
|
||||
private readonly RemoteFlowEnvironment remoteFlowEnvironment;
|
||||
|
||||
public MsgControllerOfClient(RemoteFlowEnvironment remoteFlowEnvironment, Func<string, object?, Task> func)
|
||||
{
|
||||
this.remoteFlowEnvironment = remoteFlowEnvironment;
|
||||
SendCommandAsync = func;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发送请求并等待远程环境响应
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException">超时触发</exception>
|
||||
public async Task SendAsync(string signal, object? senddata = null, int debounceTimeInMs = 100)
|
||||
{
|
||||
if (!DebounceHelper.CanExecute(signal, debounceTimeInMs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
await SendCommandAsync.Invoke(signal, senddata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送请求并等待远程环境响应
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException">超时触发</exception>
|
||||
public async Task<TResult> SendAndWaitDataAsync<TResult>(string signal, object? senddata = null, int debounceTimeInMs = 50)
|
||||
{
|
||||
_ = SendCommandAsync.Invoke(signal, senddata);
|
||||
return await remoteFlowEnvironment.WaitData<TResult>(signal);
|
||||
#if DEBUG
|
||||
|
||||
if (DebounceHelper.CanExecute(signal, debounceTimeInMs))
|
||||
{
|
||||
_ = SendCommandAsync.Invoke(signal, senddata);
|
||||
return await remoteFlowEnvironment.WaitData<TResult>(signal);
|
||||
|
||||
//(var type, var result) = await remoteFlowEnvironment.WaitDataWithTimeoutAsync<TResult>(signal, TimeSpan.FromSeconds(150));
|
||||
//if (type == TriggerType.Overtime)
|
||||
//{
|
||||
// throw new NotImplementedException("超时触发");
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// return result;
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
return default;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
#region 消息接收
|
||||
|
||||
/// <summary>
|
||||
/// 远程环境发来项目信息
|
||||
/// </summary>
|
||||
/// <param name="flowEnvInfo"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.GetEnvInfo)]
|
||||
public void GetEnvInfo([UseMsgData] FlowEnvInfo flowEnvInfo)
|
||||
{
|
||||
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.GetEnvInfo, flowEnvInfo);
|
||||
}
|
||||
|
||||
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode)]
|
||||
public void AddInterruptExpression([UseMsgData] NodeInfo nodeInfo)
|
||||
{
|
||||
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.CreateNode, nodeInfo);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 远程环境发来项目信息
|
||||
/// </summary>
|
||||
/// <param name="sereinProjectData"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.GetProjectInfo)]
|
||||
public void GetProjectInfo([UseMsgData] SereinProjectData sereinProjectData)
|
||||
{
|
||||
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.GetProjectInfo, sereinProjectData);
|
||||
}
|
||||
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.SetNodeInterrupt)]
|
||||
public void SetNodeInterrupt()
|
||||
{
|
||||
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.GetProjectInfo, null);
|
||||
}
|
||||
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.AddInterruptExpression)]
|
||||
public void AddInterruptExpression()
|
||||
{
|
||||
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.AddInterruptExpression, null);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
443
NodeFlow/Env/MsgControllerOfServer.cs
Normal file
443
NodeFlow/Env/MsgControllerOfServer.cs
Normal file
@@ -0,0 +1,443 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Network.WebSocketCommunication;
|
||||
using Serein.Library.Network.WebSocketCommunication.Handle;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Env
|
||||
{
|
||||
/// <summary>
|
||||
/// 服务端的消息管理(用于处理客户端的请求)
|
||||
/// </summary>
|
||||
[AutoSocketModule(ThemeKey = FlowEnvironment.ThemeKey, DataKey = FlowEnvironment.DataKey)]
|
||||
public class MsgControllerOfServer : ISocketHandleModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 受控环境
|
||||
/// </summary>
|
||||
public IFlowEnvironment environment;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket处理
|
||||
/// </summary>
|
||||
public Guid HandleGuid { get; } = new Guid();
|
||||
|
||||
/// <summary>
|
||||
/// <para>表示是否正在控制远程</para>
|
||||
/// <para>Local control remote env</para>
|
||||
/// </summary>
|
||||
public bool IsLcR { get; set; }
|
||||
/// <summary>
|
||||
/// <para>表示是否受到远程控制</para>
|
||||
/// <para>Remote control local env</para>
|
||||
/// </summary>
|
||||
public bool IsRcL { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 流程环境远程管理服务
|
||||
/// </summary>
|
||||
private WebSocketServer FlowEnvRemoteWebSocket;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 启动不带Token验证的远程服务
|
||||
/// </summary>
|
||||
public MsgControllerOfServer(IFlowEnvironment environment)
|
||||
{
|
||||
this.environment = environment;
|
||||
FlowEnvRemoteWebSocket ??= new WebSocketServer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动带token验证的远程服务
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
public MsgControllerOfServer(IFlowEnvironment environment, string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
Console.WriteLine("当前没有设置token,但使用了token验证的服务端");
|
||||
|
||||
}
|
||||
this.environment = environment;
|
||||
FlowEnvRemoteWebSocket ??= new WebSocketServer(token, OnInspectionAuthorized);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#region 基本方法
|
||||
/// <summary>
|
||||
/// 启动远程
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public async Task StartRemoteServerAsync(int port = 7525)
|
||||
{
|
||||
|
||||
FlowEnvRemoteWebSocket.MsgHandleHelper.AddModule(this,
|
||||
(ex, send) =>
|
||||
{
|
||||
send(new
|
||||
{
|
||||
code = 400,
|
||||
ex = ex.Message
|
||||
});
|
||||
});
|
||||
var url = $"http://*:{port}/";
|
||||
try
|
||||
{
|
||||
await FlowEnvRemoteWebSocket.StartAsync(url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FlowEnvRemoteWebSocket.MsgHandleHelper.RemoveModule(this);
|
||||
Console.WriteLine("打开远程管理异常:" + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束远程管理
|
||||
/// </summary>
|
||||
[AutoSocketHandle]
|
||||
public void StopRemoteServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
FlowEnvRemoteWebSocket.Stop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("结束远程管理异常:" + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证远程token
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> OnInspectionAuthorized(dynamic token)
|
||||
{
|
||||
if (IsLcR)
|
||||
{
|
||||
return false; // 正在远程控制远程环境时,禁止其它客户端远程控制
|
||||
}
|
||||
if (IsRcL)
|
||||
{
|
||||
return false; // 正在受到远程控制时,禁止其它客户端远程控制
|
||||
}
|
||||
await Task.Delay(0);
|
||||
var tokenValue = token.ToString();
|
||||
if ("123456".Equals(tokenValue))
|
||||
{
|
||||
// 同时切换远程环境
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取发送消息的委托
|
||||
/// </summary>
|
||||
/// <param name="SendAsync"></param>
|
||||
private void OnResultSendMsgFunc(Func<string, Task> SendAsync)
|
||||
{
|
||||
// 从受控环境向主控环境发送消息。
|
||||
Func<string, object, Task> func = async (theme, data) =>
|
||||
{
|
||||
JObject sendJson = new JObject
|
||||
{
|
||||
[FlowEnvironment.ThemeKey] = theme,
|
||||
[FlowEnvironment.DataKey] = JObject.FromObject(data),
|
||||
};
|
||||
var msg = sendJson.ToString();
|
||||
await SendAsync(msg);
|
||||
};
|
||||
|
||||
// var remoteEnv = new RemoteFlowEnvironment(func); // 创建一个远程环境
|
||||
// OnSwitchedEnvironment.Invoke(remoteEnv); // 通知前台切换到了远程环境
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 异步运行
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlow)]
|
||||
private async Task StartAsync()
|
||||
{
|
||||
var uiContextOperation = environment.IOC.Get<UIContextOperation>();
|
||||
await environment.StartAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从远程环境运行选定的节点
|
||||
/// </summary>
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlowInSelectNode)]
|
||||
private async Task StartAsyncInSelectNode(string startNodeGuid)
|
||||
{
|
||||
await environment.StartAsyncInSelectNode(startNodeGuid);
|
||||
}
|
||||
/// <summary>
|
||||
/// 结束流程
|
||||
/// </summary>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ExitFlow)]
|
||||
private void ExitFlow()
|
||||
{
|
||||
environment.ExitFlow();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 激活全局触发器
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ActivateFlipflopNode)]
|
||||
private void ActivateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
environment.ActivateFlipflopNode(nodeGuid);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 关闭全局触发器
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.TerminateFlipflopNode)]
|
||||
private void TerminateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
environment.TerminateFlipflopNode(nodeGuid);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前环境信息(远程连接)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.GetEnvInfo)]
|
||||
private async Task<FlowEnvInfo> GetEnvInfoAsync()
|
||||
{
|
||||
return await environment.GetEnvInfoAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载项目文件
|
||||
/// </summary>
|
||||
/// <param name="flowEnvInfo">环境信息</param>
|
||||
// [AutoSocketHandle(ThemeValue = EnvMsgTheme.GetProjectInfo)]
|
||||
private void LoadProject(FlowEnvInfo flowEnvInfo)
|
||||
{
|
||||
environment.LoadProject(flowEnvInfo, "");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 连接远程环境
|
||||
/// </summary>
|
||||
/// <param name="addres">远程环境地址</param>
|
||||
/// <param name="port">远程环境端口</param>
|
||||
/// <param name="token">密码</param>
|
||||
// [AutoSocketHandle]
|
||||
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
|
||||
{
|
||||
return await environment.ConnectRemoteEnv(addres, port, token);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 退出远程环境
|
||||
/// </summary>
|
||||
// [AutoSocketHandle]
|
||||
public void ExitRemoteEnv()
|
||||
{
|
||||
Console.WriteLine("暂未实现远程退出远程环境");
|
||||
IsLcR = false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 序列化当前项目的依赖信息、节点信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.GetProjectInfo)]
|
||||
public async Task<SereinProjectData> GetProjectInfoAsync()
|
||||
{
|
||||
return await environment.GetProjectInfoAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从文件路径中加载DLL
|
||||
/// </summary>
|
||||
/// <param name="dllPath"></param>
|
||||
/// <returns></returns>
|
||||
// [AutoSocketHandle(ThemeValue = EnvMsgTheme)]
|
||||
public void LoadDll(string dllPath)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// 移除DLL
|
||||
/// </summary>
|
||||
/// <param name="assemblyFullName"></param>
|
||||
/// <returns></returns>
|
||||
// [AutoSocketHandle(ThemeValue = EnvMsgTheme)]
|
||||
public bool RemoteDll(string assemblyFullName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从远程环境创建节点
|
||||
/// </summary>
|
||||
/// <param name="nodeType"></param>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="mdInfo">如果是表达式节点条件节点,该项为null</param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode,ArgNotNull = false)]
|
||||
public async Task<NodeInfo> CreateNode([Needful] string nodeType, [Needful] PositionOfUI position, MethodDetailsInfo? mdInfo = null)
|
||||
{
|
||||
if (!EnumHelper.TryConvertEnum<NodeControlType>(nodeType, out var nodeControlType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var nodeInfo = await environment.CreateNodeAsync(nodeControlType, position, mdInfo); // 监听到客户端创建节点的请求
|
||||
return nodeInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// 从远程环境移除节点
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode)]
|
||||
public void RemoveNode(string nodeGuid)
|
||||
{
|
||||
environment.RemoveNode(nodeGuid);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 连接节点
|
||||
/// </summary>
|
||||
/// <param name="fromNodeGuid">起始节点</param>
|
||||
/// <param name="toNodeGuid">目标节点</param>
|
||||
/// <param name="connectionType">连接关系</param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)]
|
||||
public void ConnectNode(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
||||
{
|
||||
environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, connectionType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除连接关系
|
||||
/// </summary>
|
||||
/// <param name="fromNodeGuid">起始节点Guid</param>
|
||||
/// <param name="toNodeGuid">目标节点Guid</param>
|
||||
/// <param name="connectionType">连接关系</param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)]
|
||||
public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
||||
{
|
||||
environment.RemoveConnect(fromNodeGuid, toNodeGuid, connectionType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移动了某个节点(远程插件使用)
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.MoveNode)]
|
||||
public void MoveNode(string nodeGuid, double x, double y)
|
||||
{
|
||||
environment.MoveNode(nodeGuid, x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置起点控件
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.SetStartNode)]
|
||||
public void SetStartNode(string nodeGuid)
|
||||
{
|
||||
environment.SetStartNode(nodeGuid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 中断指定节点,并指定中断等级。
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid">被中断的目标节点Guid</param>
|
||||
/// <param name="interruptClass">中断级别</param>
|
||||
/// <returns>操作是否成功</returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.SetNodeInterrupt)]
|
||||
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, string interruptClass)
|
||||
{
|
||||
|
||||
if (!EnumHelper.TryConvertEnum<InterruptClass>(interruptClass, out var @class))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await this.environment.SetNodeInterruptAsync(nodeGuid, @class);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加表达式中断
|
||||
/// </summary>
|
||||
/// <param name="key">如果是节点,传入Guid;如果是对象,传入类型FullName</param>
|
||||
/// <param name="expression">合法的条件表达式</param>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.AddInterruptExpression)]
|
||||
public async Task<bool> AddInterruptExpression(string key, string expression)
|
||||
{
|
||||
return await environment.AddInterruptExpressionAsync(key, expression);
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置对象的监视状态
|
||||
/// </summary>
|
||||
/// <param name="key">如果是节点,传入Guid;如果是对象,传入类型FullName</param>
|
||||
/// <param name="isMonitor">ture监视对象;false取消对象监视</param>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.SetMonitor)]
|
||||
public void SetMonitorObjState(string key, bool isMonitor)
|
||||
{
|
||||
environment.SetMonitorObjState(key, isMonitor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点数据更改
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="value"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ValueNotification)]
|
||||
public async Task ValueNotification(string nodeGuid, string path, string value)
|
||||
{
|
||||
await environment.NotificationNodeValueChangeAsync(nodeGuid, path, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
578
NodeFlow/Env/RemoteFlowEnvironment.cs
Normal file
578
NodeFlow/Env/RemoteFlowEnvironment.cs
Normal file
@@ -0,0 +1,578 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.NodeFlow.Env
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 远程流程环境
|
||||
/// </summary>
|
||||
public class RemoteFlowEnvironment : ChannelFlowTrigger<string>, IFlowEnvironment
|
||||
{
|
||||
/// <summary>
|
||||
/// 连接到远程环境后切换到的环境接口实现
|
||||
/// </summary>
|
||||
/// <param name="webSocketClient">连接到远程环境的客户端</param>
|
||||
public RemoteFlowEnvironment(RemoteEnvControl RemoteEnvControl)
|
||||
{
|
||||
remoteEnvControl = RemoteEnvControl;
|
||||
msgClient = new MsgControllerOfClient(this, RemoteEnvControl.SendAsync);
|
||||
RemoteEnvControl.EnvClient.MsgHandleHelper.AddModule(msgClient, (ex, send) =>
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
});
|
||||
}
|
||||
|
||||
//private readonly Func<string, object?, Task> SendCommandAsync;
|
||||
private readonly RemoteEnvControl remoteEnvControl;
|
||||
private readonly MsgControllerOfClient msgClient;
|
||||
private readonly ConcurrentDictionary<string, MethodDetails> MethodDetailss = [];
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 环境加载的节点集合
|
||||
/// Node Guid - Node Model
|
||||
/// </summary>
|
||||
private Dictionary<string, NodeModelBase> Nodes { get; } = [];
|
||||
|
||||
public event LoadDllHandler OnDllLoad;
|
||||
public event ProjectLoadedHandler OnProjectLoaded;
|
||||
public event NodeConnectChangeHandler OnNodeConnectChange;
|
||||
public event NodeCreateHandler OnNodeCreate;
|
||||
public event NodeRemoteHandler OnNodeRemote;
|
||||
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 ISereinIOC IOC => throw new NotImplementedException();
|
||||
|
||||
public string EnvName => FlowEnvironment.SpaceName;
|
||||
|
||||
public bool IsGlobalInterrupt => false;
|
||||
|
||||
public bool IsLcR => true;
|
||||
|
||||
public bool IsRcL => false;
|
||||
|
||||
public RunState FlowState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
public RunState FlipFlopState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
public IFlowEnvironment CurrentEnv => this;
|
||||
|
||||
public void SetConsoleOut()
|
||||
{
|
||||
var logTextWriter = new LogTextWriter(msg =>
|
||||
{
|
||||
OnEnvOut?.Invoke(msg);
|
||||
});
|
||||
Console.SetOut(logTextWriter);
|
||||
}
|
||||
|
||||
public void WriteLineObjToJson(object obj)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:WriteLineObjToJson");
|
||||
}
|
||||
|
||||
public async Task StartRemoteServerAsync(int port = 7525)
|
||||
{
|
||||
await Console.Out.WriteLineAsync("远程环境尚未实现的接口:StartRemoteServerAsync");
|
||||
}
|
||||
|
||||
public void StopRemoteServer()
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:StopRemoteServer");
|
||||
}
|
||||
|
||||
public async Task<SereinProjectData> GetProjectInfoAsync()
|
||||
{
|
||||
var prjectInfo = await msgClient.SendAndWaitDataAsync<SereinProjectData>(EnvMsgTheme.GetProjectInfo); // 等待服务器返回项目信息
|
||||
return prjectInfo;
|
||||
}
|
||||
|
||||
public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:LoadProject");
|
||||
|
||||
|
||||
// dll面板
|
||||
var libmds = flowEnvInfo.LibraryMds;
|
||||
foreach (var lib in libmds)
|
||||
{
|
||||
NodeLibrary nodeLibrary = new NodeLibrary
|
||||
{
|
||||
FullName = lib.LibraryName,
|
||||
FilePath = "Remote",
|
||||
};
|
||||
var mdInfos = lib.Mds.ToList();
|
||||
OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos)); // 通知UI创建dll面板显示
|
||||
|
||||
foreach (var mdInfo in mdInfos)
|
||||
{
|
||||
MethodDetailss.TryAdd(mdInfo.MethodName, new MethodDetails(mdInfo)); // 从DLL读取时生成元数据
|
||||
}
|
||||
}
|
||||
//flowSemaphore.
|
||||
|
||||
var projectData = flowEnvInfo.Project;
|
||||
|
||||
|
||||
List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
|
||||
List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>();
|
||||
|
||||
// 加载节点
|
||||
foreach (var nodeInfo in projectData.Nodes)
|
||||
{
|
||||
var controlType = FlowFunc.GetNodeControlType(nodeInfo);
|
||||
if (controlType == NodeControlType.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
MethodDetails? methodDetails;
|
||||
MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 尝试获取方法信息
|
||||
|
||||
var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载远程项目时创建节点
|
||||
nodeModel.LoadInfo(nodeInfo); // 创建节点model
|
||||
|
||||
|
||||
if (nodeModel is null)
|
||||
{
|
||||
nodeInfo.Guid = string.Empty;
|
||||
continue;
|
||||
}
|
||||
TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
|
||||
if (nodeInfo.ChildNodeGuids?.Length > 0)
|
||||
{
|
||||
regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids));
|
||||
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position));
|
||||
}
|
||||
else
|
||||
{
|
||||
ordinaryNodes.Add((nodeModel, nodeInfo.Position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载区域子项
|
||||
foreach ((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes)
|
||||
{
|
||||
foreach (var childNodeGuid in item.childNodeGuids)
|
||||
{
|
||||
Nodes.TryGetValue(childNodeGuid, out NodeModelBase? childNode);
|
||||
if (childNode is null)
|
||||
{
|
||||
// 节点尚未加载
|
||||
continue;
|
||||
}
|
||||
// 存在节点
|
||||
OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid));
|
||||
}
|
||||
}
|
||||
|
||||
// 加载节点
|
||||
foreach ((NodeModelBase nodeModel, PositionOfUI position) item in ordinaryNodes)
|
||||
{
|
||||
bool IsContinue = false;
|
||||
foreach ((NodeModelBase region, string[] childNodeGuids) item2 in regionChildNodes)
|
||||
{
|
||||
foreach (var childNodeGuid in item2.childNodeGuids)
|
||||
{
|
||||
if (item.nodeModel.Guid.Equals(childNodeGuid))
|
||||
{
|
||||
IsContinue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (IsContinue) continue;
|
||||
OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 确定节点之间的连接关系
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(100);
|
||||
foreach (var nodeInfo in projectData.Nodes)
|
||||
{
|
||||
if (!Nodes.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
|
||||
{
|
||||
// 不存在对应的起始节点
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
List<(ConnectionType connectionType, string[] guids)> allToNodes = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
|
||||
(ConnectionType.IsFail, nodeInfo.FalseNodes),
|
||||
(ConnectionType.IsError, nodeInfo.ErrorNodes),
|
||||
(ConnectionType.Upstream, nodeInfo.UpstreamNodes)];
|
||||
|
||||
List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
||||
.Select(info => (info.connectionType,
|
||||
info.guids.Where(guid => Nodes.ContainsKey(guid)).Select(guid => Nodes[guid])
|
||||
.ToArray()))
|
||||
.ToList();
|
||||
// 遍历每种类型的节点分支(四种)
|
||||
foreach ((ConnectionType connectionType, NodeModelBase[] toNodes) item in fromNodes)
|
||||
{
|
||||
// 遍历当前类型分支的节点(确认连接关系)
|
||||
foreach (var toNode in item.toNodes)
|
||||
{
|
||||
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
||||
toNode.Guid,
|
||||
item.connectionType,
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI创建节点
|
||||
|
||||
//ConnectNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
SetStartNode(projectData.StartNode);
|
||||
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs());
|
||||
|
||||
}
|
||||
private bool TryAddNode(NodeModelBase nodeModel)
|
||||
{
|
||||
nodeModel.Guid ??= Guid.NewGuid().ToString();
|
||||
Nodes[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;
|
||||
}
|
||||
|
||||
private void ConnectNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
|
||||
{
|
||||
if (fromNode is null || toNode is null || fromNode == toNode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ToExistOnFrom = true;
|
||||
var FromExistInTo = true;
|
||||
ConnectionType[] ct = [ConnectionType.IsSucceed,
|
||||
ConnectionType.IsFail,
|
||||
ConnectionType.IsError,
|
||||
ConnectionType.Upstream];
|
||||
|
||||
|
||||
foreach (ConnectionType 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)
|
||||
{
|
||||
Console.WriteLine("起始节点已与目标节点存在连接");
|
||||
|
||||
//return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 检查是否可能存在异常
|
||||
if (!ToExistOnFrom && FromExistInTo)
|
||||
{
|
||||
Console.WriteLine("目标节点不是起始节点的子节点,起始节点却是目标节点的父节点");
|
||||
return;
|
||||
}
|
||||
else if (ToExistOnFrom && !FromExistInTo)
|
||||
{
|
||||
//
|
||||
Console.WriteLine(" 起始节点不是目标节点的父节点,目标节点却是起始节点的子节点");
|
||||
return;
|
||||
}
|
||||
else // if (!ToExistOnFrom && !FromExistInTo)
|
||||
{
|
||||
// 可以正常连接
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fromNode.SuccessorNodes[connectionType].Add(toNode); // 添加到起始节点的子分支
|
||||
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支
|
||||
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
||||
toNode.Guid,
|
||||
connectionType,
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<FlowEnvInfo> GetEnvInfoAsync()
|
||||
{
|
||||
|
||||
var envInfo = await msgClient.SendAndWaitDataAsync<FlowEnvInfo>(EnvMsgTheme.GetEnvInfo);
|
||||
|
||||
return envInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
|
||||
{
|
||||
await Console.Out.WriteLineAsync("远程环境尚未实现的接口:ConnectRemoteEnv");
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
public void ExitRemoteEnv()
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:ExitRemoteEnv");
|
||||
}
|
||||
|
||||
public void LoadDll(string dllPath)
|
||||
{
|
||||
// 将dll文件发送到远程环境,由远程环境进行加载
|
||||
Console.WriteLine("远程环境尚未实现的接口:LoadDll");
|
||||
}
|
||||
|
||||
public bool RemoteDll(string assemblyFullName)
|
||||
{
|
||||
// 尝试移除远程环境中的加载了的依赖
|
||||
Console.WriteLine("远程环境尚未实现的接口:RemoteDll");
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:ClearAll");
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
// 远程环境下不需要UI上下文
|
||||
await msgClient.SendAsync(EnvMsgTheme.StartFlow);
|
||||
}
|
||||
|
||||
public async Task StartAsyncInSelectNode(string startNodeGuid)
|
||||
{
|
||||
await msgClient.SendAsync(EnvMsgTheme.StartFlowInSelectNode, new
|
||||
{
|
||||
nodeGuid = startNodeGuid
|
||||
});
|
||||
}
|
||||
|
||||
public async void ExitFlow()
|
||||
{
|
||||
await msgClient.SendAsync(EnvMsgTheme.ExitFlow, null);
|
||||
}
|
||||
|
||||
public void MoveNode(string nodeGuid, double x, double y)
|
||||
{
|
||||
OnNodeMoved.Invoke(new NodeMovedEventArgs(nodeGuid, x, y));
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.MoveNode,
|
||||
new
|
||||
{
|
||||
nodeGuid,
|
||||
x,
|
||||
y
|
||||
});
|
||||
}
|
||||
|
||||
public void SetStartNode(string nodeGuid)
|
||||
{
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.SetStartNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
||||
{
|
||||
//_ = RemoteEnv.SendAsync(EnvMsgTheme.ConnectNode, new
|
||||
//{
|
||||
// fromNodeGuid,
|
||||
// toNodeGuid,
|
||||
// connectionType = connectionType.ToString(),
|
||||
//});
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ConnectNode, new
|
||||
{
|
||||
fromNodeGuid,
|
||||
toNodeGuid,
|
||||
connectionType = connectionType.ToString(),
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<NodeInfo> CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null)
|
||||
{
|
||||
var nodeInfo = await msgClient.SendAndWaitDataAsync<NodeInfo>(EnvMsgTheme.CreateNode, new
|
||||
{
|
||||
nodeType = nodeControlType.ToString(),
|
||||
position = position,
|
||||
mdInfo = methodDetailsInfo,
|
||||
});
|
||||
|
||||
MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息
|
||||
var nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 远程环境下加载节点
|
||||
TryAddNode(nodeModel);
|
||||
nodeModel.LoadInfo(nodeInfo);
|
||||
|
||||
// 通知UI更改
|
||||
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position));
|
||||
return nodeInfo;
|
||||
}
|
||||
|
||||
public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
||||
{
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.RemoveConnect, new
|
||||
{
|
||||
fromNodeGuid,
|
||||
toNodeGuid,
|
||||
connectionType = connectionType.ToString(),
|
||||
});
|
||||
}
|
||||
|
||||
public void RemoveNode(string nodeGuid)
|
||||
{
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.RemoveNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
}
|
||||
|
||||
public void ActivateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.ActivateFlipflopNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
}
|
||||
|
||||
public void TerminateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.TerminateFlipflopNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass)
|
||||
{
|
||||
var state = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.SetNodeInterrupt, // 设置节点中断
|
||||
new
|
||||
{
|
||||
nodeGuid,
|
||||
interruptClass = interruptClass.ToString(),
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> AddInterruptExpressionAsync(string key, string expression)
|
||||
{
|
||||
var state = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.AddInterruptExpression, // 设置节点/对象的中断表达式
|
||||
new
|
||||
{
|
||||
key,
|
||||
expression,
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
public void SetMonitorObjState(string key, bool isMonitor)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:SetMonitorObjState");
|
||||
}
|
||||
|
||||
public async Task<(bool, string[])> CheckObjMonitorStateAsync(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
var exps = Array.Empty<string>();
|
||||
return (false, exps);
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await msgClient.SendAndWaitDataAsync<(bool, string[])>(EnvMsgTheme.SetNodeInterrupt, // 检查并获取节点/对象是否正在监视、以及监视的表达式
|
||||
new
|
||||
{
|
||||
key,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public async Task<ChannelFlowInterrupt.CancelType> GetOrCreateGlobalInterruptAsync()
|
||||
{
|
||||
await Console.Out.WriteLineAsync("远程环境尚未实现的接口:GetOrCreateGlobalInterruptAsync");
|
||||
return ChannelFlowInterrupt.CancelType.Error;
|
||||
}
|
||||
|
||||
public bool TryGetMethodDetailsInfo(string methodName, out MethodDetailsInfo mdInfo)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:TryGetMethodDetailsInfo");
|
||||
mdInfo = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetDelegateDetails(string methodName, out DelegateDetails del)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:TryGetDelegateDetails");
|
||||
del = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:MonitorObjectNotification");
|
||||
|
||||
}
|
||||
|
||||
public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:TriggerInterrupt");
|
||||
}
|
||||
|
||||
public void NodeLocated(string nodeGuid)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:NodeLocated");
|
||||
}
|
||||
|
||||
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
||||
{
|
||||
|
||||
//Console.WriteLine($"通知远程环境修改节点数据:{nodeGuid},name:{path},value:{value}");
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new
|
||||
{
|
||||
nodeGuid = nodeGuid,
|
||||
path = path,
|
||||
value = value.ToString(),
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,24 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Core.NodeFlow;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.Library.Ex;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Network.WebSocketCommunication;
|
||||
using Serein.Library.Web;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.Library;
|
||||
using Serein.NodeFlow.Env;
|
||||
using Serein.NodeFlow.Model;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml.Linq;
|
||||
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
||||
|
||||
namespace Serein.NodeFlow
|
||||
{
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 流程启动器
|
||||
/// </summary>
|
||||
public class FlowStarter
|
||||
{
|
||||
|
||||
public FlowStarter()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 控制触发器
|
||||
/// 控制全局触发器的结束
|
||||
/// </summary>
|
||||
private CancellationTokenSource _flipFlopCts = null;
|
||||
private CancellationTokenSource? _flipFlopCts;
|
||||
|
||||
/// <summary>
|
||||
/// 是否停止启动
|
||||
@@ -45,11 +28,8 @@ namespace Serein.NodeFlow
|
||||
/// <summary>
|
||||
/// 结束运行时需要执行的方法
|
||||
/// </summary>
|
||||
private Func<Task> ExitAction { get; set; } = null;
|
||||
/// <summary>
|
||||
/// 运行的上下文
|
||||
/// </summary>
|
||||
private IDynamicContext Context { get; set; } = null;
|
||||
private Func<Task>? ExitAction { get; set; }
|
||||
|
||||
private void CheckStartState()
|
||||
{
|
||||
if (IsStopStart)
|
||||
@@ -62,11 +42,17 @@ namespace Serein.NodeFlow
|
||||
/// <summary>
|
||||
/// 从选定的节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="env"></param>
|
||||
/// <param name="startNode"></param>
|
||||
/// <returns></returns>
|
||||
public async Task StartFlowInSelectNodeAsync(NodeModelBase startNode)
|
||||
public async Task StartFlowInSelectNodeAsync(IFlowEnvironment env, NodeModelBase startNode)
|
||||
{
|
||||
if (Context is null) return;
|
||||
IDynamicContext Context;
|
||||
#if NET6_0_OR_GREATER
|
||||
Context = new Serein.Library.Core.NodeFlow.DynamicContext(env); // 从起始节点启动流程时创建上下文
|
||||
#else
|
||||
Context = new Serein.Library.Framework.NodeFlow.DynamicContext(env);
|
||||
#endif
|
||||
await startNode.StartFlowAsync(Context); // 开始运行时从选定节点开始运行
|
||||
}
|
||||
|
||||
@@ -111,7 +97,8 @@ namespace Serein.NodeFlow
|
||||
#region 选择运行环境的上下文
|
||||
|
||||
// 判断使用哪一种流程上下文
|
||||
#if NET6_0_OR_GREATER
|
||||
IDynamicContext Context;
|
||||
#if NET6_0_OR_GREATER
|
||||
Context = new Serein.Library.Core.NodeFlow.DynamicContext(env); // 从起始节点启动流程时创建上下文
|
||||
#else
|
||||
Context = new Serein.Library.Framework.NodeFlow.DynamicContext(env);
|
||||
@@ -235,6 +222,9 @@ namespace Serein.NodeFlow
|
||||
env.IOC.Run<WebApiServer>(web => {
|
||||
web?.Stop();
|
||||
});
|
||||
env.IOC.Run<WebSocketServer>(server => {
|
||||
server?.Stop();
|
||||
});
|
||||
|
||||
foreach (MethodDetails? md in exitMethods)
|
||||
{
|
||||
@@ -243,7 +233,6 @@ namespace Serein.NodeFlow
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
await dd.InvokeAsync(md.ActingInstance, [Context]);
|
||||
//((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
||||
}
|
||||
|
||||
TerminateAllGlobalFlipflop();
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
{
|
||||
@@ -12,10 +10,12 @@ namespace Serein.NodeFlow.Model
|
||||
public class CompositeActionNode : NodeModelBase
|
||||
{
|
||||
public List<SingleActionNode> ActionNodes;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 组合动作节点(用于动作区域)
|
||||
/// </summary>
|
||||
public CompositeActionNode(List<SingleActionNode> actionNodes)
|
||||
public CompositeActionNode(IFlowEnvironment environment, List<SingleActionNode> actionNodes):base(environment)
|
||||
{
|
||||
ActionNodes = actionNodes;
|
||||
}
|
||||
@@ -30,6 +30,7 @@ namespace Serein.NodeFlow.Model
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public override NodeInfo? ToInfo()
|
||||
{
|
||||
if (MethodDetails is null) return null;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
{
|
||||
@@ -10,6 +9,10 @@ namespace Serein.NodeFlow.Model
|
||||
/// </summary>
|
||||
public class CompositeConditionNode : NodeModelBase
|
||||
{
|
||||
public CompositeConditionNode(IFlowEnvironment environment):base(environment)
|
||||
{
|
||||
|
||||
}
|
||||
public List<SingleConditionNode> ConditionNodes { get; } = [];
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.Library;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
{
|
||||
@@ -9,7 +8,10 @@ namespace Serein.NodeFlow.Model
|
||||
/// </summary>
|
||||
public class SingleActionNode : NodeModelBase
|
||||
{
|
||||
|
||||
public SingleActionNode(IFlowEnvironment environment):base(environment)
|
||||
{
|
||||
|
||||
}
|
||||
public override Parameterdata[] GetParameterdatas()
|
||||
{
|
||||
if (base.MethodDetails.ParameterDetailss.Length > 0)
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.NodeFlow.Tool.SereinExpression;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
{
|
||||
@@ -12,6 +9,10 @@ namespace Serein.NodeFlow.Model
|
||||
/// </summary>
|
||||
public class SingleConditionNode : NodeModelBase
|
||||
{
|
||||
public SingleConditionNode(IFlowEnvironment environment):base(environment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为自定义参数
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.NodeFlow.Tool.SereinExpression;
|
||||
using System.Text;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
{
|
||||
@@ -12,6 +9,10 @@ namespace Serein.NodeFlow.Model
|
||||
/// </summary>
|
||||
public class SingleExpOpNode : NodeModelBase
|
||||
{
|
||||
public SingleExpOpNode(IFlowEnvironment environment) : base(environment)
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 表达式
|
||||
/// </summary>
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.Library.Ex;
|
||||
using Serein.Library.NodeFlow.Tool;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.NodeFlow.Env;
|
||||
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 触发器节点
|
||||
/// </summary>
|
||||
public class SingleFlipflopNode : NodeModelBase
|
||||
{
|
||||
//public override async Task<object?> Executing(IDynamicContext context)
|
||||
//public override Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
//{
|
||||
// NextOrientation = Library.Enums.ConnectionType.IsError;
|
||||
// RuningException = new FlipflopException ("无法以非await/async的形式调用触发器");
|
||||
// return null;
|
||||
//}
|
||||
|
||||
public SingleFlipflopNode(IFlowEnvironment environment) : base(environment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 执行触发器进行等待触发
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
{
|
||||
#region 执行前中断
|
||||
@@ -76,10 +77,11 @@ namespace Serein.NodeFlow.Model
|
||||
// flipflopTask?.Dispose();
|
||||
}
|
||||
}
|
||||
public static object GetContextValueDynamic(dynamic context)
|
||||
{
|
||||
return context.Value; // dynamic 会在运行时处理类型
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取触发器参数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override Parameterdata[] GetParameterdatas()
|
||||
{
|
||||
if (base.MethodDetails.ParameterDetailss.Length > 0)
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow
|
||||
namespace Serein.NodeFlow
|
||||
{
|
||||
public class MoveNodeData
|
||||
{
|
||||
public NodeControlType NodeControlType { get; set; }
|
||||
// public MethodDetails MethodDetails { get; set; }
|
||||
public MethodDetailsInfo MethodDetailsInfo { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.0.13</Version>
|
||||
<Version>1.0.14</Version>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
94
NodeFlow/Tool/LogTextWriter.cs
Normal file
94
NodeFlow/Tool/LogTextWriter.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace Serein.NodeFlow.Tool
|
||||
{
|
||||
/// <summary>
|
||||
/// 捕获Console输出
|
||||
/// </summary>
|
||||
public class LogTextWriter : TextWriter
|
||||
{
|
||||
private readonly Action<string> logAction; // 更新日志UI的委托
|
||||
private readonly StringWriter stringWriter = new(); // 缓存日志内容
|
||||
private readonly Channel<string> logChannel = Channel.CreateUnbounded<string>(); // 日志管道
|
||||
//private int writeCount = 0; // 写入计数器
|
||||
//private const int maxWrites = 500; // 写入最大计数
|
||||
|
||||
/// <summary>
|
||||
/// 定义输出委托与清除输出内容委托
|
||||
/// </summary>
|
||||
/// <param name="logAction"></param>
|
||||
public LogTextWriter(Action<string> logAction)
|
||||
{
|
||||
this.logAction = logAction;
|
||||
|
||||
// 异步启动日志处理任务,不阻塞主线程
|
||||
Task.Run(ProcessLogQueueAsync);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编码类型
|
||||
/// </summary>
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
stringWriter.Write(value);
|
||||
if (value == '\n')
|
||||
{
|
||||
EnqueueLog();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return;
|
||||
stringWriter.Write(value);
|
||||
if (value.Contains('\n'))
|
||||
{
|
||||
EnqueueLog();
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteLine(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return;
|
||||
stringWriter.WriteLine(value);
|
||||
EnqueueLog();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将日志加入通道
|
||||
/// </summary>
|
||||
private void EnqueueLog()
|
||||
{
|
||||
var log = stringWriter.ToString();
|
||||
stringWriter.GetStringBuilder().Clear();
|
||||
if (!logChannel.Writer.TryWrite(log))
|
||||
{
|
||||
// 如果写入失败(不太可能),则直接丢弃日志或处理
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步处理日志队列
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task ProcessLogQueueAsync()
|
||||
{
|
||||
await foreach (var log in logChannel.Reader.ReadAllAsync()) // 异步读取日志通道
|
||||
{
|
||||
logAction?.Invoke(log); // 执行日志写入到UI的委托
|
||||
|
||||
//writeCount++;
|
||||
//if (writeCount >= maxWrites)
|
||||
//{
|
||||
// writeCount = 0; // 重置计数器
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.Core.NodeFlow;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using Serein.Library;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Serein.NodeFlow.Tool;
|
||||
|
||||
@@ -77,7 +72,7 @@ public static class NodeMethodDetailsHelper
|
||||
Type? returnType;
|
||||
bool isTask = IsGenericTask(method.ReturnType, out var taskResult);
|
||||
|
||||
if (attribute.MethodDynamicType == Library.Enums.NodeType.Flipflop)
|
||||
if (attribute.MethodDynamicType == Library.NodeType.Flipflop)
|
||||
{
|
||||
if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
{
|
||||
@@ -126,7 +121,7 @@ public static class NodeMethodDetailsHelper
|
||||
|
||||
|
||||
|
||||
var md = new MethodDetails
|
||||
var md = new MethodDetails() // 从DLL生成方法描述
|
||||
{
|
||||
ActingInstanceType = type,
|
||||
// ActingInstance = instance,
|
||||
@@ -137,7 +132,7 @@ public static class NodeMethodDetailsHelper
|
||||
ParameterDetailss = explicitDataOfParameters,
|
||||
ReturnType = returnType,
|
||||
};
|
||||
var dd = new DelegateDetails( emitMethodType, methodDelegate) ;
|
||||
var dd = new DelegateDetails(emitMethodType, methodDelegate) ;
|
||||
return (md, dd);
|
||||
|
||||
}
|
||||
@@ -270,6 +265,7 @@ public static class NodeMethodDetailsHelper
|
||||
/// <returns></returns>
|
||||
private static string GetExplicitTypeName(Type type)
|
||||
{
|
||||
|
||||
return type switch
|
||||
{
|
||||
Type t when t.IsEnum => "Select",
|
||||
|
||||
Reference in New Issue
Block a user