重新设计了FlowLIbrary相关类;为工作台默认添加了基础依赖、默认画布。

This commit is contained in:
fengjiayi
2025-07-28 12:16:29 +08:00
parent 6354c4c7fd
commit ccb8e49abc
39 changed files with 497 additions and 453 deletions

View File

@@ -27,7 +27,7 @@ namespace Serein.FlowStartTool
Console.WriteLine($"{DateTime.Now} [{infoType}] : {value}{Environment.NewLine}");
};
await flowEnvironment.StartRemoteServerAsync(7525); // 启动 web socket 监听远程请求
//await flowEnvironment.StartRemoteServerAsync(7525); // 启动 web socket 监听远程请求
IsRuning = false;
}

View File

@@ -179,19 +179,14 @@ namespace Serein.Library.Api
/// </summary>
public class LoadDllEventArgs : FlowEventArgs
{
public LoadDllEventArgs(NodeLibraryInfo nodeLibraryInfo, List<MethodDetailsInfo> MethodDetailss)
public LoadDllEventArgs(FlowLibraryInfo nodeLibraryInfo)
{
this.NodeLibraryInfo = nodeLibraryInfo;
this.MethodDetailss = MethodDetailss;
}
/// <summary>
/// 已加载了的程序集
/// </summary>
public NodeLibraryInfo NodeLibraryInfo { get;}
/// <summary>
/// dll文件中有效的流程方法描述
/// </summary>
public List<MethodDetailsInfo> MethodDetailss { get;}
public FlowLibraryInfo NodeLibraryInfo { get;}
}
/// <summary>
@@ -791,7 +786,7 @@ namespace Serein.Library.Api
/// <summary>
/// 是否全局中断
/// </summary>
bool IsGlobalInterrupt { get; }
bool _IsGlobalInterrupt { get; }
/// <summary>
/// <para>表示是否正在控制远程</para>

View File

@@ -1,13 +1,10 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serein.Library.Api;
using Serein.Library.Api;
using Serein.Library.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Serein.Library
{

View File

@@ -14,18 +14,21 @@ namespace Serein.Library
/// 基础功能
/// </summary>
[DynamicFlow(Name ="[基础功能]")]
public static class SereinBaseFunction
public static class FlowBaseLibrary
{
[NodeAction(NodeType.Action, "对象透传")]
public static object SereinTransmissionObject(object value) => value;
public static object TransmissionObject(object value) => value;
[NodeAction(NodeType.Action, "键值对组装")]
public static Dictionary<string, object> SereinKvDataCollection(string argName,
params object[] value)
public static Dictionary<string, object> DictSet(string argNames, params object[] value)
{
var names = argName.Split(';');
var count = Math.Min(value.Length, names.Length);
var names = argNames.Split(';');
if(value.Length != names.Length)
{
throw new ArgumentException("参数名称数量与入参数量不一致");
}
var count = value.Length;
var dict = new Dictionary<string, object>();
for (int i = 0; i < count; i++)
{
@@ -35,13 +38,13 @@ namespace Serein.Library
}
[NodeAction(NodeType.Action, "数组组装")]
public static object[] SereinListDataCollection(params object[] value)
public static object[] ArraySet(params object[] value)
{
return value;
}
[NodeAction(NodeType.Action, "输出")]
public static object[] SereinConsole(params object[] value)
public static object[] Console(params object[] value)
{
foreach (var item in value)
{
@@ -51,7 +54,7 @@ namespace Serein.Library
}
[NodeAction(NodeType.Action, "逻辑分支")]
public static object SereinLogicalBranch([NodeParam(IsExplicit = false)]bool @bool,
public static object LogicalBranch([NodeParam(IsExplicit = false)]bool @bool,
object t_value,
object f_value)
{
@@ -59,7 +62,7 @@ namespace Serein.Library
}
[NodeAction(NodeType.Action, "文本拼接")]
public static string SereinTextJoin(params object[] value)
public static string TextJoin(params object[] value)
{
StringBuilder sb = new StringBuilder();
foreach (var item in value)
@@ -83,19 +86,19 @@ namespace Serein.Library
[NodeAction(NodeType.Action, "键值对动态构建对象")]
public static object SereinKvDataToObject(Dictionary<string, object> dict,
public static object CreateDynamicObjectOfDict(Dictionary<string, object> dict,
string classTypeName = "newClass_dynamic",
bool IsPrint = false)
{
if (!DynamicObjectHelper.TryResolve(dict, classTypeName, out var result))
{
Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
System.Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
}
else
{
if (IsPrint)
{
Console.WriteLine("创建完成,正在打印结果");
System.Console.WriteLine("创建完成,正在打印结果");
DynamicObjectHelper.PrintObjectProperties(result);
}
}
@@ -104,7 +107,7 @@ namespace Serein.Library
[NodeAction(NodeType.Action, "设置或更新全局数据")]
public static object SereinAddOrUpdateFlowGlobalData(string name, object data)
public static object AddOrUpdateFlowGlobalData(string name, object data)
{
SereinEnv.AddOrUpdateFlowGlobalData(name, data);
return data;

View File

@@ -631,7 +631,7 @@ namespace Serein.Library
public string ProjectFileLocation => throw new NotImplementedException();
public bool IsGlobalInterrupt => throw new NotImplementedException();
public bool _IsGlobalInterrupt => throw new NotImplementedException();
public bool IsControlRemoteEnv => throw new NotImplementedException();

View File

@@ -12,13 +12,12 @@ namespace Serein.Library
/// <summary>
/// 环境方法信息
/// </summary>
public LibraryMds[] LibraryMds { get; set; }
public FlowLibraryInfo[] LibraryMds { get; set; }
/// <summary>
/// 项目信息
/// </summary>
public SereinProjectData Project { get; set; }
// IOC节点对象信息
}
@@ -26,16 +25,27 @@ namespace Serein.Library
/// <summary>
/// 程序集相关的方法信息
/// </summary>
public class LibraryMds
public class FlowLibraryInfo
{
/// <summary>
/// 程序集名称
/// </summary>
public string AssemblyName { get; set; }
/// <summary>
/// 文件名
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 路径
/// </summary>
public string FilePath { get; set; }
/// <summary>
/// 相关的方法详情
/// </summary>
public MethodDetailsInfo[] Mds { get; set; }
public List<MethodDetailsInfo> MethodInfos { get; set; }
}
@@ -57,7 +67,7 @@ namespace Serein.Library
/// 依赖的DLL
/// </summary>
public NodeLibraryInfo[] Librarys { get; set; }
public FlowLibraryInfo[] Librarys { get; set; }
/// <summary>
/// 画布集合
@@ -89,29 +99,16 @@ namespace Serein.Library
}
/*
/// <summary>
/// 项目依赖的程序集,项目文件相关
/// </summary>
/// <summary>
public class NodeLibraryInfo
public class FlowLibraryInfo
{
/// <summary>
/// 文件名
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 路径
/// </summary>
public string FilePath { get; set; }
/// <summary>
/// 所属的程序集名称
/// </summary>
public string AssemblyName { get; set; }
}
*/
/// <summary>
/// 节点信息,项目文件相关

View File

@@ -1,4 +1,5 @@
using System;
using System.Text.RegularExpressions;
namespace Serein.Library
{
@@ -85,9 +86,9 @@ namespace Serein.Library
public sealed class NodeActionAttribute : Attribute
{
public NodeActionAttribute(NodeType methodDynamicType,
string methodTips = "",
bool scan = true,
string lockName = "")
string methodTips = "",
bool scan = true,
string lockName = "")
{
Scan = scan;
MethodDynamicType = methodDynamicType;
@@ -110,6 +111,10 @@ namespace Serein.Library
/// 暂无意义
/// </summary>
public string LockName;
/// <summary>
/// 分组名称,暂无意义
/// </summary>
public string GroupName;
}

View File

@@ -1,16 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.2.1.1</Version>
<TargetFrameworks>net8.0;net462</TargetFrameworks>
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>SereinFow</Title>
<Version>1.2.2</Version>
<Description>动态节点流、可视化编辑的基本依赖支持导入C# DLL生成自定义节点提供二次开发支持适合用于可视化编程和流程设计</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/fhhyyp/serein-flow</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<TargetFrameworks>net8.0;net462</TargetFrameworks>
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<LangVersion>latest</LangVersion>
<SatelliteResourceLanguages>no</SatelliteResourceLanguages>
@@ -22,6 +23,22 @@
<CompilerGeneratedFilesOutputPath>.\obj\g</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
<NoWarn>1701;1702;1573</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net462|AnyCPU'">
<NoWarn>1701;1702;1573</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0|AnyCPU'">
<NoWarn>1701;1702;1573</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net462|AnyCPU'">
<NoWarn>1701;1702;1573</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Http\**" />
<Compile Remove="Network\**" />
@@ -45,7 +62,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="9.0.0" />
<PackageReference Include="System.IO.Ports" Version="9.0.7" />
<PackageReference Include="System.Reactive" Version="6.0.1" />

View File

@@ -1,5 +1,4 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serein.Library.Api;
using Serein.Library.Api;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;

View File

@@ -208,7 +208,7 @@ namespace Serein.NodeFlow.Env
{
CanvasInfo = new FlowCanvasDetailsInfo
{
Name = $"Canvas {_add_canvas_count++}",
Name = string.IsNullOrWhiteSpace(canvasName) ? $"Canvas {_add_canvas_count++}" : canvasName,
Width = width,
Height = height,
Guid = Guid.NewGuid().ToString(),

View File

@@ -135,7 +135,7 @@ namespace Serein.NodeFlow.Env
public string ProjectFileLocation => currentFlowEnvironment.EnvName;
/// <inheritdoc/>
public bool IsGlobalInterrupt => currentFlowEnvironment.IsGlobalInterrupt;
public bool _IsGlobalInterrupt => currentFlowEnvironment._IsGlobalInterrupt;
/// <inheritdoc/>
public bool IsControlRemoteEnv => currentFlowEnvironment.IsControlRemoteEnv;

View File

@@ -2,6 +2,7 @@
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.Library.Utils.SereinExpression;
using Serein.NodeFlow.Model.Library;
using Serein.NodeFlow.Services;
using Serein.NodeFlow.Tool;
using System.Text;
@@ -38,13 +39,13 @@ namespace Serein.NodeFlow.Env
Event = flowEnvironmentEvent;
NodeMVVMManagement = nodeMVVMService;
FlowEdit = flowEdit;
FlowLibraryService = flowLibraryManagement;
IOC = sereinIOC;
this.flowModelService = flowModelService;
FlowControl = flowControl;
this.flowOperationService = flowOperationService;
this.IsGlobalInterrupt = false;
this.flowEnvIOC = sereinIOC;
_flowLibraryService = flowLibraryManagement;
_flowModelService = flowModelService;
_flowOperationService = flowOperationService;
_IsGlobalInterrupt = false;
_flowEnvIOC = sereinIOC;
}
@@ -146,7 +147,7 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 是否全局中断
/// </summary>
public bool IsGlobalInterrupt { get; set; }
public bool _IsGlobalInterrupt { get; set; }
/// <summary>
/// <para>单例模式IOC容器内部维护了一个实例字典默认使用类型的FullName作为Key如果以“接口-实现类”的方式注册那么将使用接口类型的FullName作为Key。</para>
@@ -185,22 +186,22 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// local环境的IOC容器主要用于注册本地环境的服务
/// </summary>
private ISereinIOC flowEnvIOC;
private ISereinIOC _flowEnvIOC;
/// <summary>
/// 通过程序集名称管理动态加载的程序集用于节点创建提供方法描述流程运行时提供Emit委托
/// </summary>
private readonly FlowLibraryService FlowLibraryService;
private readonly FlowLibraryService _flowLibraryService;
/// <summary>
/// 流程节点操作服务
/// </summary>
private readonly FlowOperationService flowOperationService;
private readonly FlowOperationService _flowOperationService;
/// <summary>
/// 流程画布、节点实体管理服务
/// </summary>
private readonly FlowModelService flowModelService;
private readonly FlowModelService _flowModelService;
/* /// <summary>
/// 环境加载的节点集合
@@ -253,7 +254,7 @@ namespace Serein.NodeFlow.Env
public async Task<FlowEnvInfo> GetEnvInfoAsync()
{
// 获取所有的程序集对应的方法信息(程序集相关的数据)
var libraryMdss = this.FlowLibraryService.GetAllLibraryMds().ToArray();
var libraryMdss = this._flowLibraryService.GetAllLibraryMds().ToArray();
// 获取当前项目的信息(节点相关的数据)
var project = await GetProjectInfoAsync(); // 远程连接获取远程环境项目信息
SereinEnv.WriteLine(InfoType.INFO, "已将当前环境信息发送到远程客户端");
@@ -328,8 +329,6 @@ namespace Serein.NodeFlow.Env
throw;
}
//
//await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点
});
}
@@ -428,9 +427,9 @@ namespace Serein.NodeFlow.Env
{
var projectData = new SereinProjectData()
{
Librarys = this.FlowLibraryService.GetAllLibraryInfo().ToArray(),
Nodes = flowModelService.GetAllNodeModel().Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
Canvass = flowModelService.GetAllCanvasModel().Select(canvas => canvas.ToInfo()).ToArray(),
Librarys = this._flowLibraryService.GetAllLibraryInfo().ToArray(),
Nodes = _flowModelService.GetAllNodeModel().Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
Canvass = _flowModelService.GetAllCanvasModel().Select(canvas => canvas.ToInfo()).ToArray(),
//StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid,
};
@@ -445,24 +444,12 @@ namespace Serein.NodeFlow.Env
/// <returns></returns>
public void LoadLibrary(string dllPath)
{
try
{
#region
var thisAssembly = typeof(IFlowEnvironment).Assembly;
var thisAssemblyName = thisAssembly.GetName().Name;
if (!string.IsNullOrEmpty(thisAssemblyName) && FlowLibraryService.GetLibraryMdsOfAssmbly(thisAssemblyName).Count == 0)
var libraryInfo = _flowLibraryService.LoadFlowLibrary(dllPath);
if (libraryInfo is not null && libraryInfo.MethodInfos.Count > 0)
{
var tmp = FlowLibraryService.LoadLibraryOfPath(thisAssembly.Location);
UIContextOperation?.Invoke(() => Event.OnDllLoad(new LoadDllEventArgs(tmp.Item1, tmp.Item2))); // 通知UI创建dll面板显示
}
#endregion
(var libraryInfo, var mdInfos) = FlowLibraryService.LoadLibraryOfPath(dllPath);
if (mdInfos.Count > 0)
{
UIContextOperation?.Invoke(() => Event.OnDllLoad(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示
UIContextOperation?.Invoke(() => Event.OnDllLoad(new LoadDllEventArgs(libraryInfo))); // 通知UI创建dll面板显示
}
}
catch (Exception ex)
@@ -471,15 +458,15 @@ namespace Serein.NodeFlow.Env
}
}
/// <summary>
/* /// <summary>
/// 加载本地程序集
/// </summary>
/// <param name="flowLibrary"></param>
public void LoadLibrary(FlowLibrary flowLibrary)
public void LoadLibrary(FlowLibraryCache flowLibrary)
{
try
{
(var libraryInfo, var mdInfos) = FlowLibraryService.LoadLibraryOfPath(flowLibrary);
libraryInfo = FlowLibraryService.LoadFlowLibrary(flowLibrary);
if (mdInfos.Count > 0)
{
UIContextOperation?.Invoke(() => Event.OnDllLoad(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示
@@ -490,7 +477,7 @@ namespace Serein.NodeFlow.Env
SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件{ex.Message}");
}
}
}*/
/// <summary>
@@ -501,10 +488,10 @@ namespace Serein.NodeFlow.Env
public bool TryUnloadLibrary(string assemblyName)
{
// 获取与此程序集相关的节点
var groupedNodes = flowModelService.GetAllNodeModel().Where(node => !string.IsNullOrWhiteSpace(node.MethodDetails.AssemblyName) && node.MethodDetails.AssemblyName.Equals(assemblyName)).ToArray();
var groupedNodes = _flowModelService.GetAllNodeModel().Where(node => !string.IsNullOrWhiteSpace(node.MethodDetails.AssemblyName) && node.MethodDetails.AssemblyName.Equals(assemblyName)).ToArray();
if (groupedNodes.Length == 0)
{
var isPass = FlowLibraryService.UnloadLibrary(assemblyName);
var isPass = _flowLibraryService.UnloadLibrary(assemblyName);
return isPass;
}
else
@@ -584,7 +571,7 @@ namespace Serein.NodeFlow.Env
{
var model = new FlowCanvasDetails(this);
model.LoadInfo(info);
flowModelService.AddCanvasModel(model);
_flowModelService.AddCanvasModel(model);
if(UIContextOperation is null)
{
@@ -608,7 +595,7 @@ namespace Serein.NodeFlow.Env
public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo? mdInfo)
{
var isPass = FlowLibraryService.TryGetMethodDetails(assemblyName, methodName, out var md);
var isPass = _flowLibraryService.TryGetMethodDetails(assemblyName, methodName, out var md);
if (!isPass || md is null)
{
mdInfo = null;
@@ -633,7 +620,7 @@ namespace Serein.NodeFlow.Env
/// <returns></returns>
public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails? delegateDetails)
{
return FlowLibraryService.TryGetDelegateDetails(assemblyName, methodName, out delegateDetails);
return _flowLibraryService.TryGetDelegateDetails(assemblyName, methodName, out delegateDetails);
}
/// <summary>
@@ -648,7 +635,7 @@ namespace Serein.NodeFlow.Env
}
this.UIContextOperation = uiContextOperation;
IOC.Register<UIContextOperation>(() => uiContextOperation).Build();
OnUIContextOperationSet();
}
@@ -713,9 +700,6 @@ namespace Serein.NodeFlow.Env
}
/// <summary>
/// 从Guid获取画布
/// </summary>
@@ -729,7 +713,7 @@ namespace Serein.NodeFlow.Env
canvasDetails = null;
return false;
}
return flowModelService.TryGetCanvasModel(nodeGuid, out canvasDetails);
return _flowModelService.TryGetCanvasModel(nodeGuid, out canvasDetails);
}
@@ -746,7 +730,7 @@ namespace Serein.NodeFlow.Env
nodeModel = null;
return false;
}
return flowModelService.TryGetNodeModel(nodeGuid, out nodeModel);
return _flowModelService.TryGetNodeModel(nodeGuid, out nodeModel);
}
@@ -777,6 +761,20 @@ namespace Serein.NodeFlow.Env
#endregion
/// <summary>
/// 设置了 UIContextOperation 需要立刻执行的方法,用于加载基础库,创建第一个画布。
/// </summary>
private void OnUIContextOperationSet()
{
var baseLibrary = _flowLibraryService.LoadBaseLibrary();
if (baseLibrary is not null && baseLibrary.MethodInfos.Count > 0)
{
UIContextOperation?.Invoke(() => Event.OnDllLoad(new LoadDllEventArgs(baseLibrary))); // 通知UI创建dll面板显示
}
// 创建第一个画布
FlowEdit.CreateCanvas("Default", 1920, 1080);
}
}

View File

@@ -97,9 +97,9 @@ namespace Serein.NodeFlow
/// </summary>
/// <param name="libraryInfo"></param>
/// <returns></returns>
public static NodeLibraryInfo ToLibrary(this NodeLibraryInfo libraryInfo)
public static FlowLibraryInfo ToLibrary(this FlowLibraryInfo libraryInfo)
{
return new NodeLibraryInfo
return new FlowLibraryInfo
{
AssemblyName = libraryInfo.AssemblyName,
FileName = libraryInfo.FileName,

View File

@@ -12,52 +12,52 @@ using System.Reflection;
using System.Text;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Serein.NodeFlow
namespace Serein.NodeFlow.Model.Library
{
public class LibraryMdDd
{
public MethodDetails MethodDetails { get; }
public MethodInfo MethodInfo { get; }
public DelegateDetails DelegateDetails { get; }
public LibraryMdDd(MethodInfo methodInfo, MethodDetails methodDetails, DelegateDetails delegateDetails)
{
MethodDetails = methodDetails;
MethodInfo = methodInfo;
DelegateDetails = delegateDetails;
}
}
/// <summary>
/// 加载在流程中的程序集依赖
/// </summary>
public class FlowLibrary
public class FlowLibraryCache
{
public Assembly Assembly { get; private set; }
//private readonly Action actionOfUnloadAssmbly;
/*, Action actionOfUnloadAssmbly*/
//this.actionOfUnloadAssmbly = actionOfUnloadAssmbly;
public FlowLibrary(Assembly assembly)
/// <summary>
/// 通过程序集创建一个流程库实例
/// </summary>
/// <param name="assembly"></param>
public FlowLibraryCache(Assembly assembly)
{
this.Assembly = assembly;
this.FullName = Path.GetFileName(Assembly.Location);
this.FilePath = Assembly.Location;
Assembly = assembly;
FullName = Path.GetFileName(Assembly.Location);
FilePath = Assembly.Location;
}
public FlowLibrary(Assembly assembly,
/// <summary>
/// 通过动态程序集和文件路径创建一个流程库实例
/// </summary>
/// <param name="dynamicAssembly"></param>
/// <param name="filePath"></param>
public FlowLibraryCache(Assembly dynamicAssembly,
string filePath)
{
this.Assembly = assembly;
this.FullName = Path.GetFileName(filePath); ;
this.FilePath = filePath;
Assembly = dynamicAssembly;
FullName = Path.GetFileName(filePath); ;
FilePath = filePath;
}
/// <summary>
/// 程序集本身
/// </summary>
public Assembly Assembly { get; private set; }
/// <summary>
/// 程序集全名
/// </summary>
public string FullName { get; private set; }
/// <summary>
/// 程序集文件路径
/// </summary>
public string FilePath { get; private set; }
/// <summary>
@@ -65,60 +65,33 @@ namespace Serein.NodeFlow
/// Key 方法名称
/// Value :方法详情
/// </summary>
public ConcurrentDictionary<string, MethodDetails> MethodDetailss { get; } = new ConcurrentDictionary<string, MethodDetails>();
public ConcurrentDictionary<string, MethodInfo> MethodInfos { get; } = new ConcurrentDictionary<string, MethodInfo>();
public Dictionary<string, MethodDetails> MethodDetailss { get; } = new Dictionary<string, MethodDetails>();
/// <summary>
/// 管理通过Emit动态构建的委托
/// Key :方法名称
/// Value :方法详情
/// 加载程序集时创建的方法信息
/// </summary>
public ConcurrentDictionary<string, DelegateDetails> DelegateDetailss { get; } = new ConcurrentDictionary<string, DelegateDetails>();
public Dictionary<string, MethodInfo> MethodInfos { get; } = new Dictionary<string, MethodInfo>();
/// <summary>
/// 记录不同的注册时机需要自动创建全局唯一实例的类型信息
/// <para>缓存节点方法通Emit委托</para>
/// <para>Key :方法名称</para>
/// <para>Value :方法详情</para>
/// </summary>
public ConcurrentDictionary<RegisterSequence, List<Type>> RegisterTypes { get; } = new ConcurrentDictionary<RegisterSequence, List<Type>>();
public Dictionary<string, DelegateDetails> DelegateDetailss { get; } = new Dictionary<string, DelegateDetails>();
/// <summary>
/// 卸载当前程序集以及附带的所有信息
/// 用于流程启动时,在不同阶段(Init_Loading_Loaded)需要创建实例的类型信息
/// </summary>
public void Upload()
{
DelegateDetailss.Clear();
RegisterTypes.Clear();
MethodDetailss.Clear();
//actionOfUnloadAssmbly?.Invoke();
}
/// <summary>
/// 转为依赖信息
/// </summary>
/// <returns></returns>
public NodeLibraryInfo ToInfo()
{
var assemblyName = Assembly.GetName().Name;
return new NodeLibraryInfo
{
AssemblyName = assemblyName,
FileName = this.FullName,
FilePath = this.FilePath,
};
}
public Dictionary<RegisterSequence, List<Type>> RegisterTypes { get; } = new Dictionary<RegisterSequence, List<Type>>();
/// <summary>
/// 动态加载程序集
/// </summary>
/// <param name="assembly">程序集本身</param>
/// <returns></returns>
public bool LoadAssembly()
public bool LoadFlowMethod()
{
Assembly assembly = this.Assembly;
Assembly assembly = Assembly;
#region
@@ -132,7 +105,7 @@ namespace Serein.NodeFlow
try
{
types = assembly.GetTypes().ToList(); // 获取程序集中的所有类型
if (types.Count < 0) // 防止动态程序集中没有类型信息?
if (types.Count <= 0)
{
return false;
}
@@ -143,7 +116,7 @@ namespace Serein.NodeFlow
var loaderExceptions = ex.LoaderExceptions;
foreach (var loaderException in loaderExceptions)
{
SereinEnv.WriteLine(InfoType.ERROR, loaderException?.Message);
SereinEnv.WriteLine(InfoType.ERROR, "加载失败 : " + loaderException?.Message);
}
return false;
}
@@ -160,8 +133,7 @@ namespace Serein.NodeFlow
// Type 具有 DynamicFlowAttribute 标记的类型
// string 类型元数据 DynamicFlowAttribute 特性中的 Name 属性
types = types.Where(type => type.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute dynamicFlowAttribute
&& dynamicFlowAttribute.Scan).ToList();
types = types.Where(type => type.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute df && df.Scan).ToList();
foreach (var type in types)
{
@@ -208,20 +180,16 @@ namespace Serein.NodeFlow
{
return false;
}
// 简单排序一下
//detailss = detailss.OrderBy(k => k.MethodDetails.MethodName,).ToList();
// 简单排序一下
detailss.Sort((a, b) => string.Compare(a.MethodDetails.MethodName, b.MethodDetails.MethodName, StringComparison.OrdinalIgnoreCase));
foreach (var item in detailss)
{
SereinEnv.WriteLine(InfoType.INFO, "loading method : " + item.MethodDetails.MethodName);
SereinEnv.WriteLine(InfoType.INFO, "加载方法 : " + item.MethodDetails.MethodName);
}
//detailss.Sort((a, b) => string.Compare());
#region
foreach (var item in detailss)
{
@@ -253,6 +221,36 @@ namespace Serein.NodeFlow
}
/// <summary>
/// 卸载当前程序集以及附带的所有信息
/// </summary>
public void Unload()
{
DelegateDetailss.Clear();
MethodInfos.Clear();
RegisterTypes.Clear();
MethodDetailss.Clear();
}
/// <summary>
/// 转为依赖信息
/// </summary>
/// <returns></returns>
public FlowLibraryInfo ToInfo()
{
var assemblyName = Assembly.GetName().Name;
var mdInfos = MethodDetailss.Values.Select(x => x.ToInfo()).ToList();
mdInfos.Sort((a, b) => string.Compare(a.MethodName, b.MethodName, StringComparison.OrdinalIgnoreCase));
return new FlowLibraryInfo
{
AssemblyName = assemblyName,
FileName = FullName,
FilePath = FilePath,
MethodInfos = mdInfos.ToList(),
};
}
}
}

View File

@@ -0,0 +1,20 @@
using Serein.Library;
using System.Reflection;
namespace Serein.NodeFlow.Model.Library
{
public class LibraryMdDd
{
public MethodDetails MethodDetails { get; }
public MethodInfo MethodInfo { get; }
public DelegateDetails DelegateDetails { get; }
public LibraryMdDd(MethodInfo methodInfo, MethodDetails methodDetails, DelegateDetails delegateDetails)
{
MethodDetails = methodDetails;
MethodInfo = methodInfo;
DelegateDetails = delegateDetails;
}
}
}

View File

@@ -231,7 +231,7 @@ namespace Serein.NodeFlow.Model
var returnType = sereinScript.ParserScript(Script, argTypes); // 开始解析获取程序主节点
MethodDetails.ReturnType = returnType;
var scriptMethodInfo = sereinScript.ConvertCSharpCode(methodName, argTypes);
var scriptMethodInfo = sereinScript.ConvertCSharpCode(methodName, argTypes);
return scriptMethodInfo;
}
catch (Exception ex)

View File

@@ -42,18 +42,18 @@ namespace Serein.NodeFlow.Model.Operation
{
if (!flowModelService.ContainsCanvasModel(CanvasGuid))
{
flowEnvironment.WriteLine(Library.InfoType.INFO, $"节点取出失败,目标画布不存在[{NodeGuid}]");
flowEnvironment.WriteLine(Serein.Library.InfoType.WARN, $"节点取出失败,目标画布不存在[{NodeGuid}]");
return false;
}
// 获取目标节点与容器节点
if (!flowModelService.TryGetNodeModel(NodeGuid, out var nodeModel))
{
flowEnvironment.WriteLine(Library.InfoType.INFO, $"节点取出失败,目标节点不存在[{NodeGuid}]");
flowEnvironment.WriteLine(Serein.Library.InfoType.WARN, $"节点取出失败,目标节点不存在[{NodeGuid}]");
return false;
}
if (nodeModel.ContainerNode is not INodeContainer containerNode)
{
flowEnvironment.WriteLine(Library.InfoType.INFO, $"节点取出失败,节点并非容器节点[{nodeModel.Guid}]");
flowEnvironment.WriteLine(Serein.Library.InfoType.WARN, $"节点取出失败,节点并非容器节点[{nodeModel.Guid}]");
return false;
}
Node = nodeModel;

View File

@@ -97,7 +97,7 @@ namespace Serein.NodeFlow.Model.Operation
// 节点与画布互相绑定
nodeModel.CanvasDetails = flowCanvasDetails;
flowCanvasDetails.Nodes.Add(nodeModel);
flowCanvasDetails.Nodes = [..flowCanvasDetails.Nodes, nodeModel];
flowModelService.AddNodeModel(nodeModel);
this.flowNode = nodeModel;

View File

@@ -1,15 +1,5 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serein.Library;
using Serein.Library;
using Serein.Library.Api;
using Serein.Script.Node;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Serein.NodeFlow.Model.Operation
{

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.2.1</Version>
<Version>1.2.2</Version>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@@ -52,6 +52,7 @@
<Compile Remove="Tool\Attribute.cs" />
<Compile Remove="Tool\DynamicTool.cs" />
<Compile Remove="Tool\ExpressionHelper.cs" />
<Compile Remove="Tool\FlowLibraryAssemblyContext2.cs" />
<Compile Remove="Tool\NodeModelBaseFunc.cs" />
<Compile Remove="Tool\TcsSignal.cs" />
<Compile Remove="Tool\ToCSharpCodeHelper.cs" />
@@ -69,6 +70,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.13.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" />
</ItemGroup>

View File

@@ -2,13 +2,14 @@
using Serein.Library.Api;
using Serein.Library.FlowNode;
using Serein.Library.Utils;
using Serein.NodeFlow.Model.Library;
using Serein.NodeFlow.Tool;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
@@ -28,54 +29,95 @@ namespace Serein.NodeFlow.Services
private readonly IFlowEnvironment flowEnvironment;
/// <summary>
/// 缓存所有加载了的程序集
/// 缓存流程依赖
/// </summary>
private readonly ConcurrentDictionary<string, FlowLibrary> _myFlowLibrarys = new ConcurrentDictionary<string, FlowLibrary>();
private readonly ConcurrentDictionary<string, FlowLibraryCache> _flowLibraryCaches = new ConcurrentDictionary<string, FlowLibraryCache>();
private readonly ConcurrentDictionary<string, FlowLibraryAssemblyContext> _flowLibraryAssemblyContexts
= new ConcurrentDictionary<string, FlowLibraryAssemblyContext>();
/// <summary>
/// 加载类库
/// 每个类库下面至少需要有“Serein.Library.dll”类库依赖
/// </summary>
/// <param name="libraryfilePath"></param>
/// <returns></returns>
public (NodeLibraryInfo, List<MethodDetailsInfo>) LoadLibraryOfPath(string libraryfilePath)
private bool CheckBaseLibrary(string libraryfilePath, out string baseLibraryPath)
{
var dir = Path.GetDirectoryName(libraryfilePath); // 获取目录路径
var sereinFlowBaseLibraryPath = Path.Combine(dir, SereinBaseLibrary);// 每个类库下面至少需要有“Serein.Library.dll”类库依赖
var sereinFlowBaseLibraryPath = Path.Combine(dir, SereinBaseLibrary);
if (!Path.Exists(sereinFlowBaseLibraryPath))
{
throw new Exception($"从文件加载DLL失败目标文件夹不存在{SereinBaseLibrary}文件" );
}
var flowAlc = new FlowLibraryAssemblyContext(sereinFlowBaseLibraryPath, Path.GetFileName(libraryfilePath));
var assembly = flowAlc.LoadFromAssemblyPath(libraryfilePath); // 加载指定路径的程序集
var flowLibrary = new FlowLibrary(assembly);
try
{
var reulst = LoadFlowLibrary(flowLibrary);
return reulst;
}
catch (Exception)
{
flowAlc?.Unload(); // 卸载程序集
flowAlc = null;
GC.Collect(); // 强制触发GC确保卸载成功
GC.WaitForPendingFinalizers();
throw;
baseLibraryPath = string.Empty;
return false;
}
baseLibraryPath = sereinFlowBaseLibraryPath;
return true;
}
/// <summary>
/// 加载类库
/// 加载基础依赖
/// </summary>
/// <param name="flowLibrary"></param>
/// <returns></returns>
public (NodeLibraryInfo, List<MethodDetailsInfo>) LoadLibraryOfPath(FlowLibrary flowLibrary)
public FlowLibraryInfo LoadBaseLibrary()
{
return LoadFlowLibrary(flowLibrary);
Assembly baseAssmbly = typeof(FlowBaseLibrary).Assembly;
var flowLibrary = new FlowLibraryCache(baseAssmbly);
flowLibrary.LoadFlowMethod();
var assemblyName = baseAssmbly.GetName().Name;
if (string.IsNullOrEmpty(assemblyName))
{
throw new Exception($"程序集\"{baseAssmbly}\"返回 Name 为 null");
}
_flowLibraryCaches.TryAdd(assemblyName, flowLibrary);
return flowLibrary.ToInfo();
}
/// <summary>
/// 加载流程依赖
/// </summary>
/// <param name="libraryfilePath"></param>
/// <exception cref="Exception"></exception>
public FlowLibraryInfo? LoadFlowLibrary(string libraryfilePath)
{
if (!CheckBaseLibrary(libraryfilePath, out var baseLibraryPath))
{
throw new Exception($"从文件加载DLL失败目标文件夹不存在{SereinBaseLibrary}文件");
}
FlowLibraryAssemblyContext flowAlc = new FlowLibraryAssemblyContext(baseLibraryPath, Path.GetFileName(libraryfilePath));
var flowAssembly = flowAlc.LoadFromAssemblyPath(libraryfilePath);
if(flowAssembly is null)
{
throw new Exception($"从文件加载DLL失败FlowLibraryAssemblyContext 加载的程序集为 null \"{libraryfilePath}\"");
}
var flowLibrary = new FlowLibraryCache(flowAssembly);
var isSuccess = flowLibrary.LoadFlowMethod();
if (!isSuccess)
{
flowAlc?.Unload(); // 卸载程序集
GC.Collect(); // 强制触发GC确保卸载成功
GC.WaitForPendingFinalizers();
return null;
}
else
{
var assemblyName = flowAssembly.GetName().Name;
if (string.IsNullOrEmpty(assemblyName))
{
flowLibrary.Unload();
flowAlc?.Unload(); // 卸载程序集
GC.Collect(); // 强制触发GC确保卸载成功
GC.WaitForPendingFinalizers();
return null;
throw new Exception($"程序集\"{flowAssembly}\"返回 Name 为 null");
}
_flowLibraryCaches.TryAdd(assemblyName, flowLibrary);
return flowLibrary.ToInfo();
}
}
/// <summary>
/// 卸载类库
/// </summary>
@@ -83,11 +125,11 @@ namespace Serein.NodeFlow.Services
/// <returns></returns>
public bool UnloadLibrary(string assemblyName)
{
if (_myFlowLibrarys.Remove(assemblyName, out var flowLibrary))
if (_flowLibraryCaches.Remove(assemblyName, out var flowLibrary))
{
try
{
flowLibrary.Upload(); // 尝试卸载
flowLibrary.Unload(); // 尝试卸载
flowLibrary = null;
return true;
}
@@ -104,6 +146,8 @@ namespace Serein.NodeFlow.Services
}
}
#region
/// <summary>
/// 获取方法描述
/// </summary>
@@ -118,7 +162,7 @@ namespace Serein.NodeFlow.Services
methodInfo = null;
return false;
}
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
if (_flowLibraryCaches.TryGetValue(assemblyName, out var flowLibrary)
&& flowLibrary.MethodInfos.TryGetValue(methodName, out methodInfo))
{
return true;
@@ -130,7 +174,7 @@ namespace Serein.NodeFlow.Services
}
}
/// <summary>
/// <summary>
/// 获取方法描述
/// </summary>
/// <param name="assemblyName">程序集名称</param>
@@ -139,7 +183,7 @@ namespace Serein.NodeFlow.Services
/// <returns>是否获取成功</returns>
public bool TryGetMethodDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodDetails md)
{
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
if (_flowLibraryCaches.TryGetValue(assemblyName, out var flowLibrary)
&& flowLibrary.MethodDetailss.TryGetValue(methodName, out md))
{
return true;
@@ -160,7 +204,7 @@ namespace Serein.NodeFlow.Services
/// <returns>是否获取成功</returns>
public bool TryGetDelegateDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out DelegateDetails dd)
{
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
if (_flowLibraryCaches.TryGetValue(assemblyName, out var flowLibrary)
&& flowLibrary.DelegateDetailss.TryGetValue(methodName, out dd))
{
return true;
@@ -182,7 +226,7 @@ namespace Serein.NodeFlow.Services
{
List<MethodDetails> mds = [];
foreach (var library in _myFlowLibrarys.Values)
foreach (var library in _flowLibraryCaches.Values)
{
var t_mds = library.MethodDetailss.Values.Where(it => it.MethodDynamicType == nodeType).ToList();
mds.AddRange(t_mds);
@@ -197,7 +241,7 @@ namespace Serein.NodeFlow.Services
public Dictionary<RegisterSequence, List<Type>> GetaAutoRegisterType()
{
Dictionary<RegisterSequence, List<Type>> rsTypes = new Dictionary<RegisterSequence, List<Type>>();
foreach (var library in _myFlowLibrarys.Values)
foreach (var library in _flowLibraryCaches.Values)
{
foreach (var kv in library.RegisterTypes)
{
@@ -220,28 +264,26 @@ namespace Serein.NodeFlow.Services
/// <returns></returns>
public List<MethodDetails> GetLibraryMdsOfAssmbly(string assemblyName)
{
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary))
if (_flowLibraryCaches.TryGetValue(assemblyName, out var flowLibrary))
{
return flowLibrary.MethodDetailss.Values.ToList();
}
return [];
}
/// <summary>
/// 获取所有方法信息,用于保存项目时调用
/// 获取流程方法信息,用于保存项目时调用
/// </summary>
/// <returns></returns>
public List<LibraryMds> GetAllLibraryMds()
public List<FlowLibraryInfo> GetAllLibraryMds()
{
List<LibraryMds> mds = new List<LibraryMds>();
foreach (FlowLibrary library in _myFlowLibrarys.Values)
List<FlowLibraryInfo> mds = new List<FlowLibraryInfo>();
foreach (FlowLibraryCache library in _flowLibraryCaches.Values)
{
var tmp = new LibraryMds
var tmp = new FlowLibraryInfo
{
AssemblyName = library.FullName,
Mds = library.MethodDetailss.Values.Select(md => md.ToInfo()).ToArray()
MethodInfos = library.MethodDetailss.Values.Select(md => md.ToInfo()).ToList()
};
mds.Add(tmp);
}
@@ -253,11 +295,11 @@ namespace Serein.NodeFlow.Services
/// 序列化当前项目的依赖信息、节点信息,用于远程登录的场景,需要将依赖信息从本地(受控端)发送到远程(主控端)
/// </summary>
/// <returns></returns>
public List<NodeLibraryInfo> GetAllLibraryInfo()
public List<FlowLibraryInfo> GetAllLibraryInfo()
{
return _myFlowLibrarys.Values.Select(library => library.ToInfo()).ToList();
return _flowLibraryCaches.Values.Select(library => library.ToInfo()).ToList();
}
#endregion
#region
@@ -266,132 +308,7 @@ namespace Serein.NodeFlow.Services
/// </summary>
public readonly static string SereinBaseLibrary = $"{nameof(Serein)}.{nameof(Library)}.dll";
//private (NodeLibraryInfo, List<MethodDetailsInfo>) LoadDllNodeInfo(Assembly assembly)
//{
// if (assembly.FullName?.ToString().Equals(typeof(IFlowEnvironment).Assembly.FullName?.ToString()) == true)
// {
// // 加载基础依赖
// return LoadAssembly(typeof(IFlowEnvironment).Assembly);
// }
// else
// {
// try
// {
// var assembly_result = LoadAssembly(assembly);
// return assembly_result;
// }
// catch (Exception)
// {
// return (null,[]);
// }
// }
//}
private (NodeLibraryInfo, List<MethodDetailsInfo>) LoadFlowLibrary(FlowLibrary flowLibrary)
{
var assembly = flowLibrary.Assembly;
if (assembly.FullName?.ToString().Equals(typeof(IFlowEnvironment).Assembly.FullName?.ToString()) == true)
{
// 加载基础依赖
flowLibrary = new FlowLibrary(typeof(IFlowEnvironment).Assembly);
}
var assmblyName = assembly.GetName().Name;
if (!string.IsNullOrEmpty(assmblyName) && _myFlowLibrarys.ContainsKey(assmblyName))
{
throw new Exception($"程序集[{assembly.GetName().FullName}]已经加载过!");
}
var loadResult = flowLibrary.LoadAssembly(); // 加载程序集
if (loadResult)
{
var assemblyName = assembly.GetName().Name;
if (string.IsNullOrEmpty(assemblyName))
{
throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败,没有程序集名称");
}
_myFlowLibrarys.TryAdd(assemblyName, flowLibrary);
List<MethodDetailsInfo> mdInfos = flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList();
mdInfos.Sort((a, b) => string.Compare(a.MethodName, b.MethodName, StringComparison.OrdinalIgnoreCase));
(NodeLibraryInfo, List<MethodDetailsInfo>) result = (flowLibrary.ToInfo(), mdInfos);
return result;
}
else
{
throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败");
}
}
#endregion
}
/// <summary>
/// 流程依赖加载
/// </summary>
public class FlowLibraryAssemblyContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
/// <summary>
/// 创建新的加载上下文
/// </summary>
/// <param name="sereinFlowLibraryPath">类库路径</param>
/// <param name="name"></param>
public FlowLibraryAssemblyContext(string sereinFlowLibraryPath, string name) : base(name, isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(sereinFlowLibraryPath);
}
protected override Assembly? Load(AssemblyName assemblyName)
{
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (!string.IsNullOrEmpty(assemblyPath))
{
var assembly = Default.LoadFromAssemblyPath(assemblyPath);
//var assembly = LoadFromAssemblyPath(assemblyPath);
return assembly;
}
else
{
return Default.Assemblies.FirstOrDefault(x => x.FullName == assemblyName.FullName);
}
// return null;
// 构建依赖项的路径
//string assemblyPath = Path.Combine(AppContext.BaseDirectory, assemblyName.Name + ".dll");
//if (File.Exists(assemblyPath))
//{
// return LoadFromAssemblyPath(assemblyPath);
//}
//assemblyPath = Path.Combine(filePath, assemblyName.Name + ".dll");
//if (File.Exists(assemblyPath))
//{
// return LoadFromAssemblyPath(assemblyPath);
//}
//return null; // 如果没有找到,返回 null
}
}
//public static class PluginAssemblyContextExtensions
//{
// public static Assembly FromAssemblyPath(this AssemblyLoadContext context, string path)
// {
// return context.LoadFromAssemblyPath(path);
// }
//}
}

View File

@@ -1,13 +1,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Tool
{

View File

@@ -0,0 +1,59 @@
using System.Reflection;
using System.Runtime.Loader;
namespace Serein.NodeFlow.Tool
{
/// <summary>
/// 流程依赖加载
/// </summary>
public class FlowLibraryAssemblyContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
/// <summary>
/// 创建新的加载上下文
/// </summary>
/// <param name="baseLibraryPath">流程基础依赖类库路径</param>
/// <param name="name"></param>
public FlowLibraryAssemblyContext(string baseLibraryPath, string name) : base(name, isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(baseLibraryPath);
}
/// <summary>
/// 加载指定的程序集
/// </summary>
/// <param name="assemblyName"></param>
/// <returns></returns>
protected override Assembly? Load(AssemblyName assemblyName)
{
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); // 加载程序集
if (!string.IsNullOrEmpty(assemblyPath))
{
var assembly = Default.LoadFromAssemblyPath(assemblyPath); // 通过默认方式进行加载程序集及相关依赖
//var assembly = LoadFromAssemblyPath(assemblyPath);
return assembly;
}
else
{
return Default.Assemblies.FirstOrDefault(x => x.FullName == assemblyName.FullName);
}
// return null;
// 构建依赖项的路径
//string assemblyPath = Path.Combine(AppContext.BaseDirectory, assemblyName.Name + ".dll");
//if (File.Exists(assemblyPath))
//{
// return LoadFromAssemblyPath(assemblyPath);
//}
//assemblyPath = Path.Combine(filePath, assemblyName.Name + ".dll");
//if (File.Exists(assemblyPath))
//{
// return LoadFromAssemblyPath(assemblyPath);
//}
//return null; // 如果没有找到,返回 null
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Library
{
/// <summary>
/// 流程依赖加载
/// </summary>
public class FlowLibraryAssemblyContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
/// <summary>
/// 创建新的加载上下文
/// </summary>
/// <param name="sereinFlowLibraryPath">类库路径</param>
/// <param name="name"></param>
public FlowLibraryAssemblyContext(string sereinFlowLibraryPath, string name) : base(name, isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(sereinFlowLibraryPath);
}
protected override Assembly? Load(AssemblyName assemblyName)
{
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (!string.IsNullOrEmpty(assemblyPath))
{
var assembly = Default.LoadFromAssemblyPath(assemblyPath);
//var assembly = LoadFromAssemblyPath(assemblyPath);
return assembly;
}
else
{
return Default.Assemblies.FirstOrDefault(x => x.FullName == assemblyName.FullName);
}
}
}
}

View File

@@ -1,10 +1,4 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serein.Library.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Serein.Library.Utils;
namespace Serein.Extend.NewtonsoftJson
{

View File

@@ -5,6 +5,15 @@
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<Title>为 SereinFlow 提供的 JSON 扩展</Title>
<Version>1.0.0</Version>
<Description>通过 NewtonsoftJson 实现JSON门户扩展用于解决 Serein.Proto.* 项目下需要 JSON 序列化与反序列化的场景</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>

View File

@@ -25,7 +25,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" PrivateAssets="all" />
</ItemGroup>
<!--<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">

View File

@@ -10,10 +10,10 @@ namespace Serein.Proto.Modbus
/// 创建 Modbus 客户端实例
/// </summary>
/// <param name="connectionString">
/// 连接字符串格式:
/// TCP示例"tcp:192.168.1.100:502"
/// UCP示例"ucp:192.168.1.100:502"
/// RTU示例"rtu:COM3:9600:1" 格式rtu:串口名:波特率:从站地址)
/// <para>连接字符串格式: </para>
/// <para>TCP示例"tcp:192.168.1.100:502" </para>
/// <para>UCP示例"ucp:192.168.1.100:502" </para>
/// <para>RTU示例"rtu:COM3:9600:1" 格式rtu:串口名:波特率:从站地址) </para>
/// </param>
public static IModbusClient Create(string connectionString)
{

View File

@@ -5,6 +5,15 @@
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<Title>全异步Modbus协议客户端工具包</Title>
<Version>1.0.0</Version>
<Description>提供TCP、UDP、RTU三种方式客户端创建方式IModbusClient client = ModbusClientFactory.Create...;</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>

View File

@@ -5,6 +5,15 @@
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<Title>基于Json数据载体的WebSocket交互工具包</Title>
<Version>1.0.0</Version>
<Description>基于Json数据载体的WebSocket交互工具包</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>

View File

@@ -1,13 +1,4 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serein.Library.Api;
using Serein.Script.Node;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script
namespace Serein.Script
{
internal static class NodeInterpreterExtension
{

View File

@@ -5,6 +5,15 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<Title>基于AST实现的脚本语言</Title>
<Version>1.0.0</Version>
<Description>使用了Emit构造委托缓存调用性能客观。提供了类型推导、转换C#代码功能。用于流程图中脚本处理,也可在其他地方进行使用。</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/fhhyyp/serein-flow</RepositoryUrl>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
@@ -18,7 +27,16 @@
<None Remove="TestExpression\**" />
<None Remove="Tool\**" />
</ItemGroup>
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<Compile Remove="Node\ExpressionNode.cs" />
</ItemGroup>

View File

@@ -1,14 +1,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serein.Library;
using Serein.Library.Api;
using Serein.Library;
using Serein.Script.Node;
using Serein.Script.Node.FlowControl;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script
{

View File

@@ -269,9 +269,8 @@ namespace Serein.Script
}
ASTNode tempNode = peekToken3.Type switch
{
TokenType.Dot => ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.Semicolon => ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.ParenthesisRight => ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.Dot or TokenType.Semicolon or TokenType.ParenthesisRight =>
ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.SquareBracketsLeft => ParseCollectionIndexNode(source), // 获取集合中的元素 source[index]....
TokenType.ParenthesisLeft => ParseMemberFunctionCallNode(source), // 获取需要调用的方法 source(arg1,arg2...)...
_ => throw new Exception($"无法从对象获取成员当前Token类型为 {peekToken.Type}。")
@@ -963,11 +962,9 @@ namespace Serein.Script
var peekToken3 = _lexer.PeekToken();
ASTNode tempNode = peekToken3.Type switch
{
TokenType.Comma => ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.Operator => ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.Dot => ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.Semicolon => ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.ParenthesisRight => ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.Comma or TokenType.Operator or TokenType.Dot or TokenType.Semicolon or TokenType.ParenthesisRight
=> ParseMemberAccessNode(source), // 获取对象中的成员 source.Value...
TokenType.SquareBracketsLeft => ParseCollectionIndexNode(source), // 获取集合中的元素 source[index]....
TokenType.ParenthesisLeft => ParseMemberFunctionCallNode(source), // 获取需要调用的方法 source(arg1,arg2...)...
_ => throw new Exception($"无法从对象获取成员当前Token : {peekToken.ToString()}。")

View File

@@ -1,14 +1,10 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serein.Library;
using Serein.Library.Api;
using Serein.Library;
using Serein.Workbench.Api;
using Serein.Workbench.Node.ViewModel;
using Serein.Workbench.Themes;
using Serein.Workbench.Views;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
namespace Serein.Workbench.Node.View

View File

@@ -1,14 +1,6 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serein.Library;
using Serein.NodeFlow;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Library;
using Serein.Workbench.Themes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Documents;
using System.Windows.Input;
namespace Serein.Workbench.Node.ViewModel
@@ -71,9 +63,9 @@ public class FlowLibrary
});
}
private static void OnCompileComplete(FlowLibrary flowLibrary)
private static void OnCompileComplete(FlowLibraryCache flowLibrary)
{
var loadResult = flowLibrary.LoadAssembly(); // 动态编译完成后加载程序集
var loadResult = flowLibrary.LoadFlowMethod(); // 动态编译完成后加载程序集
if (!loadResult)
{
return ;

View File

@@ -181,7 +181,7 @@ namespace Serein.Workbench.Services
SereinEnv.WriteLine(InfoType.INFO, "项目文件保存路径:" + savePath);
for (int index = 0; index < project.Librarys.Length; index++)
{
NodeLibraryInfo? library = project.Librarys[index];
FlowLibraryInfo? library = project.Librarys[index];
string sourceFilePath = new Uri(library.FilePath).LocalPath; // 源文件夹
string targetDir = System.IO.Path.Combine(librarySavePath, library.AssemblyName); // 目标文件夹
if (!Path.Exists(targetDir))

View File

@@ -1,5 +1,5 @@
using Microsoft.Win32;
using Serein.NodeFlow;
using Serein.NodeFlow.Model.Library;
using Serein.NodeFlow.Tool;
using System;
using System.Collections.Generic;
@@ -34,7 +34,7 @@ namespace Serein.Workbench.Themes
/// <summary>
/// 编译成功回调
/// </summary>
public Action<FlowLibrary> OnCompileComplete { get; set; }
public Action<FlowLibraryCache> OnCompileComplete { get; set; }
public DynamicCompilerView()
{
InitializeComponent();
@@ -132,7 +132,7 @@ namespace Serein.Workbench.Themes
var path = textboxAssemblyName.Text;
Assembly assembly = _compiler.Compile(code, path);
FlowLibrary flowLibrary = new FlowLibrary(assembly, path);
FlowLibraryCache flowLibrary = new FlowLibraryCache(assembly, path);
if (assembly != null)
{

View File

@@ -19,13 +19,13 @@ namespace Serein.Workbench.ViewModels
private readonly IFlowEEForwardingService flowEEForwardingService;
private readonly IFlowEnvironment flowEnvironment;
[ObservableProperty]
private ObservableCollection<FlowLibraryInfo> flowLibraryInfos;
private ObservableCollection<Models.FlowLibraryInfo> flowLibraryInfos;
public FlowLibrarysViewModel(IFlowEEForwardingService flowEEForwardingService,IFlowEnvironment flowEnvironment)
{
this.flowEEForwardingService = flowEEForwardingService;
this.flowEnvironment = flowEnvironment;
FlowLibraryInfos = new ObservableCollection<FlowLibraryInfo>();
FlowLibraryInfos = new ObservableCollection<Models.FlowLibraryInfo>();
flowEEForwardingService.DllLoad += FlowEEForwardingService_OnDllLoad;
}
/// <summary>
@@ -48,15 +48,15 @@ namespace Serein.Workbench.ViewModels
private void FlowEEForwardingService_OnDllLoad(Library.Api.LoadDllEventArgs eventArgs)
{
if (!eventArgs.IsSucceed) return;
List<MethodDetailsInfo> mds = eventArgs.MethodDetailss;
NodeLibraryInfo libraryInfo = eventArgs.NodeLibraryInfo;
List<MethodDetailsInfo> mds = eventArgs.NodeLibraryInfo.MethodInfos.ToList() ;
Library.FlowLibraryInfo libraryInfo = eventArgs.NodeLibraryInfo;
var methodInfo = new ObservableCollection<MethodDetailsInfo>();
foreach (var md in mds)
{
methodInfo.Add(md);
}
var flInfo = new FlowLibraryInfo
var flInfo = new Models.FlowLibraryInfo
{
LibraryName = libraryInfo.AssemblyName,
FilePath = libraryInfo.FilePath,