mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-12 10:56:34 +08:00
重写了Emit构造委托的执行
This commit is contained in:
@@ -27,5 +27,35 @@ namespace Serein.Library.Entity
|
|||||||
private EmitMethodType _emitMethodType;
|
private EmitMethodType _emitMethodType;
|
||||||
public Delegate EmitDelegate { get => _emitDelegate; }
|
public Delegate EmitDelegate { get => _emitDelegate; }
|
||||||
public EmitMethodType EmitMethodType { get => _emitMethodType; }
|
public EmitMethodType EmitMethodType { get => _emitMethodType; }
|
||||||
|
|
||||||
|
public async Task<object> Invoke(object instance, object[] args)
|
||||||
|
{
|
||||||
|
object result = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (EmitMethodType == EmitMethodType.HasResultTask && 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)
|
||||||
|
{
|
||||||
|
await task.Invoke(instance, args);
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
else if (EmitMethodType == EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
|
||||||
|
{
|
||||||
|
result = func.Invoke(instance, args);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("创建了非预期委托(应该不会出现)");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,26 +7,69 @@ using System.Net.WebSockets;
|
|||||||
|
|
||||||
namespace Serein.Library.Network.WebSocketCommunication
|
namespace Serein.Library.Network.WebSocketCommunication
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para>标记该类是处理模板,需要获取WebSocketServer/WebSocketClient了实例后,使用(Server/Client).MsgHandleHelper.AddModule()进行添加。</para>
|
||||||
|
/// <para>处理模板需要继承 ISocketHandleModule 接口,否则WebSocket接受到数据时,将无法进行调用相应的处理模板。</para>
|
||||||
|
/// <para>使用方式:</para>
|
||||||
|
/// <para>[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]</para>
|
||||||
|
/// <para>public class PlcSocketService : ISocketHandleModule</para>
|
||||||
|
/// <para>类中方法示例:void AddUser(string name,age 35)</para>
|
||||||
|
/// <para>Json示例:{ "theme":"AddUser", //【ThemeKey】 </para>
|
||||||
|
/// <para> "data": { // 【DataKey】 </para>
|
||||||
|
/// <para> "name":"张三", </para>
|
||||||
|
/// <para> "age":35, } } </para>
|
||||||
|
/// <para>WebSocket中收到以上该Json时,通过ThemeKey获取到"AddUser",然后找到AddUser()方法,并将"data"作为数据对象,取出相应数据作为入参(args:"张三",35)进行调用(如果有)</para>
|
||||||
|
/// <para></para>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public sealed class AutoSocketModuleAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string ThemeKey;
|
||||||
|
public string DataKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>作用:WebSocket中处理Json时,将通过Json中ThemeKey 对应的内容(ThemeValue)自动路由到相应方法进行处理。</para>
|
||||||
|
/// <para>如果没有显式设置ThemeValue,将默认使用方法名称作为ThemeValue。</para>
|
||||||
|
/// <para>如果没有显式设置IsReturnValue标记为false,当方法顺利完成(没有抛出异常,且返回对象非null),会自动转为json文本发送回去</para>
|
||||||
|
/// <para>如果返回类型为Task或Task<TResult>,将会自动等待异步完成并获取结果(无法处理Task<Task<TResult>>的情况)。</para>
|
||||||
|
/// <para>如果返回了值类型,会自动装箱为引用对象。</para>
|
||||||
|
/// <para>如果有方法执行过程中发送消息的需求,请在入参中声明以下类型的成员,调用时将传入发送消息的委托。</para>
|
||||||
|
/// <para>Action<string> : 发送文本内容。</para>
|
||||||
|
/// <para>Action<object> : 会自动将对象解析为Json字符串,发送文本内容。</para>
|
||||||
|
/// <para>Action<dynamic> : 会自动将对象解析为Json字符串,发送文本内容。</para>
|
||||||
|
/// <para>Func<string,Task> : 异步发送文本内容。</para>
|
||||||
|
/// <para>Func<object,Task> : 会自动将对象解析为Json字符串,异步发送文本内容。</para>
|
||||||
|
/// <para>Func<dynamic,Task> : 会自动将对象解析为Json字符串,异步发送文本内容。</para>
|
||||||
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
public sealed class AutoSocketHandleAttribute : Attribute
|
public sealed class AutoSocketHandleAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 描述Json业务字段,如果不设置,将默认使用方法名称。
|
||||||
|
/// </summary>
|
||||||
public string ThemeValue = string.Empty;
|
public string ThemeValue = string.Empty;
|
||||||
|
/// <summary>
|
||||||
|
/// <para>标记方法执行完成后是否需要将结果发送。</para>
|
||||||
|
/// <para>但以下情况将不会发送:</para>
|
||||||
|
/// <para>1.返回类型为void</para>
|
||||||
|
/// <para>2.返回类型为Task</para>
|
||||||
|
/// <para>3.返回了null</para>
|
||||||
|
/// <para>补充:如果返回类型是Task<TResult></para>
|
||||||
|
/// <para>会进行异步等待,当Task结束后,自动获取TResult进行发送(请避免Task<Task<TResult>>诸如此类的Task泛型嵌套)</para>
|
||||||
|
/// </summary>
|
||||||
public bool IsReturnValue = true;
|
public bool IsReturnValue = true;
|
||||||
//public Type DataType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SocketHandleModel
|
internal class SocketHandleModel
|
||||||
{
|
{
|
||||||
public string ThemeValue { get; set; } = string.Empty;
|
public string ThemeValue { get; set; } = string.Empty;
|
||||||
public bool IsReturnValue { get; set; } = true;
|
public bool IsReturnValue { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
public sealed class AutoSocketModuleAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string JsonDataField;
|
|
||||||
public string JsonThemeField;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Serein.Library.Network.WebSocketCommunication.Handle
|
namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||||
{
|
{
|
||||||
public class MyHandleConfig
|
public class WebSocketHandleConfig
|
||||||
{
|
{
|
||||||
private readonly Delegate EmitDelegate;
|
private readonly Delegate EmitDelegate;
|
||||||
private readonly EmitHelper.EmitMethodType EmitMethodType;
|
private readonly EmitHelper.EmitMethodType EmitMethodType;
|
||||||
|
|
||||||
private Action<Exception, Action<object>> OnExceptionTracking;
|
private Action<Exception, Action<object>> OnExceptionTracking;
|
||||||
|
|
||||||
public MyHandleConfig(SocketHandleModel model,ISocketControlBase instance, MethodInfo methodInfo, Action<Exception, Action<object>> onExceptionTracking)
|
internal WebSocketHandleConfig(SocketHandleModel model,ISocketHandleModule instance, MethodInfo methodInfo, Action<Exception, Action<object>> onExceptionTracking)
|
||||||
{
|
{
|
||||||
EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo,out EmitDelegate);
|
EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo,out EmitDelegate);
|
||||||
this.Model = model;
|
this.Model = model;
|
||||||
@@ -36,7 +36,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SocketHandleModel Model;
|
private SocketHandleModel Model;
|
||||||
private ISocketControlBase Instance;
|
private ISocketHandleModule Instance;
|
||||||
public Guid HandleGuid { get; }
|
public Guid HandleGuid { get; }
|
||||||
private string[] ParameterName;
|
private string[] ParameterName;
|
||||||
private Type[] ParameterType;
|
private Type[] ParameterType;
|
||||||
@@ -51,7 +51,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
|||||||
var argName = ParameterName[i];
|
var argName = ParameterName[i];
|
||||||
if (type.IsGenericType)
|
if (type.IsGenericType)
|
||||||
{
|
{
|
||||||
if (type.IsAssignableFrom(typeof(Func<object, Task>)))
|
if (type.IsAssignableFrom(typeof(Func<object, Task>)))
|
||||||
{
|
{
|
||||||
args[i] = new Func<object, Task>(async data =>
|
args[i] = new Func<object, Task>(async data =>
|
||||||
{
|
{
|
||||||
@@ -8,9 +8,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Serein.Library.Network.WebSocketCommunication.Handle
|
namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||||
{
|
{
|
||||||
public class MyHandleModule
|
public class WebSocketHandleModule
|
||||||
{
|
{
|
||||||
public MyHandleModule(string ThemeJsonKey, string DataJsonKey)
|
public WebSocketHandleModule(string ThemeJsonKey, string DataJsonKey)
|
||||||
{
|
{
|
||||||
this.ThemeJsonKey = ThemeJsonKey;
|
this.ThemeJsonKey = ThemeJsonKey;
|
||||||
this.DataJsonKey = DataJsonKey;
|
this.DataJsonKey = DataJsonKey;
|
||||||
@@ -21,17 +21,17 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public ConcurrentDictionary<string, MyHandleConfig> MyHandleConfigs = new ConcurrentDictionary<string, MyHandleConfig>();
|
public ConcurrentDictionary<string, WebSocketHandleConfig> MyHandleConfigs = new ConcurrentDictionary<string, WebSocketHandleConfig>();
|
||||||
public void AddHandleConfigs(SocketHandleModel model, ISocketControlBase instance, MethodInfo methodInfo
|
internal void AddHandleConfigs(SocketHandleModel model, ISocketHandleModule instance, MethodInfo methodInfo
|
||||||
, Action<Exception, Action<object>> onExceptionTracking)
|
, Action<Exception, Action<object>> onExceptionTracking)
|
||||||
{
|
{
|
||||||
if (!MyHandleConfigs.ContainsKey(model.ThemeValue))
|
if (!MyHandleConfigs.ContainsKey(model.ThemeValue))
|
||||||
{
|
{
|
||||||
var myHandleConfig = new MyHandleConfig(model,instance, methodInfo, onExceptionTracking);
|
var myHandleConfig = new WebSocketHandleConfig(model,instance, methodInfo, onExceptionTracking);
|
||||||
MyHandleConfigs[model.ThemeValue] = myHandleConfig;
|
MyHandleConfigs[model.ThemeValue] = myHandleConfig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public bool ResetConfig(ISocketControlBase socketControlBase)
|
public bool ResetConfig(ISocketHandleModule socketControlBase)
|
||||||
{
|
{
|
||||||
foreach (var kv in MyHandleConfigs.ToArray())
|
foreach (var kv in MyHandleConfigs.ToArray())
|
||||||
{
|
{
|
||||||
@@ -20,13 +20,13 @@ using System.Security.Cryptography;
|
|||||||
namespace Serein.Library.Network.WebSocketCommunication.Handle
|
namespace Serein.Library.Network.WebSocketCommunication.Handle
|
||||||
{
|
{
|
||||||
|
|
||||||
public class SocketMsgHandleHelper
|
public class WebSocketMsgHandleHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// (Theme Name ,Data Name) - HandleModule
|
/// (Theme Name ,Data Name) - HandleModule
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConcurrentDictionary<(string, string), MyHandleModule> MyHandleModuleDict
|
public ConcurrentDictionary<(string, string), WebSocketHandleModule> MyHandleModuleDict
|
||||||
= new ConcurrentDictionary<(string, string), MyHandleModule>();
|
= new ConcurrentDictionary<(string, string), WebSocketHandleModule>();
|
||||||
|
|
||||||
private Action<Exception, Action<object>> _onExceptionTracking;
|
private Action<Exception, Action<object>> _onExceptionTracking;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -34,18 +34,18 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<Exception, Action<object>> OnExceptionTracking;
|
public event Action<Exception, Action<object>> OnExceptionTracking;
|
||||||
|
|
||||||
private MyHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName)
|
private WebSocketHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName)
|
||||||
{
|
{
|
||||||
var key = (themeKeyName, dataKeyName);
|
var key = (themeKeyName, dataKeyName);
|
||||||
if (!MyHandleModuleDict.TryGetValue(key, out var myHandleModule))
|
if (!MyHandleModuleDict.TryGetValue(key, out var myHandleModule))
|
||||||
{
|
{
|
||||||
myHandleModule = new MyHandleModule(themeKeyName, dataKeyName);
|
myHandleModule = new WebSocketHandleModule(themeKeyName, dataKeyName);
|
||||||
MyHandleModuleDict[key] = myHandleModule;
|
MyHandleModuleDict[key] = myHandleModule;
|
||||||
}
|
}
|
||||||
return myHandleModule;
|
return myHandleModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoteModule(ISocketControlBase socketControlBase)
|
public void RemoteModule(ISocketHandleModule socketControlBase)
|
||||||
{
|
{
|
||||||
var type = socketControlBase.GetType();
|
var type = socketControlBase.GetType();
|
||||||
var moduleAttribute = type.GetCustomAttribute<AutoSocketModuleAttribute>();
|
var moduleAttribute = type.GetCustomAttribute<AutoSocketModuleAttribute>();
|
||||||
@@ -53,8 +53,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var themeKeyName = moduleAttribute.JsonThemeField;
|
var themeKeyName = moduleAttribute.ThemeKey;
|
||||||
var dataKeyName = moduleAttribute.JsonDataField;
|
var dataKeyName = moduleAttribute.DataKey;
|
||||||
var key = (themeKeyName, dataKeyName);
|
var key = (themeKeyName, dataKeyName);
|
||||||
if (MyHandleModuleDict.TryGetValue(key, out var myHandleModules))
|
if (MyHandleModuleDict.TryGetValue(key, out var myHandleModules))
|
||||||
{
|
{
|
||||||
@@ -63,7 +63,14 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public void AddModule(ISocketControlBase socketControlBase, Action<Exception, Action<object>> onExceptionTracking)
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="socketControlBase"></param>
|
||||||
|
/// <param name="onExceptionTracking"></param>
|
||||||
|
public void AddModule(ISocketHandleModule socketControlBase, Action<Exception, Action<object>> onExceptionTracking)
|
||||||
{
|
{
|
||||||
var type = socketControlBase.GetType();
|
var type = socketControlBase.GetType();
|
||||||
var moduleAttribute = type.GetCustomAttribute<AutoSocketModuleAttribute>();
|
var moduleAttribute = type.GetCustomAttribute<AutoSocketModuleAttribute>();
|
||||||
@@ -72,8 +79,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var themeKey = moduleAttribute.JsonThemeField;
|
var themeKey = moduleAttribute.ThemeKey;
|
||||||
var dataKey = moduleAttribute.JsonDataField;
|
var dataKey = moduleAttribute.DataKey;
|
||||||
|
|
||||||
var handlemodule = AddMyHandleModule(themeKey, dataKey);
|
var handlemodule = AddMyHandleModule(themeKey, dataKey);
|
||||||
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Serein.Library.Network.WebSocketCommunication
|
namespace Serein.Library.Network.WebSocketCommunication
|
||||||
{
|
{
|
||||||
public interface ISocketControlBase
|
public interface ISocketHandleModule
|
||||||
{
|
{
|
||||||
Guid HandleGuid { get; }
|
Guid HandleGuid { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Serein.Library.Network.WebSocketCommunication
|
namespace Serein.Library.Network.WebSocketCommunication
|
||||||
{
|
{
|
||||||
|
[AutoRegister]
|
||||||
public class WebSocketServer
|
public class WebSocketServer
|
||||||
{
|
{
|
||||||
public SocketMsgHandleHelper MsgHandleHelper { get; } = new SocketMsgHandleHelper();
|
public WebSocketMsgHandleHelper MsgHandleHelper { get; } = new WebSocketMsgHandleHelper();
|
||||||
|
|
||||||
HttpListener listener;
|
private HttpListener listener;
|
||||||
public async Task StartAsync(string url)
|
public async Task StartAsync(string url)
|
||||||
{
|
{
|
||||||
listener = new HttpListener();
|
listener = new HttpListener();
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ using System;
|
|||||||
namespace Serein.Library.Attributes
|
namespace Serein.Library.Attributes
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示该属性为自动注入依赖项
|
/// <para>表示该属性为自动注入依赖项。</para>
|
||||||
|
/// <para>使用场景:构造函数中存在互相依赖的情况</para>
|
||||||
|
/// <para>例如ServiceA类构造函数中需要传入ServiceB,ServiceB类构造函数中也需要传入ServiceA</para>
|
||||||
|
/// <para>这种情况会导致流程启动时,IOC容器无法注入构造函数并创建类型,导致启动失败。</para>
|
||||||
|
/// <para>解决方法:从ServiceA类的构造函数中移除ServiceB类型的入参,将该类型更改为公开可见的可写属性成员ServiceB serviceB{get;set;},并在该属性上标记[AutoInjection]特性</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public sealed class AutoInjectionAttribute : Attribute
|
public sealed class AutoInjectionAttribute : Attribute
|
||||||
@@ -25,8 +29,17 @@ namespace Serein.Library.Attributes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
FlowLoading,
|
FlowLoading,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示该类自动注册(单例模式)
|
/// <para>启动流程时,会将标记了该特性的类自动注册到IOC容器中,从而无需手动进行注册绑定。</para>
|
||||||
|
/// <para>流程启动后,IOC容器会进行5次注册绑定。</para>
|
||||||
|
/// <para>第1次注册绑定:初始化所有节点所属的类([DynamicFlow]标记的类)。</para>
|
||||||
|
/// <para>第2次注册绑定:※初始化所有[AutoRegister(Class=FlowInit)]的类。</para>
|
||||||
|
/// <para>第3次注册绑定:调用所有Init节点后,进行注册绑定。</para>
|
||||||
|
/// <para>第4次注册绑定:※初始化所有[AutoRegister(Class=FlowLoading)]的类</para>
|
||||||
|
/// <para>第5次注册绑定:调用所有Load节点后,进行注册绑定。</para>
|
||||||
|
/// <para>需要注意的是,在第1次进行注册绑定的过程中,如果类的构造函数存在入参,那么也会将入参自动创建实例并托管到IOC容器中。</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public sealed class AutoRegisterAttribute : Attribute
|
public sealed class AutoRegisterAttribute : Attribute
|
||||||
@@ -39,7 +52,7 @@ namespace Serein.Library.Attributes
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用来判断一个类是否需要注册并构建节点
|
/// <para>表示该类中存在节点信息</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class DynamicFlowAttribute : Attribute
|
public class DynamicFlowAttribute : Attribute
|
||||||
@@ -49,14 +62,21 @@ namespace Serein.Library.Attributes
|
|||||||
Name = name;
|
Name = name;
|
||||||
Scan = scan;
|
Scan = scan;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 补充名称,不影响运行流程
|
||||||
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 如果设置为false,将忽略该类
|
||||||
|
/// </summary>
|
||||||
public bool Scan { get; set; } = true;
|
public bool Scan { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 生成的节点类型
|
/// <para>表示该方法将会生成节点,或是加入到流程运行中</para>
|
||||||
|
/// <para>如果是Task类型的返回值,将会自动进行等待</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||||
public class NodeActionAttribute : Attribute
|
public class NodeActionAttribute : Attribute
|
||||||
@@ -71,10 +91,25 @@ namespace Serein.Library.Attributes
|
|||||||
MethodTips = methodTips;
|
MethodTips = methodTips;
|
||||||
LockName = lockName;
|
LockName = lockName;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 如果设置为false时将不会生成节点信息
|
||||||
|
/// </summary>
|
||||||
public bool Scan;
|
public bool Scan;
|
||||||
|
/// <summary>
|
||||||
|
/// 类似于注释的效果
|
||||||
|
/// </summary>
|
||||||
public string MethodTips;
|
public string MethodTips;
|
||||||
|
/// <summary>
|
||||||
|
/// 标记节点行为
|
||||||
|
/// </summary>
|
||||||
public NodeType MethodDynamicType;
|
public NodeType MethodDynamicType;
|
||||||
|
/// <summary>
|
||||||
|
/// 显示标注方法返回类型,不影响运行逻辑(用于表示触发器触发后返回的数据)
|
||||||
|
/// </summary>
|
||||||
public Type ReturnType;
|
public Type ReturnType;
|
||||||
|
/// <summary>
|
||||||
|
/// 暂无意义
|
||||||
|
/// </summary>
|
||||||
public string LockName;
|
public string LockName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,20 @@ namespace Net462DllTest.Properties
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
理想的项目架构:
|
理想的项目架构:
|
||||||
|
|
||||||
|
每一种功能拆分为新的项目
|
||||||
|
|
||||||
FlowEnv - LoginControl:
|
FlowEnv - LoginControl:
|
||||||
|
|
||||||
|
|
||||||
LoginControl
|
LoginControl
|
||||||
↙ ↘
|
↙ ↘
|
||||||
(View-Interaction) (Node-Interaction)
|
(View-Interaction) (Node-Interaction)
|
||||||
↓ ↕
|
↓ ↕
|
||||||
View ←→ ViewModel ←→ Trigger ← SingleEnum
|
View ←→ ViewModel ←→ Trigger ← SingleEnum
|
||||||
↓
|
↓ ↓ ↖
|
||||||
Model
|
↓ ↓ ↖
|
||||||
· DataChanged → Trigger
|
Node →→→ Model → Event(OnDataChanged)
|
||||||
|
|
||||||
|
|
||||||
视图驱动触发器,触发器驱动数据。
|
视图驱动触发器,触发器驱动数据。
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ namespace Net462DllTest.Web
|
|||||||
|
|
||||||
[DynamicFlow("[PlcSocketService]")]
|
[DynamicFlow("[PlcSocketService]")]
|
||||||
[AutoRegister]
|
[AutoRegister]
|
||||||
[AutoSocketModule(JsonThemeField = "theme", JsonDataField = "data")]
|
[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]
|
||||||
public class PlcSocketService : ISocketControlBase
|
public class PlcSocketService : ISocketHandleModule
|
||||||
{
|
{
|
||||||
public Guid HandleGuid { get; } = new Guid();
|
public Guid HandleGuid { get; } = new Guid();
|
||||||
|
|
||||||
@@ -101,6 +101,14 @@ namespace Net462DllTest.Web
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AutoSocketHandle]
|
||||||
|
public object ReadVar(PlcVarName varName)
|
||||||
|
{
|
||||||
|
var result = MyPlc.Read(varName);
|
||||||
|
Console.WriteLine($"获取变量成功:({varName})\t result = {result}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
[AutoSocketHandle(IsReturnValue = false)]
|
[AutoSocketHandle(IsReturnValue = false)]
|
||||||
public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None,
|
public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None,
|
||||||
string ip = "192.168.10.100",
|
string ip = "192.168.10.100",
|
||||||
@@ -134,13 +142,7 @@ namespace Net462DllTest.Web
|
|||||||
return MyPlc;
|
return MyPlc;
|
||||||
}
|
}
|
||||||
|
|
||||||
[AutoSocketHandle]
|
|
||||||
public object ReadVar(PlcVarName varName)
|
|
||||||
{
|
|
||||||
var result = MyPlc.Read(varName);
|
|
||||||
Console.WriteLine($"获取变量成功:({varName})\t result = {result}");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[AutoSocketHandle(IsReturnValue = false)]
|
[AutoSocketHandle(IsReturnValue = false)]
|
||||||
public SiemensPlcDevice WriteVar(object value, PlcVarName varName)
|
public SiemensPlcDevice WriteVar(object value, PlcVarName varName)
|
||||||
@@ -149,9 +151,12 @@ namespace Net462DllTest.Web
|
|||||||
return MyPlc;
|
return MyPlc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlcVarModelDataProxy BatchReadVar()
|
[AutoSocketHandle]
|
||||||
|
public PlcVarModelDataProxy BatchReadVar(Func<string,Task> send)
|
||||||
{
|
{
|
||||||
|
send("开始读取");
|
||||||
MyPlc.BatchRefresh();
|
MyPlc.BatchRefresh();
|
||||||
|
send("读取完成");
|
||||||
return plcVarModelDataProxy;
|
return plcVarModelDataProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -204,11 +204,7 @@ namespace Serein.NodeFlow.Base
|
|||||||
md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
|
md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
|
||||||
object instance = md.ActingInstance;
|
object instance = md.ActingInstance;
|
||||||
|
|
||||||
//bool haveParameter = md.ExplicitDatas.Length > 0;
|
|
||||||
//bool haveResult = md.ReturnType != typeof(void);
|
|
||||||
// Type? taskResult = null;
|
|
||||||
//bool isTask = md.ReturnType is not null && MethodDetailsHelper.IsGenericTask(md.ReturnType, out taskResult);
|
|
||||||
//bool isTaskHaveResult = taskResult is not null;
|
|
||||||
object? result = null;
|
object? result = null;
|
||||||
|
|
||||||
//Console.WriteLine($"(isTask, isTaskHaveResult):{(isTask, isTaskHaveResult)}");
|
//Console.WriteLine($"(isTask, isTaskHaveResult):{(isTask, isTaskHaveResult)}");
|
||||||
@@ -217,49 +213,7 @@ namespace Serein.NodeFlow.Base
|
|||||||
// Action/Func([方法作用的实例],[可能的参数值],[可能的返回值])
|
// Action/Func([方法作用的实例],[可能的参数值],[可能的返回值])
|
||||||
|
|
||||||
object?[]? args = GetParameters(context, this, md);
|
object?[]? args = GetParameters(context, this, md);
|
||||||
var delType = dd.EmitMethodType;
|
result = await dd.Invoke(md.ActingInstance, args);
|
||||||
var del = dd.EmitDelegate;
|
|
||||||
if (delType == EmitHelper.EmitMethodType.HasResultTask && del is Func<object, object?[]?, Task<object?>> hasResultTask)
|
|
||||||
{
|
|
||||||
result = await hasResultTask(instance, args);
|
|
||||||
}
|
|
||||||
else if (delType == EmitHelper.EmitMethodType.Task && del is Func<object, object?[]?, Task> task)
|
|
||||||
{
|
|
||||||
await task.Invoke(instance, args);
|
|
||||||
result = null;
|
|
||||||
}
|
|
||||||
else if (delType == EmitHelper.EmitMethodType.Func && del is Func<object, object?[]?, object?> func)
|
|
||||||
{
|
|
||||||
result = func.Invoke(instance, args);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("构造委托无法正确调用");
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (isTask)
|
|
||||||
//{
|
|
||||||
// // 异步方法(因为返回了Task,所以排除Action<>委托的可能)
|
|
||||||
// result = (haveParameter, isTaskHaveResult) switch
|
|
||||||
// {
|
|
||||||
// (false, false) => await ExecutionAsync((Func<object, Task>)del, instance), // 调用节点方法,返回方法传回类型
|
|
||||||
// (true, false) => await ExecutionAsync((Func<object, object?[]?, Task>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型
|
|
||||||
// (false, true) => await ExecutionAsync((Func<object, Task<object?>>)del, instance), // 调用节点方法,返回方法传回类型
|
|
||||||
// (true, true) => await ExecutionAsync((Func<object, object?[]?, Task<object?>>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型
|
|
||||||
// };
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// // 非异步方法
|
|
||||||
// result = (haveParameter, haveResult) switch
|
|
||||||
// {
|
|
||||||
// (false, false) => Execution((Action<object>)del, instance), // 调用节点方法,返回null
|
|
||||||
// (true, false) => Execution((Action<object, object?[]?>)del, instance, parameters), // 调用节点方法,返回null
|
|
||||||
// (false, true) => Execution((Func<object, object?>)del, instance), // 调用节点方法,返回方法传回类型
|
|
||||||
// (true, true) => Execution((Func<object, object?[]?, object?>)del, instance, parameters), // 调用节点方法,获取入参参数,返回方法返回类型
|
|
||||||
// };
|
|
||||||
//}
|
|
||||||
|
|
||||||
NextOrientation = ConnectionType.IsSucceed;
|
NextOrientation = ConnectionType.IsSucceed;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -273,47 +227,6 @@ namespace Serein.NodeFlow.Base
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#region 节点转换的委托类型
|
|
||||||
public static object? Execution(Action<object> del, object instance)
|
|
||||||
{
|
|
||||||
del.Invoke(instance);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public static object? Execution(Action<object, object?[]?> del, object instance, object?[]? parameters)
|
|
||||||
{
|
|
||||||
del.Invoke(instance, parameters);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public static object? Execution(Func<object, object?> del, object instance)
|
|
||||||
{
|
|
||||||
return del.Invoke(instance);
|
|
||||||
}
|
|
||||||
public static object? Execution(Func<object, object?[]?, object?> del, object instance, object?[]? parameters)
|
|
||||||
{
|
|
||||||
return del.Invoke(instance, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static async Task<object?> ExecutionAsync(Func<object, Task> del, object instance)
|
|
||||||
{
|
|
||||||
await del.Invoke(instance);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public static async Task<object?> ExecutionAsync(Func<object, object?[]?, Task> del, object instance, object?[]? parameters)
|
|
||||||
{
|
|
||||||
await del.Invoke(instance, parameters);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public static async Task<object?> ExecutionAsync(Func<object, Task<object?>> del, object instance)
|
|
||||||
{
|
|
||||||
return await del.Invoke(instance);
|
|
||||||
}
|
|
||||||
public static async Task<object?> ExecutionAsync(Func<object, object?[]?, Task<object?>> del, object instance, object?[]? parameters)
|
|
||||||
{
|
|
||||||
return await del.Invoke(instance, parameters);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取对应的参数数组
|
/// 获取对应的参数数组
|
||||||
|
|||||||
@@ -984,10 +984,10 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var methods = MethodDetailsHelper.GetMethodsToProcess(type);
|
var methods = NodeMethodDetailsHelper.GetMethodsToProcess(type);
|
||||||
foreach(var method in methods)
|
foreach(var method in methods)
|
||||||
{
|
{
|
||||||
(var md, var del) = MethodDetailsHelper.CreateMethodDetails(type, method, assemblyName);
|
(var md, var del) = NodeMethodDetailsHelper.CreateMethodDetails(type, method, assemblyName);
|
||||||
if(md is null || del is null)
|
if(md is null || del is null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{method}");
|
Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{method}");
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace Serein.NodeFlow
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 结束运行时需要执行的方法
|
/// 结束运行时需要执行的方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Action ExitAction { get; set; } = null;
|
private Func<Task> ExitAction { get; set; } = null;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 运行的上下文
|
/// 运行的上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -136,7 +136,6 @@ namespace Serein.NodeFlow
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region 选择运行环境的上下文
|
#region 选择运行环境的上下文
|
||||||
|
|
||||||
// 判断使用哪一种流程上下文
|
// 判断使用哪一种流程上下文
|
||||||
@@ -234,7 +233,8 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
throw new Exception("不存在对应委托");
|
throw new Exception("不存在对应委托");
|
||||||
}
|
}
|
||||||
((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
await dd.Invoke(md.ActingInstance, [Context]);
|
||||||
|
//((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
||||||
}
|
}
|
||||||
Context.Env.IOC.Build(); // 绑定初始化时注册的类型
|
Context.Env.IOC.Build(); // 绑定初始化时注册的类型
|
||||||
|
|
||||||
@@ -254,14 +254,15 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
throw new Exception("不存在对应委托");
|
throw new Exception("不存在对应委托");
|
||||||
}
|
}
|
||||||
|
await dd.Invoke(md.ActingInstance, [Context]);
|
||||||
//((Action<object, object?[]?>)del).Invoke(md.ActingInstance, [Context]);
|
//((Action<object, object?[]?>)del).Invoke(md.ActingInstance, [Context]);
|
||||||
((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
//((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
||||||
}
|
}
|
||||||
Context.Env.IOC.Build(); // 预防有人在加载时才注册类型,再绑定一次
|
Context.Env.IOC.Build(); // 预防有人在加载时才注册类型,再绑定一次
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 设置流程退出时的回调函数
|
#region 设置流程退出时的回调函数
|
||||||
ExitAction = () =>
|
ExitAction = async () =>
|
||||||
{
|
{
|
||||||
env.IOC.Run<WebApiServer>(web => {
|
env.IOC.Run<WebApiServer>(web => {
|
||||||
web?.Stop();
|
web?.Stop();
|
||||||
@@ -273,7 +274,8 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
throw new Exception("不存在对应委托");
|
throw new Exception("不存在对应委托");
|
||||||
}
|
}
|
||||||
((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
await dd.Invoke(md.ActingInstance, [Context]);
|
||||||
|
//((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TerminateAllGlobalFlipflop();
|
TerminateAllGlobalFlipflop();
|
||||||
|
|||||||
@@ -24,13 +24,12 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
//public override object? Executing(IDynamicContext context)
|
public override async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||||
public override Task<object?> ExecutingAsync(IDynamicContext context)
|
|
||||||
{
|
{
|
||||||
// 条件区域中遍历每个条件节点
|
// 条件区域中遍历每个条件节点
|
||||||
foreach (SingleConditionNode? node in ConditionNodes)
|
foreach (SingleConditionNode? node in ConditionNodes)
|
||||||
{
|
{
|
||||||
var state = Judge(context, node);
|
var state = await JudgeAsync(context, node);
|
||||||
NextOrientation = state; // 每次判读完成后,设置区域后继方向为判断结果
|
NextOrientation = state; // 每次判读完成后,设置区域后继方向为判断结果
|
||||||
if (state != ConnectionType.IsSucceed)
|
if (state != ConnectionType.IsSucceed)
|
||||||
{
|
{
|
||||||
@@ -42,11 +41,11 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ConnectionType Judge(IDynamicContext context, SingleConditionNode node)
|
private async Task<ConnectionType> JudgeAsync(IDynamicContext context, SingleConditionNode node)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
node.ExecutingAsync(context);
|
await node.ExecutingAsync(context);
|
||||||
return node.NextOrientation;
|
return node.NextOrientation;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -40,31 +40,45 @@ namespace Serein.NodeFlow.Model
|
|||||||
object instance = md.ActingInstance;
|
object instance = md.ActingInstance;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Task<IFlipflopContext> flipflopTask;
|
|
||||||
var args = GetParameters(context, this, md);
|
var args = GetParameters(context, this, md);
|
||||||
var delType = dd.EmitMethodType;
|
var result = await dd.Invoke(md.ActingInstance, args);
|
||||||
var del = dd.EmitDelegate;
|
if (result is IFlipflopContext flipflopContext)
|
||||||
if (delType == EmitHelper.EmitMethodType.HasResultTask && del is Func<object, object?[]?, Task<object>> hasResultTask)
|
|
||||||
{
|
{
|
||||||
var flipflopTaskObj = await hasResultTask(instance, args);
|
NextOrientation = flipflopContext.State.ToContentType();
|
||||||
if(flipflopTaskObj is IFlipflopContext flipflopContext)
|
if (flipflopContext.TriggerData is null || flipflopContext.TriggerData.Type == Library.NodeFlow.Tool.TriggerType.Overtime)
|
||||||
{
|
{
|
||||||
NextOrientation = flipflopContext.State.ToContentType();
|
throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
||||||
if (flipflopContext.TriggerData is null || flipflopContext.TriggerData.Type == Library.NodeFlow.Tool.TriggerType.Overtime)
|
|
||||||
{
|
|
||||||
throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
|
||||||
}
|
|
||||||
return flipflopContext.TriggerData.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new FlipflopException("触发器节点返回了非预期的类型", true, FlipflopException.CancelClass.Flow);
|
|
||||||
}
|
}
|
||||||
|
return flipflopContext.TriggerData.Value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new FlipflopException("触发器节点构造了非预期的委托", true, FlipflopException.CancelClass.Flow);
|
throw new FlipflopException("触发器节点返回了非预期的类型", true, FlipflopException.CancelClass.Flow);
|
||||||
}
|
}
|
||||||
|
// Task<IFlipflopContext> flipflopTask;
|
||||||
|
//var delType = dd.EmitMethodType;
|
||||||
|
//var del = dd.EmitDelegate;
|
||||||
|
//if (delType == EmitHelper.EmitMethodType.HasResultTask && del is Func<object, object?[]?, Task<object>> hasResultTask)
|
||||||
|
//{
|
||||||
|
// var flipflopTaskObj = await hasResultTask(instance, args);
|
||||||
|
// if(flipflopTaskObj is IFlipflopContext flipflopContext)
|
||||||
|
// {
|
||||||
|
// NextOrientation = flipflopContext.State.ToContentType();
|
||||||
|
// if (flipflopContext.TriggerData is null || flipflopContext.TriggerData.Type == Library.NodeFlow.Tool.TriggerType.Overtime)
|
||||||
|
// {
|
||||||
|
// throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
||||||
|
// }
|
||||||
|
// return flipflopContext.TriggerData.Value;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// throw new FlipflopException("触发器节点返回了非预期的类型", true, FlipflopException.CancelClass.Flow);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// throw new FlipflopException("触发器节点构造了非预期的委托", true, FlipflopException.CancelClass.Flow);
|
||||||
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (FlipflopException ex)
|
catch (FlipflopException ex)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace Serein.NodeFlow.Tool;
|
namespace Serein.NodeFlow.Tool;
|
||||||
|
|
||||||
public static class MethodDetailsHelper
|
public static class NodeMethodDetailsHelper
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -57,7 +57,7 @@ public static class MethodDetailsHelper
|
|||||||
public static (MethodDetails?, DelegateDetails?) CreateMethodDetails(Type type, MethodInfo method, string assemblyName)
|
public static (MethodDetails?, DelegateDetails?) CreateMethodDetails(Type type, MethodInfo method, string assemblyName)
|
||||||
{
|
{
|
||||||
var attribute = method.GetCustomAttribute<NodeActionAttribute>();
|
var attribute = method.GetCustomAttribute<NodeActionAttribute>();
|
||||||
if(attribute is null)
|
if(attribute is null || attribute.Scan == false)
|
||||||
{
|
{
|
||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ namespace SereinFlowRemoteManagement
|
|||||||
{
|
{
|
||||||
environment.IOC.Register<WebSocketServer>();
|
environment.IOC.Register<WebSocketServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[NodeAction(NodeType.Loading)]
|
[NodeAction(NodeType.Loading)]
|
||||||
public void Loading(IDynamicContext context)
|
public void Loading(IDynamicContext context)
|
||||||
{
|
{
|
||||||
@@ -35,7 +36,10 @@ namespace SereinFlowRemoteManagement
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void GetAllNodeInfo()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user