尝试使用源生成器规范NodeModel代码逻辑

This commit is contained in:
fengjiayi
2024-10-20 12:10:57 +08:00
parent 9931fa7436
commit e38833a58c
127 changed files with 5173 additions and 1839 deletions

View 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);
}
}

View 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
View 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;
//}
}
}

View 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
}
}

View 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);
}
}
}

View 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(),
});
}
}
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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; } = [];

View File

@@ -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)

View File

@@ -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>
/// 是否为自定义参数

View File

@@ -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>

View File

@@ -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)

View File

@@ -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; }
}
}

View File

@@ -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>

View 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; // 重置计数器
//}
}
}
}
}

View File

@@ -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",