mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-04 15:26:34 +08:00
暂时实现了简陋的脚本AST分析解释,后面再绑定到控件上
This commit is contained in:
58
Library/Api/IScriptFlowApi.cs
Normal file
58
Library/Api/IScriptFlowApi.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// 脚本代码中关于流程运行的API
|
||||
/// </summary>
|
||||
public interface IScriptFlowApi
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前流程
|
||||
/// </summary>
|
||||
IFlowEnvironment Env { get; }
|
||||
/// <summary>
|
||||
/// 对应的节点
|
||||
/// </summary>
|
||||
NodeModelBase NodeModel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 动态流程上下文
|
||||
/// </summary>
|
||||
IDynamicContext Context { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 根据索引从入参数据获取数据
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
object GetDataOfParams(int index);
|
||||
/// <summary>
|
||||
/// 根据入参名称从入参数据获取数据
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
object GetDataOfParams(string name);
|
||||
/// <summary>
|
||||
/// 获取全局数据
|
||||
/// </summary>
|
||||
/// <param name="keyName"></param>
|
||||
/// <returns></returns>
|
||||
object GetGlobalData(string keyName);
|
||||
/// <summary>
|
||||
/// 获取流程当前传递的数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
object GetFlowData();
|
||||
/// <summary>
|
||||
/// 立即调用某个节点并获取其返回值
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
Task<object> CallNode(string nodeGuid);
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,10 @@ namespace Serein.Library
|
||||
/// 全局数据
|
||||
/// </summary>
|
||||
GlobalData,
|
||||
/// <summary>
|
||||
/// 脚本节点
|
||||
/// </summary>
|
||||
Script,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,22 +22,11 @@ namespace Serein.Library
|
||||
public DelegateDetails(MethodInfo methodInfo)
|
||||
{
|
||||
var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var emitDelegate);
|
||||
_emitMethodType = emitMethodType;
|
||||
_emitMethodInfo = emitMethodType;
|
||||
_emitDelegate = emitDelegate;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 记录Emit委托
|
||||
/// </summary>
|
||||
/// <param name="EmitMethodType"></param>
|
||||
/// <param name="EmitDelegate"></param>
|
||||
public DelegateDetails(EmitMethodType EmitMethodType, Delegate EmitDelegate)
|
||||
{
|
||||
_emitMethodType = EmitMethodType;
|
||||
_emitDelegate = EmitDelegate;
|
||||
}
|
||||
|
||||
|
||||
/*/// <summary>
|
||||
/// 更新委托方法
|
||||
@@ -50,9 +39,13 @@ namespace Serein.Library
|
||||
_emitDelegate = EmitDelegate;
|
||||
}*/
|
||||
|
||||
|
||||
private Delegate _emitDelegate;
|
||||
private EmitMethodType _emitMethodType;
|
||||
private EmitMethodInfo _emitMethodInfo;
|
||||
|
||||
/// <summary>
|
||||
/// 该Emit委托的相应信息
|
||||
/// </summary>
|
||||
public EmitMethodInfo EmitMethodInfo => _emitMethodInfo;
|
||||
|
||||
///// <summary>
|
||||
///// <para>普通方法:Func<object,object[],object></para>
|
||||
@@ -65,6 +58,22 @@ namespace Serein.Library
|
||||
///// </summary>
|
||||
//public EmitMethodType EmitMethodType { get => _emitMethodType; }
|
||||
|
||||
|
||||
|
||||
public async Task<object> AutoInvokeAsync(object[] args)
|
||||
{
|
||||
if (_emitMethodInfo.IsStatic)
|
||||
{
|
||||
return await InvokeAsync(null, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
var obj = Activator.CreateInstance(_emitMethodInfo.DeclaringType);
|
||||
return await InvokeAsync(obj, args);
|
||||
}
|
||||
throw new Exception("Not static method");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>使用的实例必须能够正确调用该委托,传入的参数也必须符合方法入参信息。</para>
|
||||
/// </summary>
|
||||
@@ -77,16 +86,20 @@ namespace Serein.Library
|
||||
{
|
||||
args = Array.Empty<object>();
|
||||
}
|
||||
if(_emitMethodInfo.IsStatic)
|
||||
{
|
||||
instance = null;
|
||||
}
|
||||
object result = null;
|
||||
if (_emitMethodType == EmitMethodType.HasResultTask && _emitDelegate is Func<object, object[], Task<object>> hasResultTask)
|
||||
if (_emitDelegate is Func<object, object[], Task<object>> hasResultTask)
|
||||
{
|
||||
result = await hasResultTask(instance, args);
|
||||
}
|
||||
else if (_emitMethodType == EmitMethodType.Task && _emitDelegate is Func<object, object[], Task> task)
|
||||
else if (_emitDelegate is Func<object, object[], Task> task)
|
||||
{
|
||||
await task.Invoke(instance, args);
|
||||
}
|
||||
else if (_emitMethodType == EmitMethodType.Func && _emitDelegate is Func<object, object[], object> func)
|
||||
else if (_emitDelegate is Func<object, object[], object> func)
|
||||
{
|
||||
result = func.Invoke(instance, args);
|
||||
}
|
||||
|
||||
@@ -69,14 +69,6 @@ namespace Serein.Library
|
||||
|
||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体节点创建完成后调用的方法,调用时间早于 LoadInfo() 方法
|
||||
/// </summary>
|
||||
public virtual void OnCreating()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public NodeModelBase(IFlowEnvironment environment)
|
||||
{
|
||||
PreviousNodes = new Dictionary<ConnectionInvokeType, List<NodeModelBase>>();
|
||||
|
||||
@@ -29,6 +29,15 @@ namespace Serein.Library
|
||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||
{
|
||||
#region 节点相关事件
|
||||
/// <summary>
|
||||
/// 实体节点创建完成后调用的方法,调用时间早于 LoadInfo() 方法
|
||||
/// </summary>
|
||||
public virtual void OnCreating()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 保存自定义信息
|
||||
@@ -55,6 +64,7 @@ namespace Serein.Library
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除该节点
|
||||
/// </summary>
|
||||
@@ -95,7 +105,6 @@ namespace Serein.Library
|
||||
this.Env = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 输出方法参数信息
|
||||
/// </summary>
|
||||
@@ -125,7 +134,6 @@ namespace Serein.Library
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 导出为节点信息
|
||||
/// </summary>
|
||||
@@ -164,8 +172,6 @@ namespace Serein.Library
|
||||
return nodeInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从节点信息加载节点
|
||||
/// </summary>
|
||||
@@ -244,6 +250,8 @@ namespace Serein.Library
|
||||
|
||||
#region 节点方法的执行
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否应该退出执行
|
||||
/// </summary>
|
||||
|
||||
56
Library/FlowNode/ScriptFlowApi.cs
Normal file
56
Library/FlowNode/ScriptFlowApi.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Serein.Library.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 脚本代码中关于流程运行的API
|
||||
/// </summary>
|
||||
public class ScriptFlowApi : IScriptFlowApi
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程环境
|
||||
/// </summary>
|
||||
public IFlowEnvironment Env { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建流程脚本接口
|
||||
/// </summary>
|
||||
/// <param name="environment"></param>
|
||||
public ScriptFlowApi(IFlowEnvironment environment)
|
||||
{
|
||||
Env = environment;
|
||||
}
|
||||
|
||||
Task<object> IScriptFlowApi.CallNode(string nodeGuid)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
object IScriptFlowApi.GetDataOfParams(int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
object IScriptFlowApi.GetDataOfParams(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
object IScriptFlowApi.GetFlowData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
object IScriptFlowApi.GetGlobalData(string keyName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -12,11 +12,11 @@ namespace Serein.Library
|
||||
/// <summary>
|
||||
/// 全局触发器CTS
|
||||
/// </summary>
|
||||
public const string FlipFlopCtsName = "<>.FlowFlipFlopCts";
|
||||
public const string FlipFlopCtsName = "$FlowFlipFlopCts";
|
||||
/// <summary>
|
||||
/// 流程运行CTS
|
||||
/// </summary>
|
||||
public const string FlowRungCtsName = "<>.FlowRungCtsName";
|
||||
public const string FlowRungCtsName = "$FlowRungCtsName";
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="FlowNode\Attribute.cs" />
|
||||
<Compile Remove="FlowNode\ScriptFlowApi.cs" />
|
||||
<Compile Remove="Utils\NativeDllHelper.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -66,6 +66,95 @@ namespace Serein.Library.Utils
|
||||
}
|
||||
|
||||
|
||||
public static Type CreateTypeWithProperties(IDictionary<string, Type> properties, string typeName)
|
||||
{
|
||||
// 如果类型已经缓存,直接返回缓存的类型
|
||||
if (typeCache.ContainsKey(typeName))
|
||||
{
|
||||
return typeCache[typeName];
|
||||
}
|
||||
|
||||
// 定义动态程序集和模块
|
||||
var assemblyName = new AssemblyName("DynamicAssembly");
|
||||
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
|
||||
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
|
||||
|
||||
// 定义动态类型
|
||||
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
|
||||
|
||||
// 为每个属性名和值添加相应的属性到动态类型中
|
||||
foreach (var kvp in properties)
|
||||
{
|
||||
string propName = kvp.Key;
|
||||
object propValue = kvp.Value;
|
||||
Type propType;
|
||||
|
||||
if (propValue is IList<Dictionary<string, Type>>) // 处理数组类型
|
||||
{
|
||||
var nestedPropValue = (propValue as IList<Dictionary<string, Type>>)[0];
|
||||
var nestedType = CreateTypeWithProperties(nestedPropValue, $"{propName}Element");
|
||||
propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
|
||||
}
|
||||
else if (propValue is Dictionary<string, Type> nestedProperties)
|
||||
{
|
||||
// 如果值是嵌套的字典,递归创建嵌套类型
|
||||
propType = CreateTypeWithProperties(nestedProperties, $"{typeName}_{propName}").GetType();
|
||||
}
|
||||
else if (propValue is Type type)
|
||||
{
|
||||
// 如果是普通类型,使用值的类型
|
||||
propType = type ?? typeof(object);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"无法解析的类型:{propValue}");
|
||||
}
|
||||
|
||||
// 定义私有字段和公共属性
|
||||
var fieldBuilder = typeBuilder.DefineField("_" + propName, propType, FieldAttributes.Private);
|
||||
var propertyBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.HasDefault, propType, null);
|
||||
|
||||
// 定义 getter 方法
|
||||
var getMethodBuilder = typeBuilder.DefineMethod(
|
||||
"get_" + propName,
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
propType,
|
||||
Type.EmptyTypes);
|
||||
|
||||
var getIL = getMethodBuilder.GetILGenerator();
|
||||
getIL.Emit(OpCodes.Ldarg_0);
|
||||
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
|
||||
getIL.Emit(OpCodes.Ret);
|
||||
|
||||
// 定义 setter 方法
|
||||
var setMethodBuilder = typeBuilder.DefineMethod(
|
||||
"set_" + propName,
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
null,
|
||||
new Type[] { propType });
|
||||
|
||||
var setIL = setMethodBuilder.GetILGenerator();
|
||||
setIL.Emit(OpCodes.Ldarg_0);
|
||||
setIL.Emit(OpCodes.Ldarg_1);
|
||||
setIL.Emit(OpCodes.Stfld, fieldBuilder);
|
||||
setIL.Emit(OpCodes.Ret);
|
||||
|
||||
// 将 getter 和 setter 方法添加到属性
|
||||
propertyBuilder.SetGetMethod(getMethodBuilder);
|
||||
propertyBuilder.SetSetMethod(setMethodBuilder);
|
||||
}
|
||||
|
||||
// 创建类型并缓存
|
||||
var dynamicType = typeBuilder.CreateType();
|
||||
typeCache[typeName] = dynamicType;
|
||||
|
||||
// 创建对象实例
|
||||
return dynamicType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 动态创建对象并赋值
|
||||
|
||||
// 方法 1: 创建动态类型及其对象实例
|
||||
public static object CreateObjectWithProperties(IDictionary<string, object> properties, string typeName)
|
||||
@@ -298,6 +387,7 @@ namespace Serein.Library.Utils
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,20 @@ namespace Serein.Library.Utils
|
||||
/// </summary>
|
||||
public class EmitHelper
|
||||
{
|
||||
|
||||
public class EmitMethodInfo
|
||||
{
|
||||
public Type DeclaringType { get; set; }
|
||||
/// <summary>
|
||||
/// 是异步方法
|
||||
/// </summary>
|
||||
public bool IsTask { get; set; }
|
||||
/// <summary>
|
||||
/// 是静态的
|
||||
/// </summary>
|
||||
public bool IsStatic { get; set; }
|
||||
}
|
||||
|
||||
public enum EmitMethodType
|
||||
{
|
||||
/// <summary>
|
||||
@@ -28,6 +42,15 @@ namespace Serein.Library.Utils
|
||||
/// 有返回值的异步方法
|
||||
/// </summary>
|
||||
HasResultTask,
|
||||
|
||||
/// <summary>
|
||||
/// 普通的方法。如果方法返回void时,将会返回null。
|
||||
/// </summary>
|
||||
StaticFunc,
|
||||
/// <summary>
|
||||
/// 无返回值的异步方法
|
||||
/// </summary>
|
||||
StaticTask,
|
||||
}
|
||||
|
||||
public static bool IsGenericTask(Type returnType, out Type taskResult)
|
||||
@@ -60,17 +83,19 @@ namespace Serein.Library.Utils
|
||||
/// <param name="methodInfo"></param>
|
||||
/// <param name="delegate"></param>
|
||||
/// <returns></returns>
|
||||
public static EmitMethodType CreateDynamicMethod(MethodInfo methodInfo,out Delegate @delegate)
|
||||
public static EmitMethodInfo CreateDynamicMethod(MethodInfo methodInfo,out Delegate @delegate)
|
||||
{
|
||||
EmitMethodInfo emitMethodInfo = new EmitMethodInfo();
|
||||
bool IsTask = IsGenericTask(methodInfo.ReturnType, out var taskGenericsType);
|
||||
bool IsTaskGenerics = taskGenericsType != null;
|
||||
DynamicMethod dynamicMethod;
|
||||
|
||||
|
||||
Type returnType;
|
||||
if (!IsTask)
|
||||
{
|
||||
// 普通方法
|
||||
returnType = typeof(object);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -96,10 +121,19 @@ namespace Serein.Library.Utils
|
||||
|
||||
var il = dynamicMethod.GetILGenerator();
|
||||
|
||||
// 加载实例 (this)
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Castclass, methodInfo.DeclaringType); // 将 ISocketControlBase 转换为目标类类型
|
||||
// 判断是否为静态方法
|
||||
bool isStatic = methodInfo.IsStatic;
|
||||
|
||||
if (isStatic)
|
||||
{
|
||||
// 如果是静态方法,直接跳过实例(不加载Ldarg_0)
|
||||
}
|
||||
else
|
||||
{
|
||||
// 加载实例 (this) 对于非静态方法
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Castclass, methodInfo.DeclaringType); // 将 ISocketControlBase 转换为目标类类型
|
||||
}
|
||||
// 加载方法参数
|
||||
var methodParams = methodInfo.GetParameters();
|
||||
for (int i = 0; i < methodParams.Length; i++)
|
||||
@@ -122,11 +156,17 @@ namespace Serein.Library.Utils
|
||||
il.Emit(OpCodes.Castclass, paramType);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 调用方法
|
||||
il.Emit(OpCodes.Callvirt, methodInfo);
|
||||
// 调用方法:静态方法使用 Call,实例方法使用 Callvirt
|
||||
if (isStatic)
|
||||
{
|
||||
il.Emit(OpCodes.Call, methodInfo); // 对于静态方法,使用 Call
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Callvirt, methodInfo); // 对于实例方法,使用 Callvirt
|
||||
}
|
||||
|
||||
//// 处理返回值,如果没有返回值,则返回null
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
@@ -139,27 +179,28 @@ namespace Serein.Library.Utils
|
||||
}
|
||||
// 处理返回值,如果没有返回值,则返回null
|
||||
il.Emit(OpCodes.Ret); // 返回
|
||||
EmitMethodType emitMethodType;
|
||||
if (IsTask)
|
||||
{
|
||||
if (IsTaskGenerics)
|
||||
{
|
||||
emitMethodType = EmitMethodType.HasResultTask;
|
||||
@delegate = dynamicMethod.CreateDelegate(typeof(Func<object, object[], Task<object>>));
|
||||
}
|
||||
else
|
||||
{
|
||||
emitMethodType = EmitMethodType.Task;
|
||||
@delegate = dynamicMethod.CreateDelegate(typeof(Func<object, object[], Task>));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
emitMethodType = EmitMethodType.Func;
|
||||
@delegate = dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
|
||||
|
||||
}
|
||||
return emitMethodType;
|
||||
return new EmitMethodInfo
|
||||
{
|
||||
DeclaringType = methodInfo.DeclaringType,
|
||||
IsTask = IsTask,
|
||||
IsStatic = isStatic
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace Serein.Library.Utils
|
||||
/// </summary>
|
||||
public class SereinIOC/* : ISereinIOC*/
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 类型集合,暂放待实例化的类型,完成实例化之后移除
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user