将部分节点基类与表达式工具类从nodeflow迁移到library,重写了环境与工作台的交互,解耦节点的获取,下一部分将尝试远程登录环境编辑流程。

This commit is contained in:
fengjiayi
2024-10-15 10:55:41 +08:00
parent 4338554384
commit dbbde4f03e
48 changed files with 3687 additions and 292 deletions

View File

@@ -233,7 +233,7 @@ namespace Serein.NodeFlow.Base
try
{
object?[]? args = GetParameters(context, this, md);
result = await dd.Invoke(md.ActingInstance, args);
result = await dd.InvokeAsync(md.ActingInstance, args);
NextOrientation = ConnectionType.IsSucceed;
return result;
}

View File

@@ -8,9 +8,13 @@ using Serein.Library.Utils;
using Serein.NodeFlow.Base;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Tool;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Cryptography;
using System.Xml.Linq;
using static Serein.Library.Utils.ChannelFlowInterrupt;
using static Serein.NodeFlow.FlowStarter;
@@ -180,11 +184,20 @@ namespace Serein.NodeFlow
/// </summary>
public ISereinIOC IOC { get => this; }
/// <summary>
/// Library 与 MethodDetailss的依赖关系
/// </summary>
public ConcurrentDictionary<NodeLibrary, List<MethodDetails>> MethodDetailsOfLibrarys { get; } = [];
/// <summary>
/// 描述所有DLL中NodeAction特性的方法的原始副本
/// 存储已加载的程序集
/// </summary>
public Dictionary<NodeLibrary, List<MethodDetails>> MethodDetailss { get; } = [];
public ConcurrentDictionary<string, NodeLibrary> Librarys { get; } = [];
/// <summary>
/// 存储已加载的方法信息。描述所有DLL中NodeAction特性的方法的原始副本
/// </summary>
public ConcurrentDictionary<string, MethodDetails> MethodDetailss { get; } = [];
#endregion
@@ -194,17 +207,6 @@ namespace Serein.NodeFlow
/// </summary>
public readonly SereinIOC sereinIOC;
/// <summary>
/// 存储加载的程序集路径
/// </summary>
/// <summary>
/// 存储加载的程序集
/// </summary>
public List<NodeLibrary> NodeLibrarys { get; } = [];
/// <summary>
/// 环境加载的节点集合
/// Node Guid - Node Model
@@ -266,7 +268,9 @@ namespace Serein.NodeFlow
#endregion
#region
#region
/// <summary>
/// 异步运行
@@ -281,15 +285,22 @@ namespace Serein.NodeFlow
List<MethodDetails> initMethods = [];
List<MethodDetails> loadMethods = [];
List<MethodDetails> exitMethods = [];
foreach(var mds in MethodDetailss.Values)
{
var initMds = mds.Where(it => it.MethodDynamicType == NodeType.Init);
var loadMds = mds.Where(it => it.MethodDynamicType == NodeType.Loading);
var exitMds = mds.Where(it => it.MethodDynamicType == NodeType.Exit);
initMethods.AddRange(initMds);
loadMethods.AddRange(loadMds);
exitMethods.AddRange(exitMds);
}
//foreach(var mds in MethodDetailss.Values)
//{
// var initMds = mds.Where(it => it.MethodDynamicType == NodeType.Init);
// var loadMds = mds.Where(it => it.MethodDynamicType == NodeType.Loading);
// var exitMds = mds.Where(it => it.MethodDynamicType == NodeType.Exit);
// initMethods.AddRange(initMds);
// loadMethods.AddRange(loadMds);
// exitMethods.AddRange(exitMds);
//}
var initMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Init);
var loadMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Loading);
var exitMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Exit);
initMethods.AddRange(initMds);
loadMethods.AddRange(loadMds);
exitMethods.AddRange(exitMds);
this.IOC.Reset(); // 开始运行时清空ioc中注册的实例
this.IOC.CustomRegisterInstance(typeof(IFlowEnvironment).FullName,this);
@@ -354,8 +365,8 @@ namespace Serein.NodeFlow
public void ClearAll()
{
//LoadedAssemblyPaths.Clear();
NodeLibrarys.Clear();
MethodDetailss.Clear();
//NodeLibrarys.Clear();
//MethodDetailss.Clear();
}
@@ -396,8 +407,10 @@ namespace Serein.NodeFlow
}
else
{
TryGetMethodDetails(nodeInfo.MethodName, out MethodDetails? methodDetails); // 加载项目时尝试获取方法信息
if(controlType == NodeControlType.ExpOp || controlType == NodeControlType.ExpOp)
//TryGetMethodDetails(nodeInfo.MethodName, out MethodDetailsInfo? methodDetailsInfo);
MethodDetailss.TryGetValue(nodeInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息
if (controlType == NodeControlType.ExpOp || controlType == NodeControlType.ExpOp)
{
methodDetails ??= new MethodDetails();
}
@@ -495,22 +508,53 @@ namespace Serein.NodeFlow
SetStartNode(project.StartNode);
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs());
}
/// <summary>
/// 保存项目为项目文件
/// 加载远程项目
/// </summary>
/// <param name="addres">远程项目地址</param>
/// <param name="port">远程项目端口</param>
/// <param name="token">密码</param>
public void LoadRemoteProject(string addres, int port, string token)
{
// -- 第1种直接从远程环境复制所有dll信息项目信息在本地打开安全问题
// 第2种WebSocket连接到远程环境实时接收远程环境的响应
Console.WriteLine($"准备连接:{addres}:{port},{token}");
// bool success = false;
//try
//{
// TcpClient tcpClient = new TcpClient();
// var result = tcpClient.BeginConnect(addres, port, null, null);
// success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(3));
//}
//catch (Exception ex)
//{
//}
//if (!success)
//{
// Console.WriteLine($"无法连接远程:{addres}:{port}");
//}
}
/// <summary>
/// 序列化当前项目的依赖信息、节点信息
/// </summary>
/// <returns></returns>
public SereinProjectData SaveProject()
public SereinProjectData GetProjectInfo()
{
var projectData = new SereinProjectData()
{
Librarys = NodeLibrarys.Select(assemblies => assemblies.Assembly.ToLibrary()).ToArray(),
Librarys = Librarys.Values.Select(assemblies => assemblies.Assembly.ToLibrary()).ToArray(),
Nodes = Nodes.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
StartNode = Nodes.Values.FirstOrDefault(it => it.IsStart)?.Guid,
};
return projectData;
}
/// <summary>
/// 从文件路径中加载DLL
/// </summary>
@@ -521,9 +565,14 @@ namespace Serein.NodeFlow
LoadDllNodeInfo(dllPath);
}
/// <summary>
/// 移除DLL
/// </summary>
/// <param name="assemblyFullName"></param>
/// <returns></returns>
public bool RemoteDll(string assemblyFullName)
{
var library = NodeLibrarys.FirstOrDefault(nl => assemblyFullName.Equals(nl.Assembly.FullName));
var library = Librarys.Values.FirstOrDefault(nl => assemblyFullName.Equals(nl.Assembly.FullName));
if(library is null)
{
return false;
@@ -535,12 +584,14 @@ namespace Serein.NodeFlow
.ToDictionary(
key => key.Key,
group => group.Count());
if(Nodes.Count == 0)
{
return true; // 当前无节点,可以直接删除
}
if (MethodDetailss.TryGetValue(library,out var mds)) // 存在方法
if (MethodDetailsOfLibrarys.TryGetValue(library,out var mds)) // 存在方法
{
foreach(var md in mds)
{
@@ -548,12 +599,17 @@ namespace Serein.NodeFlow
{
if (count > 0)
{
return false; // 创建过相关的节点
return false; // 创建过相关的节点,无法移除
}
}
}
MethodDetailss.Remove(library);
return true; // 没有创建相关的节点
// 开始移除相关信息
foreach (var md in mds)
{
MethodDetailss.TryRemove(md.MethodName, out _);
}
MethodDetailsOfLibrarys.TryRemove(library, out _);
return true;
}
else
{
@@ -567,9 +623,14 @@ namespace Serein.NodeFlow
/// </summary>
/// <param name="nodeControlType"></param>
/// <param name="position"></param>
/// <param name="methodDetails"></param>
public void CreateNode(NodeControlType nodeControlType, Position position, MethodDetails? methodDetails = null)
/// <param name="methodDetailsInfo">如果是表达式节点条件节点该项为null</param>
public void CreateNode(NodeControlType nodeControlType, Position position, MethodDetailsInfo? methodDetailsInfo = null)
{
MethodDetails? methodDetails = null;
if (methodDetailsInfo != null &&!MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out methodDetails))
{
return;
}
var nodeModel = CreateNode(nodeControlType, methodDetails);
TryAddNode(nodeModel);
@@ -646,8 +707,8 @@ namespace Serein.NodeFlow
/// <summary>
/// 连接节点
/// </summary>
/// <param name="fromNode">起始节点</param>
/// <param name="toNode">目标节点</param>
/// <param name="fromNodeGuid">起始节点</param>
/// <param name="toNodeGuid">目标节点</param>
/// <param name="connectionType">连接关系</param>
public void ConnectNode(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
{
@@ -684,13 +745,13 @@ namespace Serein.NodeFlow
/// <summary>
/// 获取方法描述
/// </summary>
public bool TryGetMethodDetails(string name, out MethodDetails? md)
public bool TryGetMethodDetailsInfo(string name, out MethodDetailsInfo? md)
{
if (!string.IsNullOrEmpty(name))
{
foreach(var mds in MethodDetailss.Values)
foreach(var t_md in MethodDetailss.Values)
{
md = mds.FirstOrDefault(it => it.MethodName == name);
md = t_md.ToInfo();
if(md != null)
{
return true;
@@ -706,6 +767,16 @@ namespace Serein.NodeFlow
}
}
/// <summary>
/// <para>通过方法名称获取对应的Emit委托</para>
/// <para>方法无入参时需要传入空数组void方法自动返回null</para>
/// <para>普通方法Func&lt;object,object[],object&gt;</para>
/// <para>异步方法Func&lt;object,object[],Task&gt;</para>
/// <para>异步有返回值方法Func&lt;object,object[],Task&lt;object&gt;&gt;</para>
/// </summary>
/// <param name="methodName"></param>
/// <param name="delegateDetails"></param>
/// <returns></returns>
public bool TryGetDelegateDetails(string methodName, out DelegateDetails? delegateDetails)
{
@@ -875,7 +946,8 @@ namespace Serein.NodeFlow
}
}
} /// <summary>
}
/// <summary>
/// 关闭全局触发器
/// </summary>
/// <param name="nodeGuid"></param>
@@ -888,13 +960,48 @@ namespace Serein.NodeFlow
flowStarter.TerminateGlobalFlipflopRuning(flipflopNode);
}
}
/// <summary>
/// 环境执行中断
/// </summary>
/// <returns></returns>
public Task<CancelType> GetOrCreateGlobalInterruptAsync()
{
IsGlobalInterrupt = true;
return ChannelFlowInterrupt.GetOrCreateChannelAsync(this.EnvName);
}
/// <summary>
/// 获取当前环境信息(远程连接)
/// </summary>
/// <returns></returns>
public object GetEnvInfo()
{
Dictionary<NodeLibrary, List<MethodDetailsInfo>> LibraryMds = [];
foreach (var mdskv in this.MethodDetailsOfLibrarys)
{
var library = mdskv.Key;
var mds = mdskv.Value;
foreach (var md in mds)
{
if (!LibraryMds.TryGetValue(library, out var t_mds))
{
t_mds = new List<MethodDetailsInfo>();
LibraryMds[library] = t_mds;
}
var mdInfo = md.ToInfo();
mdInfo.LibraryName = library.Assembly.GetName().FullName;
t_mds.Add(mdInfo);
}
}
var project = this.GetProjectInfo();
return new
{
project = project,
envNode = LibraryMds.Values,
};
}
/// <summary>
/// Guid 转 NodeModel
@@ -902,7 +1009,7 @@ namespace Serein.NodeFlow
/// <param name="nodeGuid">节点Guid</param>
/// <returns>节点Model</returns>
/// <exception cref="ArgumentNullException">无法获取节点、Guid/节点为null时报错</exception>
public NodeModelBase? GuidToModel(string nodeGuid)
private NodeModelBase? GuidToModel(string nodeGuid)
{
if (string.IsNullOrEmpty(nodeGuid))
{
@@ -931,10 +1038,15 @@ namespace Serein.NodeFlow
(var nodeLibrary, var registerTypes, var mdlist) = LoadAssembly(dllPath);
if (nodeLibrary is not null && mdlist.Count > 0)
{
MethodDetailss.Add(nodeLibrary, mdlist);
NodeLibrarys.Add(nodeLibrary);
Librarys.TryAdd(nodeLibrary.Name, nodeLibrary);
MethodDetailsOfLibrarys.TryAdd(nodeLibrary, mdlist);
foreach(var md in mdlist)
{
MethodDetailss.TryAdd(md.MethodName, md);
}
foreach(var kv in registerTypes)
foreach (var kv in registerTypes)
{
if (!AutoRegisterTypes.TryGetValue(kv.Key, out var types))
{
@@ -943,8 +1055,8 @@ namespace Serein.NodeFlow
}
types.AddRange(kv.Value);
}
OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdlist)); // 通知UI创建dll面板显示
var mdInfo = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息
OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfo)); // 通知UI创建dll面板显示
}
}
@@ -1046,6 +1158,7 @@ namespace Serein.NodeFlow
var nodeLibrary = new NodeLibrary
{
Name = assembly.GetName().FullName,
Assembly = assembly,
Path = dllPath,
};

View File

@@ -24,15 +24,7 @@ namespace Serein.NodeFlow
/// </summary>
public class FlowStarter
{
/// <summary>
/// 全局触发器CTS
/// </summary>
public const string FlipFlopCtsName = "<>.FlowFlipFlopCts";
/// <summary>
/// 流程运行CTS
/// </summary>
public const string FlowRungCtsName = "<>.FlowRungCtsName";
public FlowStarter()
{
}
@@ -209,7 +201,7 @@ namespace Serein.NodeFlow
{
throw new Exception("不存在对应委托");
}
await dd.Invoke(md.ActingInstance, [Context]);
await dd.InvokeAsync(md.ActingInstance, [Context]);
//((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
}
Context.Env.IOC.Build(); // 绑定初始化时注册的类型
@@ -230,7 +222,7 @@ namespace Serein.NodeFlow
{
throw new Exception("不存在对应委托");
}
await dd.Invoke(md.ActingInstance, [Context]);
await dd.InvokeAsync(md.ActingInstance, [Context]);
//((Action<object, object?[]?>)del).Invoke(md.ActingInstance, [Context]);
//((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
}
@@ -250,7 +242,7 @@ namespace Serein.NodeFlow
{
throw new Exception("不存在对应委托");
}
await dd.Invoke(md.ActingInstance, [Context]);
await dd.InvokeAsync(md.ActingInstance, [Context]);
//((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
}
@@ -277,7 +269,7 @@ namespace Serein.NodeFlow
env.FlipFlopState = RunState.Running;
// 如果存在需要启动的触发器,则开始启动
_flipFlopCts = new CancellationTokenSource();
env.IOC.CustomRegisterInstance(FlipFlopCtsName, _flipFlopCts,false);
env.IOC.CustomRegisterInstance(NodeStaticConfig.FlipFlopCtsName, _flipFlopCts,false);
// 使用 TaskCompletionSource 创建未启动的触发器任务
var tasks = flipflopNodes.Select(async node =>

View File

@@ -26,7 +26,7 @@ namespace Serein.NodeFlow.Model
throw new NotImplementedException("动作区域暂未实现");
}
internal override Parameterdata[] GetParameterdatas()
public override Parameterdata[] GetParameterdatas()
{
return [];
}

View File

@@ -37,7 +37,7 @@ namespace Serein.NodeFlow.Model
break;
}
}
return Task.FromResult(PreviousNode?.GetFlowData()); // 条件区域透传上一节点的数据
return Task.FromResult( PreviousNode?.GetFlowData()); // 条件区域透传上一节点的数据
}
@@ -57,7 +57,7 @@ namespace Serein.NodeFlow.Model
}
}
internal override Parameterdata[] GetParameterdatas()
public override Parameterdata[] GetParameterdatas()
{
return [];
}

View File

@@ -10,7 +10,7 @@ namespace Serein.NodeFlow.Model
public class SingleActionNode : NodeModelBase
{
internal override Parameterdata[] GetParameterdatas()
public override Parameterdata[] GetParameterdatas()
{
if (base.MethodDetails.ParameterDetailss.Length > 0)
{

View File

@@ -71,7 +71,7 @@ namespace Serein.NodeFlow.Model
return Task.FromResult(result);
}
internal override Parameterdata[] GetParameterdatas()
public override Parameterdata[] GetParameterdatas()
{
var value = CustomData switch
{
@@ -90,8 +90,6 @@ namespace Serein.NodeFlow.Model
}];
}
public override NodeModelBase LoadInfo(NodeInfo nodeInfo)
{
var node = this;

View File

@@ -49,7 +49,7 @@ namespace Serein.NodeFlow.Model
}
internal override Parameterdata[] GetParameterdatas()
public override Parameterdata[] GetParameterdatas()
{
return [new Parameterdata{ Expression = Expression}];
}

View File

@@ -42,7 +42,7 @@ namespace Serein.NodeFlow.Model
try
{
var args = GetParameters(context, this, md);
var result = await dd.Invoke(md.ActingInstance, args);
var result = await dd.InvokeAsync(md.ActingInstance, args);
dynamic flipflopContext = result;
FlipflopStateType flipflopStateType = flipflopContext.State;
NextOrientation = flipflopStateType.ToContentType();
@@ -80,7 +80,7 @@ namespace Serein.NodeFlow.Model
{
return context.Value; // dynamic 会在运行时处理类型
}
internal override Parameterdata[] GetParameterdatas()
public override Parameterdata[] GetParameterdatas()
{
if (base.MethodDetails.ParameterDetailss.Length > 0)
{

View File

@@ -11,6 +11,7 @@ namespace Serein.NodeFlow
public class MoveNodeData
{
public NodeControlType NodeControlType { get; set; }
public MethodDetails MethodDetails { get; set; }
// public MethodDetails MethodDetails { get; set; }
public MethodDetailsInfo MethodDetailsInfo { get; set; }
}
}

View File

@@ -1,5 +1,4 @@
using Serein.Library.Api;
using Serein.Library.Enums;
using Serein.Library.Enums;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@@ -19,18 +19,25 @@
</PropertyGroup>
<ItemGroup>
<Compile Remove="Base\**" />
<Compile Remove="bin\**" />
<Compile Remove="SerinExpression\**" />
<Compile Remove="Tool\SereinExpression\**" />
<EmbeddedResource Remove="Base\**" />
<EmbeddedResource Remove="bin\**" />
<EmbeddedResource Remove="SerinExpression\**" />
<EmbeddedResource Remove="Tool\SereinExpression\**" />
<None Remove="Base\**" />
<None Remove="bin\**" />
<None Remove="SerinExpression\**" />
<None Remove="Tool\SereinExpression\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="ConnectionType.cs" />
<Compile Remove="DynamicContext.cs" />
<Compile Remove="MethodDetails.cs" />
<Compile Remove="NodeStaticConfig.cs" />
<Compile Remove="Tool\Attribute.cs" />
<Compile Remove="Tool\DynamicTool.cs" />
<Compile Remove="Tool\ExpressionHelper.cs" />