mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-29 19:13:23 +08:00
重写了节点的view、viewmodel关系,实现了对画布元素的选取功能,重构了底层依赖,添加了对net .Framework4.6.1以上的Framework类库支持
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Serein.Library.Http
|
namespace Serein.Library.Core.Http
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示参数为url中的数据(Get请求中不需要显式标注)
|
/// 表示参数为url中的数据(Get请求中不需要显式标注)
|
||||||
@@ -96,6 +96,7 @@ namespace Serein.Library.Http
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsUrl = true;
|
public bool IsUrl = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*public sealed class WebApiAttribute(API http, bool isUrl = true, string url = "") : Attribute
|
/*public sealed class WebApiAttribute(API http, bool isUrl = true, string url = "") : Attribute
|
||||||
{
|
{
|
||||||
public API Http { get; } = http;
|
public API Http { get; } = http;
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
namespace Serein.Library.Http
|
namespace Serein.Library.Core.Http
|
||||||
{
|
{
|
||||||
public class ControllerBase
|
public class ControllerBase
|
||||||
{
|
{
|
||||||
|
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public string BobyData { get; set; }
|
public string BobyData { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public string GetLog(Exception ex)
|
public string GetLog(Exception ex)
|
||||||
{
|
{
|
||||||
return "Url : " + Url + Environment.NewLine +
|
return "Url : " + Url + Environment.NewLine +
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Serein.Library.IOC;
|
using Serein.Library.Api.Api;
|
||||||
|
using Serein.Library.Core.IOC;
|
||||||
using Serein.Tool;
|
using Serein.Tool;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -12,7 +12,7 @@ using System.Web;
|
|||||||
using Enum = System.Enum;
|
using Enum = System.Enum;
|
||||||
using Type = System.Type;
|
using Type = System.Type;
|
||||||
|
|
||||||
namespace Serein.Library.Http
|
namespace Serein.Library.Core.Http
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
@@ -28,11 +28,11 @@ namespace Serein.Library.Http
|
|||||||
private readonly ConcurrentDictionary<string, object> _controllerInstances; // 存储控制器实例对象
|
private readonly ConcurrentDictionary<string, object> _controllerInstances; // 存储控制器实例对象
|
||||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>> _routes; // 用于存储路由信息
|
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>> _routes; // 用于存储路由信息
|
||||||
|
|
||||||
private readonly IServiceContainer serviceRegistry; // 用于存储路由信息
|
private readonly ISereinIoc serviceRegistry; // 用于存储路由信息
|
||||||
|
|
||||||
//private Type PostRequest;
|
//private Type PostRequest;
|
||||||
|
|
||||||
public Router(IServiceContainer serviceRegistry) // 构造函数,初始化 Router 类的新实例
|
public Router(ISereinIoc serviceRegistry) // 构造函数,初始化 Router 类的新实例
|
||||||
{
|
{
|
||||||
this.serviceRegistry = serviceRegistry;
|
this.serviceRegistry = serviceRegistry;
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ namespace Serein.Library.Http
|
|||||||
/// 手动注册 实例持久控制器实例
|
/// 手动注册 实例持久控制器实例
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="controllerInstance"></param>
|
/// <param name="controllerInstance"></param>
|
||||||
public void RegisterController<T>(T controllerInstance) // 方法声明,用于动态注册路由
|
public void RegisterController<TController>(TController controllerInstance) where TController : ControllerBase // 方法声明,用于动态注册路由
|
||||||
{
|
{
|
||||||
if(controllerInstance == null) return;
|
if(controllerInstance == null) return;
|
||||||
Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型
|
Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型
|
||||||
@@ -286,6 +286,8 @@ namespace Serein.Library.Http
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解析路由,调用对应的方法
|
/// 解析路由,调用对应的方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using Serein.Library.IOC;
|
using Serein.Library.Api.Api;
|
||||||
|
using Serein.Library.Core.IOC;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.AccessControl;
|
|
||||||
|
|
||||||
namespace Serein.Library.Http
|
namespace Serein.Library.Core.Http
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -27,7 +27,7 @@ namespace Serein.Library.Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 启动服务器
|
// 启动服务器
|
||||||
public WebServer Start(string prefixe, IServiceContainer serviceContainer)
|
public WebServer Start(string prefixe, ISereinIoc serviceContainer)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -1,54 +1,23 @@
|
|||||||
using Serein.Library.Http;
|
using Serein.Library.Api;
|
||||||
using SqlSugar;
|
using Serein.Library.Attributes;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Serein.Library.IOC
|
namespace Serein.Library.Core.IOC
|
||||||
{
|
{
|
||||||
|
public class SereinIoc : ISereinIoc
|
||||||
|
|
||||||
public interface IServiceContainer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取或创建类型的实例(不注入依赖项)
|
|
||||||
/// </summary>
|
|
||||||
object GetOrCreateServiceInstance(Type serviceType, params object[] parameters);
|
|
||||||
T CreateServiceInstance<T>(params object[] parameters);
|
|
||||||
IServiceContainer Reset(); // 清空
|
|
||||||
IServiceContainer Register(Type type, params object[] parameters);
|
|
||||||
IServiceContainer Register<T>(params object[] parameters);
|
|
||||||
IServiceContainer Register<TService, TImplementation>(params object[] parameters) where TImplementation : TService;
|
|
||||||
T GetOrInstantiate<T>();
|
|
||||||
object GetOrInstantiate(Type type);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建目标类型的对象, 并注入依赖项
|
|
||||||
/// </summary>
|
|
||||||
object? Instantiate(Type type, params object[] parameters);
|
|
||||||
IServiceContainer Build();
|
|
||||||
IServiceContainer Run<T>(Action<T> action);
|
|
||||||
IServiceContainer Run<T1, T2>(Action<T1, T2> action);
|
|
||||||
IServiceContainer Run<T1, T2, T3>(Action<T1, T2, T3> action);
|
|
||||||
IServiceContainer Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action);
|
|
||||||
IServiceContainer Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action);
|
|
||||||
IServiceContainer Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action);
|
|
||||||
IServiceContainer Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action);
|
|
||||||
IServiceContainer Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ServiceContainer : IServiceContainer
|
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, object> _dependencies;
|
private readonly ConcurrentDictionary<string, object> _dependencies;
|
||||||
private readonly ConcurrentDictionary<string, Type> _typeMappings;
|
private readonly ConcurrentDictionary<string, Type> _typeMappings;
|
||||||
private readonly List<Type> _waitingForInstantiation;
|
private readonly List<Type> _waitingForInstantiation;
|
||||||
|
|
||||||
public ServiceContainer()
|
public SereinIoc()
|
||||||
{
|
{
|
||||||
|
|
||||||
_dependencies = new ConcurrentDictionary<string, object>
|
_dependencies = new ConcurrentDictionary<string, object>
|
||||||
{
|
{
|
||||||
[typeof(IServiceContainer).FullName] = this
|
[typeof(ISereinIoc).FullName] = this
|
||||||
};
|
};
|
||||||
|
|
||||||
_typeMappings = new ConcurrentDictionary<string, Type>();
|
_typeMappings = new ConcurrentDictionary<string, Type>();
|
||||||
@@ -75,14 +44,13 @@ namespace Serein.Library.IOC
|
|||||||
|
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
|
|
||||||
}
|
}
|
||||||
public T CreateServiceInstance<T>(params object[] parameters)
|
public T CreateServiceInstance<T>(params object[] parameters)
|
||||||
{
|
{
|
||||||
return (T)GetOrCreateServiceInstance(typeof(T), parameters);
|
return (T)GetOrCreateServiceInstance(typeof(T), parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Reset()
|
public ISereinIoc Reset()
|
||||||
{
|
{
|
||||||
foreach(var instancei in _dependencies.Values)
|
foreach(var instancei in _dependencies.Values)
|
||||||
{
|
{
|
||||||
@@ -97,7 +65,7 @@ namespace Serein.Library.IOC
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Register(Type type, params object[] parameters)
|
public ISereinIoc Register(Type type, params object[] parameters)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!_typeMappings.ContainsKey(type.FullName))
|
if (!_typeMappings.ContainsKey(type.FullName))
|
||||||
@@ -107,13 +75,13 @@ namespace Serein.Library.IOC
|
|||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public IServiceContainer Register<T>(params object[] parameters)
|
public ISereinIoc Register<T>(params object[] parameters)
|
||||||
{
|
{
|
||||||
Register(typeof(T), parameters);
|
Register(typeof(T), parameters);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Register<TService, TImplementation>(params object[] parameters)
|
public ISereinIoc Register<TService, TImplementation>(params object[] parameters)
|
||||||
where TImplementation : TService
|
where TImplementation : TService
|
||||||
{
|
{
|
||||||
_typeMappings[typeof(TService).FullName!] = typeof(TImplementation);
|
_typeMappings[typeof(TService).FullName!] = typeof(TImplementation);
|
||||||
@@ -152,7 +120,7 @@ namespace Serein.Library.IOC
|
|||||||
return (T)value;
|
return (T)value;
|
||||||
//throw new InvalidOperationException("目标类型未创建实例");
|
//throw new InvalidOperationException("目标类型未创建实例");
|
||||||
}
|
}
|
||||||
public IServiceContainer Build()
|
public ISereinIoc Build()
|
||||||
{
|
{
|
||||||
foreach (var type in _typeMappings.Values)
|
foreach (var type in _typeMappings.Values)
|
||||||
{
|
{
|
||||||
@@ -224,7 +192,7 @@ namespace Serein.Library.IOC
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region run()
|
#region run()
|
||||||
public IServiceContainer Run<T>(Action<T> action)
|
public ISereinIoc Run<T>(Action<T> action)
|
||||||
{
|
{
|
||||||
var service = GetOrInstantiate<T>();
|
var service = GetOrInstantiate<T>();
|
||||||
if (service != null)
|
if (service != null)
|
||||||
@@ -234,7 +202,7 @@ namespace Serein.Library.IOC
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Run<T1, T2>(Action<T1, T2> action)
|
public ISereinIoc Run<T1, T2>(Action<T1, T2> action)
|
||||||
{
|
{
|
||||||
var service1 = GetOrInstantiate<T1>();
|
var service1 = GetOrInstantiate<T1>();
|
||||||
var service2 = GetOrInstantiate<T2>();
|
var service2 = GetOrInstantiate<T2>();
|
||||||
@@ -243,7 +211,7 @@ namespace Serein.Library.IOC
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Run<T1, T2, T3>(Action<T1, T2, T3> action)
|
public ISereinIoc Run<T1, T2, T3>(Action<T1, T2, T3> action)
|
||||||
{
|
{
|
||||||
var service1 = GetOrInstantiate<T1>();
|
var service1 = GetOrInstantiate<T1>();
|
||||||
var service2 = GetOrInstantiate<T2>();
|
var service2 = GetOrInstantiate<T2>();
|
||||||
@@ -252,7 +220,7 @@ namespace Serein.Library.IOC
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
|
public ISereinIoc Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
|
||||||
{
|
{
|
||||||
var service1 = GetOrInstantiate<T1>();
|
var service1 = GetOrInstantiate<T1>();
|
||||||
var service2 = GetOrInstantiate<T2>();
|
var service2 = GetOrInstantiate<T2>();
|
||||||
@@ -262,7 +230,7 @@ namespace Serein.Library.IOC
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action)
|
public ISereinIoc Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action)
|
||||||
{
|
{
|
||||||
var service1 = GetOrInstantiate<T1>();
|
var service1 = GetOrInstantiate<T1>();
|
||||||
var service2 = GetOrInstantiate<T2>();
|
var service2 = GetOrInstantiate<T2>();
|
||||||
@@ -273,7 +241,7 @@ namespace Serein.Library.IOC
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action)
|
public ISereinIoc Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action)
|
||||||
{
|
{
|
||||||
var service1 = GetOrInstantiate<T1>();
|
var service1 = GetOrInstantiate<T1>();
|
||||||
var service2 = GetOrInstantiate<T2>();
|
var service2 = GetOrInstantiate<T2>();
|
||||||
@@ -285,7 +253,7 @@ namespace Serein.Library.IOC
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||||
{
|
{
|
||||||
var service1 = GetOrInstantiate<T1>();
|
var service1 = GetOrInstantiate<T1>();
|
||||||
var service2 = GetOrInstantiate<T2>();
|
var service2 = GetOrInstantiate<T2>();
|
||||||
@@ -298,7 +266,7 @@ namespace Serein.Library.IOC
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServiceContainer Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||||
{
|
{
|
||||||
var service1 = GetOrInstantiate<T1>();
|
var service1 = GetOrInstantiate<T1>();
|
||||||
var service2 = GetOrInstantiate<T2>();
|
var service2 = GetOrInstantiate<T2>();
|
||||||
35
Library.Core/NodeFlow/DynamicContext.cs
Normal file
35
Library.Core/NodeFlow/DynamicContext.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Utils;
|
||||||
|
|
||||||
|
namespace Serein.Library.Core.NodeFlow
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态流程上下文
|
||||||
|
/// </summary>
|
||||||
|
public class DynamicContext: IDynamicContext
|
||||||
|
{
|
||||||
|
public DynamicContext(ISereinIoc sereinIoc)
|
||||||
|
{
|
||||||
|
SereinIoc = sereinIoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeRunCts NodeRunCts { get; set; }
|
||||||
|
public ISereinIoc SereinIoc { get; }
|
||||||
|
public Task CreateTimingTask(Action action, int time = 100, int count = -1)
|
||||||
|
{
|
||||||
|
NodeRunCts ??= SereinIoc.GetOrInstantiate<NodeRunCts>();
|
||||||
|
return Task.Factory.StartNew(async () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
NodeRunCts.Token.ThrowIfCancellationRequested();
|
||||||
|
await Task.Delay(time);
|
||||||
|
action.Invoke();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
38
Library.Core/NodeFlow/DynamicNodeCoreType.cs
Normal file
38
Library.Core/NodeFlow/DynamicNodeCoreType.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.LibraryCore.NodeFlow
|
||||||
|
{
|
||||||
|
public enum DynamicNodeCoreType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化
|
||||||
|
/// </summary>
|
||||||
|
Init,
|
||||||
|
/// <summary>
|
||||||
|
/// 开始载入
|
||||||
|
/// </summary>
|
||||||
|
Loading,
|
||||||
|
/// <summary>
|
||||||
|
/// 结束
|
||||||
|
/// </summary>
|
||||||
|
Exit,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发器
|
||||||
|
/// </summary>
|
||||||
|
Flipflop,
|
||||||
|
/// <summary>
|
||||||
|
/// 条件节点
|
||||||
|
/// </summary>
|
||||||
|
Condition,
|
||||||
|
/// <summary>
|
||||||
|
/// 动作节点
|
||||||
|
/// </summary>
|
||||||
|
Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
85
Library.Core/NodeFlow/FlipflopContext.cs
Normal file
85
Library.Core/NodeFlow/FlipflopContext.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Enums;
|
||||||
|
|
||||||
|
namespace Serein.Library.Core.NodeFlow
|
||||||
|
{
|
||||||
|
public static class FlipflopFunc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 传入触发器方法的返回类型,尝试获取Task[Flipflop[]] 中的泛型类型
|
||||||
|
/// </summary>
|
||||||
|
//public static Type GetFlipflopInnerType(Type type)
|
||||||
|
//{
|
||||||
|
// // 检查是否为泛型类型且为 Task<>
|
||||||
|
// if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
||||||
|
// {
|
||||||
|
// // 获取 Task<> 的泛型参数类型,即 Flipflop<>
|
||||||
|
// var innerType = type.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
// // 检查泛型参数是否为 Flipflop<>
|
||||||
|
// if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||||
|
// {
|
||||||
|
// // 获取 Flipflop<> 的泛型参数类型,即 T
|
||||||
|
// var flipflopInnerType = innerType.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
// // 返回 Flipflop<> 中的具体类型
|
||||||
|
// return flipflopInnerType;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // 如果不符合条件,返回 null
|
||||||
|
// return null;
|
||||||
|
//}
|
||||||
|
|
||||||
|
public static bool IsTaskOfFlipflop(Type type)
|
||||||
|
{
|
||||||
|
// 检查是否为泛型类型且为 Task<>
|
||||||
|
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
||||||
|
{
|
||||||
|
// 获取 Task<> 的泛型参数类型
|
||||||
|
var innerType = type.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
// 判断 innerType 是否继承 IFlipflopContext
|
||||||
|
//if (typeof(IFlipflopContext).IsAssignableFrom(innerType))
|
||||||
|
//{
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// 检查泛型参数是否为 Flipflop<>
|
||||||
|
if (innerType == typeof(IFlipflopContext))
|
||||||
|
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发器上下文
|
||||||
|
/// </summary>
|
||||||
|
public class FlipflopContext : IFlipflopContext
|
||||||
|
{
|
||||||
|
public FlowStateType State { get; set; }
|
||||||
|
//public TResult? Data { get; set; }
|
||||||
|
public object Data { get; set; }
|
||||||
|
|
||||||
|
public FlipflopContext(FlowStateType ffState)
|
||||||
|
{
|
||||||
|
State = ffState;
|
||||||
|
}
|
||||||
|
public FlipflopContext(FlowStateType ffState, object data)
|
||||||
|
{
|
||||||
|
State = ffState;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
Library.Core/NodeFlow/FlowStateType.cs
Normal file
25
Library.Core/NodeFlow/FlowStateType.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Core.NodeFlow
|
||||||
|
{
|
||||||
|
|
||||||
|
//public enum FlowStateType
|
||||||
|
//{
|
||||||
|
// /// <summary>
|
||||||
|
// /// 成功(方法成功执行)
|
||||||
|
// /// </summary>
|
||||||
|
// Succeed,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误)
|
||||||
|
// /// </summary>
|
||||||
|
// Fail,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 异常(节点没有成功执行,执行时发生非预期的错误)
|
||||||
|
// /// </summary>
|
||||||
|
// Error,
|
||||||
|
//}
|
||||||
|
}
|
||||||
35
Library.Core/NodeFlow/Tool/Attribute.cs
Normal file
35
Library.Core/NodeFlow/Tool/Attribute.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Serein.Library.Api.Enums;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Core.NodeFlow.Tool
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用来判断一个类是否需要注册并构建实例(单例模式场景使用)
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class DynamicFlowAttribute(bool scan = true) : Attribute
|
||||||
|
{
|
||||||
|
public bool Scan { get; set; } = scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标记一个方法是什么类型,加载dll后用来拖拽到画布中
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||||
|
public class MethodDetailAttribute(DynamicNodeType methodDynamicType,
|
||||||
|
string methodTips = "",
|
||||||
|
bool scan = true,
|
||||||
|
string lockName = "") : Attribute
|
||||||
|
{
|
||||||
|
public bool Scan { get; set; } = scan;
|
||||||
|
public string MethodTips { get; } = methodTips;
|
||||||
|
public DynamicNodeType MethodDynamicType { get; } = methodDynamicType;
|
||||||
|
public string LockName { get; } = lockName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
202
Library.Core/NodeFlow/Tool/DynamicTool.cs
Normal file
202
Library.Core/NodeFlow/Tool/DynamicTool.cs
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing.Printing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Core.NodeFlow.Tool
|
||||||
|
{
|
||||||
|
|
||||||
|
#region 锁、tsk工具 (已注释)
|
||||||
|
/*public class LockManager
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, LockQueue> _locks = new ConcurrentDictionary<string, LockQueue>();
|
||||||
|
|
||||||
|
public void CreateLock(string name)
|
||||||
|
{
|
||||||
|
_locks.TryAdd(name, new LockQueue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AcquireLockAsync(string name, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (!_locks.ContainsKey(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var lockQueue = _locks[name];
|
||||||
|
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
lock (lockQueue.Queue)
|
||||||
|
{
|
||||||
|
lockQueue.Queue.Enqueue(tcs);
|
||||||
|
if (lockQueue.Queue.Count == 1)
|
||||||
|
{
|
||||||
|
tcs.SetResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await tcs.Task.ConfigureAwait(false);
|
||||||
|
|
||||||
|
// 处理取消操作
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
cancellationToken.Register(() =>
|
||||||
|
{
|
||||||
|
lock (lockQueue.Queue)
|
||||||
|
{
|
||||||
|
if (lockQueue.Queue.Contains(tcs))
|
||||||
|
{
|
||||||
|
tcs.TrySetCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReleaseLock(string name)
|
||||||
|
{
|
||||||
|
if (!_locks.ContainsKey(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var lockQueue = _locks[name];
|
||||||
|
|
||||||
|
lock (lockQueue.Queue)
|
||||||
|
{
|
||||||
|
if (lockQueue.Queue.Count > 0)
|
||||||
|
{
|
||||||
|
lockQueue.Queue.Dequeue();
|
||||||
|
|
||||||
|
if (lockQueue.Queue.Count > 0)
|
||||||
|
{
|
||||||
|
var next = lockQueue.Queue.Peek();
|
||||||
|
next.SetResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LockQueue
|
||||||
|
{
|
||||||
|
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface ITaskResult
|
||||||
|
{
|
||||||
|
object Result { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TaskResult<T> : ITaskResult
|
||||||
|
{
|
||||||
|
public TaskResult(T result)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Result { get; }
|
||||||
|
|
||||||
|
object ITaskResult.Result => Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DynamicTasks
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentDictionary<string, Task<ITaskResult>> TaskGuidPairs = new();
|
||||||
|
public static Task<ITaskResult> GetTask(string Guid)
|
||||||
|
{
|
||||||
|
TaskGuidPairs.TryGetValue(Guid, out Task<ITaskResult> task);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool AddTask<T>(string Guid, T result)
|
||||||
|
{
|
||||||
|
var task = Task.FromResult<ITaskResult>(new TaskResult<T>(result));
|
||||||
|
|
||||||
|
return TaskGuidPairs.TryAdd(Guid, task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class TaskNodeManager
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, TaskQueue> _taskQueues = new ConcurrentDictionary<string, TaskQueue>();
|
||||||
|
|
||||||
|
public void CreateTaskNode(string name)
|
||||||
|
{
|
||||||
|
_taskQueues.TryAdd(name, new TaskQueue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task WaitForTaskNodeAsync(string name, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (!_taskQueues.ContainsKey(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var taskQueue = _taskQueues[name];
|
||||||
|
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
lock (taskQueue.Queue)
|
||||||
|
{
|
||||||
|
taskQueue.Queue.Enqueue(tcs);
|
||||||
|
if (taskQueue.Queue.Count == 1)
|
||||||
|
{
|
||||||
|
tcs.SetResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await tcs.Task.ConfigureAwait(false);
|
||||||
|
|
||||||
|
// 处理取消操作
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
cancellationToken.Register(() =>
|
||||||
|
{
|
||||||
|
lock (taskQueue.Queue)
|
||||||
|
{
|
||||||
|
if (taskQueue.Queue.Contains(tcs))
|
||||||
|
{
|
||||||
|
tcs.TrySetCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CompleteTaskNode(string name)
|
||||||
|
{
|
||||||
|
if (!_taskQueues.ContainsKey(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var taskQueue = _taskQueues[name];
|
||||||
|
|
||||||
|
lock (taskQueue.Queue)
|
||||||
|
{
|
||||||
|
if (taskQueue.Queue.Count > 0)
|
||||||
|
{
|
||||||
|
taskQueue.Queue.Dequeue();
|
||||||
|
|
||||||
|
if (taskQueue.Queue.Count > 0)
|
||||||
|
{
|
||||||
|
var next = taskQueue.Queue.Peek();
|
||||||
|
next.SetResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TaskQueue
|
||||||
|
{
|
||||||
|
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
52
Library.Core/Serein.Library.Core.csproj
Normal file
52
Library.Core/Serein.Library.Core.csproj
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="DbSql\**" />
|
||||||
|
<Compile Remove="Flow\**" />
|
||||||
|
<Compile Remove="Http\**" />
|
||||||
|
<Compile Remove="IOC\**" />
|
||||||
|
<Compile Remove="NodeFlow\Tool\**" />
|
||||||
|
<Compile Remove="obj\**" />
|
||||||
|
<Compile Remove="SerinExpression\**" />
|
||||||
|
<Compile Remove="Tool\**" />
|
||||||
|
<EmbeddedResource Remove="DbSql\**" />
|
||||||
|
<EmbeddedResource Remove="Flow\**" />
|
||||||
|
<EmbeddedResource Remove="Http\**" />
|
||||||
|
<EmbeddedResource Remove="IOC\**" />
|
||||||
|
<EmbeddedResource Remove="NodeFlow\Tool\**" />
|
||||||
|
<EmbeddedResource Remove="obj\**" />
|
||||||
|
<EmbeddedResource Remove="SerinExpression\**" />
|
||||||
|
<EmbeddedResource Remove="Tool\**" />
|
||||||
|
<None Remove="DbSql\**" />
|
||||||
|
<None Remove="Flow\**" />
|
||||||
|
<None Remove="Http\**" />
|
||||||
|
<None Remove="IOC\**" />
|
||||||
|
<None Remove="NodeFlow\Tool\**" />
|
||||||
|
<None Remove="obj\**" />
|
||||||
|
<None Remove="SerinExpression\**" />
|
||||||
|
<None Remove="Tool\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="NodeFlow\DynamicNodeCoreType.cs" />
|
||||||
|
<Compile Remove="NodeFlow\FlowStateType.cs" />
|
||||||
|
<Compile Remove="ServiceContainer.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Serein.Library.SerinExpression
|
namespace Serein.LibraryCore.SerinExpression
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 条件解析抽象类
|
/// 条件解析抽象类
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Serein.Library.SerinExpression;
|
namespace Serein.LibraryCore.SerinExpression;
|
||||||
|
|
||||||
public class SerinConditionParser
|
public class SerinConditionParser
|
||||||
{
|
{
|
||||||
@@ -290,6 +290,7 @@ public class SerinConditionParser
|
|||||||
{
|
{
|
||||||
">" => ValueTypeConditionResolver<T>.Operator.GreaterThan,
|
">" => ValueTypeConditionResolver<T>.Operator.GreaterThan,
|
||||||
"<" => ValueTypeConditionResolver<T>.Operator.LessThan,
|
"<" => ValueTypeConditionResolver<T>.Operator.LessThan,
|
||||||
|
"=" => ValueTypeConditionResolver<T>.Operator.Equal,
|
||||||
"==" => ValueTypeConditionResolver<T>.Operator.Equal,
|
"==" => ValueTypeConditionResolver<T>.Operator.Equal,
|
||||||
">=" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
">=" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
||||||
"≥" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
"≥" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
|
|
||||||
namespace Serein.Library.SerinExpression
|
namespace Serein.LibraryCore.SerinExpression
|
||||||
{
|
{
|
||||||
public class SerinArithmeticExpressionEvaluator
|
public class SerinArithmeticExpressionEvaluator
|
||||||
{
|
{
|
||||||
@@ -25,7 +25,16 @@ namespace Serein.Library.SerinExpression
|
|||||||
|
|
||||||
public class SerinExpressionEvaluator
|
public class SerinExpressionEvaluator
|
||||||
{
|
{
|
||||||
public static object Evaluate(string expression, object targetObJ, out bool IsChange)
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expression">表达式</param>
|
||||||
|
/// <param name="targetObJ">操作对象</param>
|
||||||
|
/// <param name="isChange">是否改变了对象(get语法)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
|
/// <exception cref="NotSupportedException"></exception>
|
||||||
|
public static object Evaluate(string expression, object targetObJ, out bool isChange)
|
||||||
{
|
{
|
||||||
var parts = expression.Split([' '], 2);
|
var parts = expression.Split([' '], 2);
|
||||||
if (parts.Length != 2)
|
if (parts.Length != 2)
|
||||||
@@ -45,7 +54,7 @@ namespace Serein.Library.SerinExpression
|
|||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
||||||
};
|
};
|
||||||
|
|
||||||
IsChange = operation switch
|
isChange = operation switch
|
||||||
{
|
{
|
||||||
"@num" => true,
|
"@num" => true,
|
||||||
"@call" => true,
|
"@call" => true,
|
||||||
167
Library.Framework/Http/Attribute.cs
Normal file
167
Library.Framework/Http/Attribute.cs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
using Serein.Library.Framework.NodeFlow;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.Http
|
||||||
|
{
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 用来判断一个类是否需要注册并构建实例(单例模式场景使用)
|
||||||
|
///// </summary>
|
||||||
|
//[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
//public class DynamicFlowAttribute : Attribute
|
||||||
|
//{
|
||||||
|
// public DynamicFlowAttribute(bool scan = true)
|
||||||
|
// {
|
||||||
|
// Scan = scan;
|
||||||
|
// }
|
||||||
|
// public bool Scan { get; set; }
|
||||||
|
//}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 标记一个方法是什么类型,加载dll后用来拖拽到画布中
|
||||||
|
///// </summary>
|
||||||
|
//[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||||
|
//public class MethodDetailAttribute : Attribute
|
||||||
|
//{
|
||||||
|
|
||||||
|
// public MethodDetailAttribute(DynamicNodeType methodDynamicType,
|
||||||
|
// string methodTips = "",
|
||||||
|
// bool scan = true,
|
||||||
|
// string lockName = "")
|
||||||
|
// {
|
||||||
|
// Scan = scan;
|
||||||
|
// MethodDynamicType = methodDynamicType;
|
||||||
|
// MethodTips = methodTips;
|
||||||
|
// LockName = lockName;
|
||||||
|
// }
|
||||||
|
// public bool Scan { get; set; }
|
||||||
|
// public string MethodTips { get; }
|
||||||
|
// public DynamicNodeType MethodDynamicType { get; }
|
||||||
|
// public string LockName { get; }
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为显式参数
|
||||||
|
/// </summary>
|
||||||
|
//[AttributeUsage(AttributeTargets.Parameter)]
|
||||||
|
//public class ExplicitAttribute : Attribute // where TEnum : Enum
|
||||||
|
//{
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表示参数为url中的数据(Get请求中不需要显式标注)
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter)]
|
||||||
|
public sealed class IsUrlDataAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表示入参参数为整个boby的数据
|
||||||
|
/// <para>
|
||||||
|
/// 例如:User类型含有int id、string name字段</para>
|
||||||
|
/// <para>
|
||||||
|
/// ① Add(User user)</para>
|
||||||
|
/// <para>请求需要传入的json为
|
||||||
|
/// {"user":{
|
||||||
|
/// "id":2,
|
||||||
|
/// "name":"李志忠"}}</para>
|
||||||
|
/// <para>
|
||||||
|
/// ② Add([Boby]User user)</para>
|
||||||
|
/// <para>请求需要传入的json为
|
||||||
|
/// {"id":2,"name":"李志忠"}</para>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter)]
|
||||||
|
public sealed class IsBobyDataAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表示该控制器会被自动注册(与程序集同一命名空间,暂时不支持运行时自动加载DLL,需要手动注册)
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public sealed class AutoHostingAttribute : Attribute
|
||||||
|
{
|
||||||
|
public AutoHostingAttribute(string url = "")
|
||||||
|
{
|
||||||
|
Url = url;
|
||||||
|
}
|
||||||
|
public string Url { get; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 表示该属性为自动注入依赖项
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
public sealed class AutoInjectionAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方法的接口类型与附加URL
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 假设UserController.Add()的WebAPI特性中
|
||||||
|
/// http是HTTP.POST
|
||||||
|
/// url被显示标明“temp”
|
||||||
|
/// 那么请求的接口是POST,URL是
|
||||||
|
/// [http://localhost:8080]/user/add/temp
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="http"></param>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
|
||||||
|
public sealed class WebApiAttribute : Attribute
|
||||||
|
|
||||||
|
{
|
||||||
|
public API Type;
|
||||||
|
public string Url;
|
||||||
|
/// <summary>
|
||||||
|
/// 方法名称不作为url的部分
|
||||||
|
/// </summary>
|
||||||
|
public bool IsUrl;
|
||||||
|
}
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
|
||||||
|
public sealed class ApiPostAttribute : Attribute
|
||||||
|
|
||||||
|
{
|
||||||
|
public string Url;
|
||||||
|
/// <summary>
|
||||||
|
/// 方法名称不作为url的部分
|
||||||
|
/// </summary>
|
||||||
|
public bool IsUrl = true;
|
||||||
|
}
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
|
||||||
|
public sealed class ApiGetAttribute : Attribute
|
||||||
|
|
||||||
|
{
|
||||||
|
public string Url;
|
||||||
|
/// <summary>
|
||||||
|
/// 方法名称不作为url的部分
|
||||||
|
/// </summary>
|
||||||
|
public bool IsUrl = true;
|
||||||
|
}
|
||||||
|
/*public sealed class WebApiAttribute(API http, bool isUrl = true, string url = "") : Attribute
|
||||||
|
{
|
||||||
|
public API Http { get; } = http;
|
||||||
|
public string Url { get; } = url;
|
||||||
|
/// <summary>
|
||||||
|
/// 方法名称不作为url的部分
|
||||||
|
/// </summary>
|
||||||
|
public bool IsUrl { get; } = isUrl;
|
||||||
|
}*/
|
||||||
|
public enum API
|
||||||
|
{
|
||||||
|
POST,
|
||||||
|
GET,
|
||||||
|
//PUT,
|
||||||
|
//DELETE
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Library.Framework/Http/ControllerBase.cs
Normal file
19
Library.Framework/Http/ControllerBase.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.Http
|
||||||
|
{
|
||||||
|
public class ControllerBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
public string BobyData { get; set; }
|
||||||
|
|
||||||
|
public string GetLog(Exception ex)
|
||||||
|
{
|
||||||
|
return "Url : " + Url + Environment.NewLine +
|
||||||
|
"Ex : " + ex.Message + Environment.NewLine +
|
||||||
|
"Data : " + BobyData + Environment.NewLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
761
Library.Framework/Http/Router.cs
Normal file
761
Library.Framework/Http/Router.cs
Normal file
@@ -0,0 +1,761 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Serein.Library.Api.Api;
|
||||||
|
using Serein.Library.Framework.IOC;
|
||||||
|
using Serein.Tool;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Remoting.Messaging;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using Enum = System.Enum;
|
||||||
|
using Type = System.Type;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.Http
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 路由注册与解析
|
||||||
|
/// </summary>
|
||||||
|
public class Router
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, bool> _controllerAutoHosting; // 存储是否实例化
|
||||||
|
private readonly ConcurrentDictionary<string, Type> _controllerTypes; // 存储控制器类型
|
||||||
|
private readonly ConcurrentDictionary<string, object> _controllerInstances; // 存储控制器实例对象
|
||||||
|
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>> _routes; // 用于存储路由信息
|
||||||
|
|
||||||
|
private readonly ISereinIoc serviceRegistry; // 用于存储路由信息
|
||||||
|
|
||||||
|
//private Type PostRequest;
|
||||||
|
|
||||||
|
public Router(ISereinIoc serviceRegistry) // 构造函数,初始化 Router 类的新实例
|
||||||
|
{
|
||||||
|
this.serviceRegistry = serviceRegistry;
|
||||||
|
|
||||||
|
_routes = new ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>>(); // 初始化路由字典
|
||||||
|
|
||||||
|
_controllerAutoHosting = new ConcurrentDictionary<string, bool>(); // 初始化控制器实例对象字典
|
||||||
|
_controllerTypes = new ConcurrentDictionary<string, Type>(); // 初始化控制器实例对象字典
|
||||||
|
_controllerInstances = new ConcurrentDictionary<string, object>(); // 初始化控制器实例对象字典
|
||||||
|
|
||||||
|
foreach (API method in Enum.GetValues(typeof(API))) // 遍历 HTTP 枚举类型的所有值
|
||||||
|
{
|
||||||
|
_routes.TryAdd(method.ToString(), new ConcurrentDictionary<string, MethodInfo>()); // 初始化每种 HTTP 方法对应的路由字典
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前程序集
|
||||||
|
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||||
|
|
||||||
|
// 获取包含“Controller”名称的类型
|
||||||
|
var controllerTypes = assembly.GetTypes()
|
||||||
|
.Where(t => t.Name.Contains("Controller"));
|
||||||
|
|
||||||
|
Type baseAttribute = typeof(AutoHostingAttribute);
|
||||||
|
Type baseController = typeof(ControllerBase);
|
||||||
|
foreach (var controllerType in controllerTypes)
|
||||||
|
{
|
||||||
|
if (controllerType.IsSubclassOf(baseController) && controllerType.IsDefined(baseAttribute))
|
||||||
|
{
|
||||||
|
|
||||||
|
// 如果属于控制器,并标记了AutoHosting特性,进行自动注册
|
||||||
|
AutoRegisterAutoController(controllerType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动注册 自动实例化控制器 类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controllerType"></param>
|
||||||
|
public void AutoRegisterAutoController(Type controllerType) // 方法声明,用于注册并实例化控制器类型
|
||||||
|
{
|
||||||
|
if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回
|
||||||
|
|
||||||
|
var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
|
||||||
|
if (autoHostingAttribute != null) {
|
||||||
|
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
||||||
|
{
|
||||||
|
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
|
||||||
|
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
|
||||||
|
if( apiGetAttribute == null && apiPostAttribute == null )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
||||||
|
{
|
||||||
|
Type = apiGetAttribute != null ? API.GET : API.POST,
|
||||||
|
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
||||||
|
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (apiPostAttribute != null) // 如果存在 WebAPIAttribute 属性
|
||||||
|
{
|
||||||
|
var url = AddRoutesUrl(autoHostingAttribute,
|
||||||
|
webApiAttribute,
|
||||||
|
controllerType, method);
|
||||||
|
Console.WriteLine(url);
|
||||||
|
if (url == null) continue;
|
||||||
|
_controllerAutoHosting[url] = true;
|
||||||
|
_controllerTypes[url] = controllerType;
|
||||||
|
|
||||||
|
_controllerInstances[url] = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
|
||||||
|
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
|
||||||
|
{
|
||||||
|
var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
|
||||||
|
Console.WriteLine(url);
|
||||||
|
if (url == null) continue;
|
||||||
|
_controllerAutoHosting[url] = true;
|
||||||
|
_controllerTypes[url] = controllerType;
|
||||||
|
_controllerInstances[url] = null;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 手动注册 自动实例化控制器实例
|
||||||
|
/// </summary>
|
||||||
|
public void RegisterAutoController<T>() // 方法声明,用于动态注册路由
|
||||||
|
{
|
||||||
|
Type controllerType = typeof(T); // 获取控制器实例的类型
|
||||||
|
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
||||||
|
{
|
||||||
|
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
|
||||||
|
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
|
||||||
|
if (apiGetAttribute == null && apiPostAttribute == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
||||||
|
{
|
||||||
|
Type = apiGetAttribute != null ? API.GET : API.POST,
|
||||||
|
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
||||||
|
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
|
||||||
|
|
||||||
|
if (url == null) continue;
|
||||||
|
_controllerAutoHosting[url] = true;
|
||||||
|
_controllerTypes[url] = controllerType;
|
||||||
|
|
||||||
|
_controllerInstances[url] = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 手动注册 实例持久控制器实例
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controllerInstance"></param>
|
||||||
|
public void RegisterController<TController>(TController controllerInstance) where TController : ControllerBase // 方法声明,用于动态注册路由
|
||||||
|
{
|
||||||
|
if(controllerInstance == null) return;
|
||||||
|
Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型
|
||||||
|
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
||||||
|
{
|
||||||
|
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
|
||||||
|
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
|
||||||
|
if (apiGetAttribute == null && apiPostAttribute == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
||||||
|
{
|
||||||
|
Type = apiGetAttribute != null ? API.GET : API.POST,
|
||||||
|
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
||||||
|
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
|
||||||
|
|
||||||
|
if (url == null) continue;
|
||||||
|
_controllerInstances[url] = controllerInstance;
|
||||||
|
_controllerAutoHosting[url] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从方法中收集路由信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controllerType"></param>
|
||||||
|
public string AddRoutesUrl(AutoHostingAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method)
|
||||||
|
{
|
||||||
|
string controllerName;
|
||||||
|
if (autoHostingAttribute == null || string.IsNullOrWhiteSpace(autoHostingAttribute.Url))
|
||||||
|
{
|
||||||
|
controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
controllerName = autoHostingAttribute.Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpMethod = webAttribute.Type; // 获取 HTTP 方法
|
||||||
|
var customUrl = webAttribute.Url; // 获取自定义 URL
|
||||||
|
|
||||||
|
string url;
|
||||||
|
|
||||||
|
if (webAttribute.IsUrl)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
|
||||||
|
{
|
||||||
|
url = $"/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
customUrl = CleanUrl(customUrl);
|
||||||
|
url = $"/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||||
|
}
|
||||||
|
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
|
||||||
|
{
|
||||||
|
url = $"/{controllerName}".ToLower(); // 构建默认 URL
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
customUrl = CleanUrl(customUrl);
|
||||||
|
url = $"/{controllerName}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||||
|
}
|
||||||
|
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 收集路由信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controllerType"></param>
|
||||||
|
public void CollectRoutes(Type controllerType)
|
||||||
|
{
|
||||||
|
string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
|
||||||
|
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
||||||
|
{
|
||||||
|
var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
|
||||||
|
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
|
||||||
|
{
|
||||||
|
var customUrl = routeAttribute.Url; // 获取自定义 URL
|
||||||
|
string url;
|
||||||
|
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
|
||||||
|
{
|
||||||
|
url = $"/api/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
customUrl = CleanUrl(customUrl);
|
||||||
|
url = $"/api/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
||||||
|
}
|
||||||
|
var httpMethod = routeAttribute.Type; // 获取 HTTP 方法
|
||||||
|
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析路由,调用对应的方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> RouteAsync(HttpListenerContext context)
|
||||||
|
{
|
||||||
|
var request = context.Request; // 获取请求对象
|
||||||
|
var response = context.Response; // 获取响应对象
|
||||||
|
var url = request.Url; // 获取请求的 URL
|
||||||
|
var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法
|
||||||
|
|
||||||
|
var template = request.Url.AbsolutePath.ToLower();
|
||||||
|
|
||||||
|
|
||||||
|
if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
|
||||||
|
|
||||||
|
ControllerBase controllerInstance;
|
||||||
|
if (!_controllerAutoHosting[template])
|
||||||
|
{
|
||||||
|
controllerInstance = (ControllerBase)_controllerInstances[template];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
controllerInstance = (ControllerBase)serviceRegistry.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controllerInstance == null)
|
||||||
|
{
|
||||||
|
return false; // 未找到控制器实例
|
||||||
|
}
|
||||||
|
|
||||||
|
controllerInstance.Url = url.AbsolutePath;
|
||||||
|
object result;
|
||||||
|
switch (httpMethod) // 根据请求的 HTTP 方法执行不同的操作
|
||||||
|
{
|
||||||
|
case "GET": // 如果是 GET 请求,传入方法、控制器、url参数
|
||||||
|
result = InvokeControllerMethodWithRouteValues(method, controllerInstance, routeValues);
|
||||||
|
break;
|
||||||
|
case "POST": // POST 请求传入方法、控制器、请求体内容,url参数
|
||||||
|
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
|
||||||
|
controllerInstance.BobyData = requestBody;
|
||||||
|
var requestJObject = requestBody.FromJSON<object>();
|
||||||
|
|
||||||
|
result = InvokeControllerMethod(method, controllerInstance, requestJObject, routeValues);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
result = null;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Return(response, result); // 返回结果
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetLog(string Url, string BobyData = "")
|
||||||
|
{
|
||||||
|
return Environment.NewLine +
|
||||||
|
"Url : " + Url + Environment.NewLine +
|
||||||
|
"Data : " + BobyData + Environment.NewLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GET请求的控制器方法
|
||||||
|
/// </summary>
|
||||||
|
private object InvokeControllerMethodWithRouteValues(MethodInfo method, object controllerInstance, Dictionary<string, string> routeValues)
|
||||||
|
{
|
||||||
|
object[] parameters = GetMethodParameters(method, routeValues);
|
||||||
|
return InvokeMethod(method, controllerInstance, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Dictionary<MethodInfo, ParameterInfo[]> methodParameterCache = new Dictionary<MethodInfo, ParameterInfo[]>();
|
||||||
|
/// <summary>
|
||||||
|
/// POST请求的调用控制器方法
|
||||||
|
/// </summary>
|
||||||
|
public object InvokeControllerMethod(MethodInfo method, object controllerInstance, dynamic requestData, Dictionary<string, string> routeValues)
|
||||||
|
{
|
||||||
|
object[] cachedMethodParameters;
|
||||||
|
|
||||||
|
if (!methodParameterCache.TryGetValue(method, out ParameterInfo[] parameters))
|
||||||
|
{
|
||||||
|
parameters = method.GetParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedMethodParameters = new object[parameters.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
string paramName = parameters[i].Name;
|
||||||
|
bool isUrlData = parameters[i].GetCustomAttribute(typeof(IsUrlDataAttribute)) != null;
|
||||||
|
bool isBobyData = parameters[i].GetCustomAttribute(typeof(IsBobyDataAttribute)) != null;
|
||||||
|
|
||||||
|
if (isUrlData)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(paramName) && routeValues.TryGetValue(paramName, out string value))
|
||||||
|
{
|
||||||
|
cachedMethodParameters[i] = ConvertValue(value, parameters[i].ParameterType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cachedMethodParameters[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isBobyData)
|
||||||
|
{
|
||||||
|
cachedMethodParameters[i] = ConvertValue(requestData.ToString(), parameters[i].ParameterType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (requestData.ContainsKey(paramName))
|
||||||
|
{
|
||||||
|
if (parameters[i].ParameterType == typeof(string))
|
||||||
|
{
|
||||||
|
cachedMethodParameters[i] = requestData[paramName].ToString();
|
||||||
|
}
|
||||||
|
else if (parameters[i].ParameterType == typeof(bool))
|
||||||
|
{
|
||||||
|
cachedMethodParameters[i] = requestData[paramName?.ToLower()].ToBool();
|
||||||
|
}
|
||||||
|
else if (parameters[i].ParameterType == typeof(int))
|
||||||
|
{
|
||||||
|
cachedMethodParameters[i] = requestData[paramName].ToInt();
|
||||||
|
}
|
||||||
|
else if (parameters[i].ParameterType == typeof(double))
|
||||||
|
{
|
||||||
|
cachedMethodParameters[i] = requestData[paramName].ToDouble();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cachedMethodParameters[i] = ConvertValue(requestData[paramName], parameters[i].ParameterType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cachedMethodParameters[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存方法和参数的映射
|
||||||
|
//methodParameterCache[method] = cachedMethodParameters;
|
||||||
|
|
||||||
|
|
||||||
|
// 调用方法
|
||||||
|
|
||||||
|
return method.Invoke(controllerInstance, cachedMethodParameters);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查方法入参参数类型,返回对应的入参数组
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
/// <param name="routeValues"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private object[] GetMethodParameters(MethodInfo method, Dictionary<string, string> routeValues)
|
||||||
|
{
|
||||||
|
ParameterInfo[] methodParameters = method.GetParameters();
|
||||||
|
object[] parameters = new object[methodParameters.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < methodParameters.Length; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
string paramName = methodParameters[i].Name;
|
||||||
|
|
||||||
|
|
||||||
|
if (routeValues.TryGetValue(paramName, out string value))
|
||||||
|
{
|
||||||
|
parameters[i] = ConvertValue(value, methodParameters[i].ParameterType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
parameters[i] = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/// <summary>
|
||||||
|
/// 转为对应的类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="targetType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private object ConvertValue(object value, Type targetType)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject(value.ToString(), targetType);
|
||||||
|
}
|
||||||
|
catch (JsonReaderException ex)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
catch (JsonSerializationException ex)
|
||||||
|
{
|
||||||
|
// 如果无法转为对应的JSON对象
|
||||||
|
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
||||||
|
int endIndex = ex.Message.IndexOf("'", startIndex); // 查找类型信息结束的索引
|
||||||
|
var typeInfo = ex.Message.Substring(startIndex, endIndex - startIndex); // 提取出错类型信息,该怎么传出去?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch // (Exception ex)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
/// <summary>
|
||||||
|
/// 转为对应的类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="targetType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private object ConvertValue(string value, Type targetType)
|
||||||
|
{
|
||||||
|
if(targetType == typeof(string))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
return JsonConvert.DeserializeObject(value.ToString(), targetType);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (JsonReaderException ex)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
catch (JsonSerializationException ex)
|
||||||
|
{
|
||||||
|
// 如果无法转为对应的JSON对象
|
||||||
|
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
||||||
|
int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引
|
||||||
|
var typeInfo = ex.Message.Substring(startIndex, endIndex); // 提取出错类型信息,该怎么传出去?
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch // (Exception ex)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 调用控制器方法传入参数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">方法</param>
|
||||||
|
/// <param name="controllerInstance">控制器实例</param>
|
||||||
|
/// <param name="methodParameters">参数列表</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static object InvokeMethod(MethodInfo method, object controllerInstance, object[] methodParameters)
|
||||||
|
{
|
||||||
|
|
||||||
|
object result = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
result = method?.Invoke(controllerInstance, methodParameters);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ArgumentException ex)
|
||||||
|
{
|
||||||
|
string targetType = ExtractTargetTypeFromExceptionMessage(ex.Message);
|
||||||
|
|
||||||
|
// 如果方法调用失败
|
||||||
|
result = new
|
||||||
|
{
|
||||||
|
error = $"函数签名类型[{targetType}]不符合",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (JsonSerializationException ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
// 查找类型信息开始的索引
|
||||||
|
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length;
|
||||||
|
// 查找类型信息结束的索引
|
||||||
|
int endIndex = ex.Message.IndexOf('\'');
|
||||||
|
// 提取类型信息
|
||||||
|
string typeInfo = ex.Message.Substring(startIndex, endIndex);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result; // 调用方法并返回结果
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方法声明,用于解析 URL 获取路由参数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static Dictionary<string, string> GetUrlData(Uri uri)
|
||||||
|
{
|
||||||
|
Dictionary<string, string> routeValues = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
var pathParts = uri.ToString().Split('?'); // 拆分 URL,获取路径部分
|
||||||
|
|
||||||
|
if (pathParts.Length > 1) // 如果包含查询字符串
|
||||||
|
{
|
||||||
|
var queryParams = HttpUtility.ParseQueryString(pathParts[1]); // 解析查询字符串
|
||||||
|
|
||||||
|
foreach (string key in queryParams) // 遍历查询字符串的键值对
|
||||||
|
{
|
||||||
|
if (key == null) continue;
|
||||||
|
|
||||||
|
routeValues[key] = queryParams[key]; // 将键值对添加到路由参数字典中
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return routeValues; // 返回路由参数字典
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取Body中的消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static async Task<string> ReadRequestBodyAsync(HttpListenerRequest request)
|
||||||
|
{
|
||||||
|
using (Stream stream = request.InputStream)
|
||||||
|
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
return await reader.ReadToEndAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 返回响应消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="response"></param>
|
||||||
|
/// <param name="msg"></param>
|
||||||
|
private static void Return(HttpListenerResponse response, dynamic msg)
|
||||||
|
{
|
||||||
|
string resultData;
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (msg is IEnumerable && !(msg is string))
|
||||||
|
{
|
||||||
|
// If msg is a collection (e.g., array or list), serialize it as JArray
|
||||||
|
resultData = JArray.FromObject(msg).ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, serialize it as JObject
|
||||||
|
resultData = JObject.FromObject(msg).ToString();
|
||||||
|
}
|
||||||
|
byte[] buffer = Encoding.UTF8.GetBytes(resultData);
|
||||||
|
response.ContentLength64 = buffer.Length;
|
||||||
|
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If serialization fails, use the original message's string representation
|
||||||
|
resultData = msg.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析JSON
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="requestBody"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
private static dynamic ParseJson(string requestBody)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(requestBody))
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid JSON format");
|
||||||
|
}
|
||||||
|
return JObject.Parse(requestBody);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid JSON format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 修正方法特性中的URL格式
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string CleanUrl(string url)
|
||||||
|
{
|
||||||
|
|
||||||
|
while (url.Length > 0 && url[0] == '/') // 去除开头的斜杠
|
||||||
|
{
|
||||||
|
url = url.Substring(1,url.Length-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (url.Length > 0 && url[url.Length-1] == '/') // 去除末尾的斜杠
|
||||||
|
{
|
||||||
|
url = url.Substring(0,url.Length-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < url.Length - 1; i++) // 去除连续的斜杠
|
||||||
|
{
|
||||||
|
if (url[i] == '/' && url[i + 1] == '/')
|
||||||
|
{
|
||||||
|
url = url.Remove(i, 1);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url; // 返回清理后的 URL
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 从控制器调用方法的异常中获取出出错类型的信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errorMessage"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ExtractTargetTypeFromExceptionMessage(string errorMessage)
|
||||||
|
{
|
||||||
|
string targetText = "为类型“";
|
||||||
|
int startIndex = errorMessage.IndexOf(targetText);
|
||||||
|
if (startIndex != -1)
|
||||||
|
{
|
||||||
|
startIndex += targetText.Length;
|
||||||
|
int endIndex = errorMessage.IndexOf('\'');
|
||||||
|
if (endIndex != -1)
|
||||||
|
{
|
||||||
|
return errorMessage.Substring(startIndex, endIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
196
Library.Framework/Http/WebAPIAttribute.cs
Normal file
196
Library.Framework/Http/WebAPIAttribute.cs
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
using Serein.Library.Api.Api;
|
||||||
|
using Serein.Library.Framework.IOC;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.Http
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTTP接口监听类
|
||||||
|
/// </summary>
|
||||||
|
public class WebServer
|
||||||
|
{
|
||||||
|
private readonly HttpListener listener; // HTTP 监听器
|
||||||
|
private Router router; // 路由器
|
||||||
|
private readonly RequestLimiter requestLimiter; //接口防刷
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public WebServer()
|
||||||
|
|
||||||
|
{
|
||||||
|
listener = new HttpListener();
|
||||||
|
|
||||||
|
requestLimiter = new RequestLimiter(5, 8);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动服务器
|
||||||
|
public WebServer Start(string prefixe, ISereinIoc serviceContainer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
router = new Router(serviceContainer);
|
||||||
|
if (listener.IsListening)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prefixe.Substring(prefixe.Length - 1, 1).Equals(@"/"))
|
||||||
|
{
|
||||||
|
prefixe += @"/";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
listener.Prefixes.Add(prefixe); // 添加监听前缀
|
||||||
|
listener.Start(); // 开始监听
|
||||||
|
|
||||||
|
Console.WriteLine($"开始监听:{prefixe}");
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
while (listener.IsListening)
|
||||||
|
{
|
||||||
|
var context = await listener.GetContextAsync(); // 获取请求上下文
|
||||||
|
_ = Task.Run(() => ProcessRequestAsync(context)); // 处理请求)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
catch (HttpListenerException ex) when (ex.ErrorCode == 183)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理请求
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task ProcessRequestAsync(HttpListenerContext context)
|
||||||
|
{
|
||||||
|
// 添加CORS头部
|
||||||
|
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
|
||||||
|
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||||
|
context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
|
||||||
|
|
||||||
|
// 处理OPTIONS预检请求
|
||||||
|
if (context.Request.HttpMethod == "OPTIONS")
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||||
|
context.Response.Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isPass = await router.RouteAsync(context); // 路由解析
|
||||||
|
if (isPass)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||||
|
context.Response.Close(); // 关闭响应
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||||
|
context.Response.Close(); // 关闭响应
|
||||||
|
}
|
||||||
|
|
||||||
|
//var isPass = requestLimiter.AllowRequest(context.Request);
|
||||||
|
//if (isPass)
|
||||||
|
//{
|
||||||
|
// // 如果路由没有匹配,返回 404
|
||||||
|
// router.RouteAsync(context); // 路由解析
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
||||||
|
// context.Response.Close(); // 关闭响应
|
||||||
|
//}
|
||||||
|
|
||||||
|
// var request = context.Request;
|
||||||
|
// 获取远程终结点信息
|
||||||
|
//var remoteEndPoint = context.Request.RemoteEndPoint;
|
||||||
|
//// 获取用户的IP地址和端口
|
||||||
|
//IPAddress ipAddress = remoteEndPoint.Address;
|
||||||
|
//int port = remoteEndPoint.Port;
|
||||||
|
//Console.WriteLine("外部连接:" + ipAddress.ToString() + ":" + port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止服务器
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
if (listener.IsListening)
|
||||||
|
{
|
||||||
|
listener?.Stop(); // 停止监听
|
||||||
|
listener?.Close(); // 关闭监听器
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterAutoController<T>()
|
||||||
|
{
|
||||||
|
//var instance = Activator.CreateInstance(typeof(T));
|
||||||
|
router.RegisterAutoController<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public void RegisterRoute<T>(T controllerInstance)
|
||||||
|
{
|
||||||
|
router.RegisterRoute(controllerInstance);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 判断访问接口的频次是否正常
|
||||||
|
/// </summary>
|
||||||
|
public class RequestLimiter
|
||||||
|
{
|
||||||
|
public RequestLimiter(int seconds, int maxRequests)
|
||||||
|
{
|
||||||
|
interval = TimeSpan.FromSeconds(seconds);
|
||||||
|
maxRequests = maxRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, Queue<DateTime>> requestHistory = new ConcurrentDictionary<string, Queue<DateTime>>();
|
||||||
|
private readonly TimeSpan interval;
|
||||||
|
private readonly int maxRequests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断访问接口的频次是否正常
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool AllowRequest(HttpListenerRequest request)
|
||||||
|
{
|
||||||
|
var clientIp = request.RemoteEndPoint.Address.ToString();
|
||||||
|
var clientPort = request.RemoteEndPoint.Port;
|
||||||
|
var clientKey = clientIp + ":" + clientPort;
|
||||||
|
|
||||||
|
var now = DateTime.Now;
|
||||||
|
|
||||||
|
// 尝试从字典中获取请求队列,不存在则创建新的队列
|
||||||
|
var requests = requestHistory.GetOrAdd(clientKey, new Queue<DateTime>());
|
||||||
|
|
||||||
|
lock (requests)
|
||||||
|
{
|
||||||
|
// 移除超出时间间隔的请求记录
|
||||||
|
while (requests.Count > 0 && now - requests.Peek() > interval)
|
||||||
|
{
|
||||||
|
requests.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果请求数超过限制,拒绝请求
|
||||||
|
if (requests.Count >= maxRequests)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加当前请求时间,并允许请求
|
||||||
|
requests.Enqueue(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
378
Library.Framework/IOC/SereinIoc.cs
Normal file
378
Library.Framework/IOC/SereinIoc.cs
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Attributes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.IOC
|
||||||
|
{
|
||||||
|
|
||||||
|
public class SereinIoc : ISereinIoc
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, object> _dependencies;
|
||||||
|
private readonly ConcurrentDictionary<string, Type> _typeMappings;
|
||||||
|
private readonly List<Type> _waitingForInstantiation;
|
||||||
|
|
||||||
|
public SereinIoc()
|
||||||
|
{
|
||||||
|
|
||||||
|
_dependencies = new ConcurrentDictionary<string, object>
|
||||||
|
{
|
||||||
|
[typeof(ISereinIoc).FullName] = this
|
||||||
|
};
|
||||||
|
|
||||||
|
_typeMappings = new ConcurrentDictionary<string, Type>();
|
||||||
|
_waitingForInstantiation = new List<Type>();
|
||||||
|
}
|
||||||
|
public object GetOrCreateServiceInstance(Type type, params object[] parameters)
|
||||||
|
{
|
||||||
|
Register(type);
|
||||||
|
object instance;
|
||||||
|
|
||||||
|
if (_dependencies.ContainsKey(type.FullName))
|
||||||
|
{
|
||||||
|
instance = _dependencies[type.FullName];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
instance = Activator.CreateInstance(type);
|
||||||
|
|
||||||
|
|
||||||
|
_dependencies[type.FullName] = instance;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
|
||||||
|
}
|
||||||
|
public T CreateServiceInstance<T>(params object[] parameters)
|
||||||
|
{
|
||||||
|
return (T)GetOrCreateServiceInstance(typeof(T), parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Reset()
|
||||||
|
{
|
||||||
|
foreach(var instancei in _dependencies.Values)
|
||||||
|
{
|
||||||
|
if (typeof(IDisposable).IsAssignableFrom(instancei.GetType()) && instancei is IDisposable disposable)
|
||||||
|
{
|
||||||
|
disposable.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_dependencies.Clear();
|
||||||
|
_waitingForInstantiation.Clear();
|
||||||
|
//_typeMappings.Clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Register(Type type, params object[] parameters)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!_typeMappings.ContainsKey(type.FullName))
|
||||||
|
{
|
||||||
|
_typeMappings[type.FullName] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public ISereinIoc Register<T>(params object[] parameters)
|
||||||
|
{
|
||||||
|
Register(typeof(T), parameters);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Register<TService, TImplementation>(params object[] parameters)
|
||||||
|
where TImplementation : TService
|
||||||
|
{
|
||||||
|
_typeMappings[typeof(TService).FullName] = typeof(TImplementation);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetOrInstantiate(Type type)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (!_dependencies.TryGetValue(type.FullName, out object value))
|
||||||
|
{
|
||||||
|
Register(type);
|
||||||
|
|
||||||
|
value = Instantiate(type);
|
||||||
|
|
||||||
|
InjectDependencies(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public T GetOrInstantiate<T>()
|
||||||
|
{
|
||||||
|
if(!_dependencies.TryGetValue(typeof(T).FullName, out object value))
|
||||||
|
{
|
||||||
|
Register<T>();
|
||||||
|
|
||||||
|
value = Instantiate(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)value;
|
||||||
|
//throw new InvalidOperationException("目标类型未创建实例");
|
||||||
|
}
|
||||||
|
public ISereinIoc Build()
|
||||||
|
{
|
||||||
|
foreach (var type in _typeMappings.Values)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(!_dependencies.ContainsKey(type.FullName))
|
||||||
|
{
|
||||||
|
|
||||||
|
_dependencies[type.FullName] = Activator.CreateInstance(type);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var instance in _dependencies.Values)
|
||||||
|
{
|
||||||
|
|
||||||
|
InjectDependencies(instance); // 替换占位符
|
||||||
|
}
|
||||||
|
|
||||||
|
//var instance = Instantiate(item.Value);
|
||||||
|
|
||||||
|
TryInstantiateWaitingDependencies();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Instantiate(Type controllerType, params object[] parameters)
|
||||||
|
{
|
||||||
|
var instance = Activator.CreateInstance(controllerType, parameters);
|
||||||
|
if(instance != null)
|
||||||
|
{
|
||||||
|
InjectDependencies(instance);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InjectDependencies(object instance)
|
||||||
|
{
|
||||||
|
var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToArray()
|
||||||
|
.Where(p => p.CanWrite && p.GetCustomAttribute<AutoInjectionAttribute>() != null);
|
||||||
|
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
var propertyType = property.PropertyType;
|
||||||
|
|
||||||
|
if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
|
||||||
|
{
|
||||||
|
property.SetValue(instance, dependencyInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryInstantiateWaitingDependencies()
|
||||||
|
{
|
||||||
|
foreach (var waitingType in _waitingForInstantiation.ToList())
|
||||||
|
{
|
||||||
|
if (_typeMappings.TryGetValue(waitingType.FullName, out var implementationType))
|
||||||
|
{
|
||||||
|
var instance = Instantiate(implementationType);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
_dependencies[waitingType.FullName] = instance;
|
||||||
|
|
||||||
|
_waitingForInstantiation.Remove(waitingType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region run()
|
||||||
|
public ISereinIoc Run<T>(Action<T> action)
|
||||||
|
{
|
||||||
|
var service = GetOrInstantiate<T>();
|
||||||
|
if (service != null)
|
||||||
|
{
|
||||||
|
action(service);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2>(Action<T1, T2> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
|
||||||
|
action(service1, service2);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3>(Action<T1, T2, T3> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
action(service1, service2, service3);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
action(service1, service2, service3, service4);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
var service5 = GetOrInstantiate<T5>();
|
||||||
|
action(service1, service2, service3, service4, service5);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
var service5 = GetOrInstantiate<T5>();
|
||||||
|
var service6 = GetOrInstantiate<T6>();
|
||||||
|
action(service1, service2, service3, service4, service5, service6);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
var service5 = GetOrInstantiate<T5>();
|
||||||
|
var service6 = GetOrInstantiate<T6>();
|
||||||
|
var service7 = GetOrInstantiate<T7>();
|
||||||
|
action(service1, service2, service3, service4, service5, service6, service7);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
var service5 = GetOrInstantiate<T5>();
|
||||||
|
var service6 = GetOrInstantiate<T6>();
|
||||||
|
var service7 = GetOrInstantiate<T7>();
|
||||||
|
var service8 = GetOrInstantiate<T8>();
|
||||||
|
action(service1, service2, service3, service4, service5, service6, service7, service8);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* public interface IServiceContainer
|
||||||
|
{
|
||||||
|
ServiceContainer Register<T>(params object[] parameters);
|
||||||
|
ServiceContainer Register<TService, TImplementation>(params object[] parameters) where TImplementation : TService;
|
||||||
|
TService Resolve<TService>();
|
||||||
|
void Get<T>(Action<T> action);
|
||||||
|
object Instantiate(Type type, params object[] parameters);
|
||||||
|
|
||||||
|
}
|
||||||
|
public class ServiceContainer : IServiceContainer
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Type, object> _dependencies;
|
||||||
|
public ServiceContainer()
|
||||||
|
{
|
||||||
|
_dependencies = new Dictionary<Type, object>
|
||||||
|
{
|
||||||
|
[typeof(IServiceContainer)] = this
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Get<T>(Action<T> action)
|
||||||
|
{
|
||||||
|
var service = Resolve<T>();
|
||||||
|
action(service);
|
||||||
|
}
|
||||||
|
public ServiceContainer Register<T>(params object[] parameters)
|
||||||
|
{
|
||||||
|
var instance = Instantiate(typeof(T), parameters);
|
||||||
|
_dependencies[typeof(T)] = instance;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceContainer Register<TService, TImplementation>(params object[] parameters)
|
||||||
|
where TImplementation : TService
|
||||||
|
{
|
||||||
|
|
||||||
|
_dependencies[typeof(TService)] = Instantiate(typeof(TImplementation), parameters);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public TService Resolve<TService>()
|
||||||
|
{
|
||||||
|
return (TService)_dependencies[typeof(TService)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Instantiate(Type controllerType, params object[] parameters)
|
||||||
|
{
|
||||||
|
var constructors = controllerType.GetConstructors(); // 获取控制器的所有构造函数
|
||||||
|
|
||||||
|
// 查找具有最多参数的构造函数
|
||||||
|
var constructor = constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
|
||||||
|
|
||||||
|
if (constructor != null)
|
||||||
|
{
|
||||||
|
if (parameters.Length > 0)
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance(controllerType, parameters);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var tmpParameters = constructor.GetParameters();
|
||||||
|
var dependencyInstances = new List<object>();
|
||||||
|
|
||||||
|
foreach (var parameter in tmpParameters)
|
||||||
|
{
|
||||||
|
var parameterType = parameter.ParameterType;
|
||||||
|
_dependencies.TryGetValue(parameterType, out var dependencyInstance);
|
||||||
|
dependencyInstances.Add(dependencyInstance);
|
||||||
|
if (dependencyInstance == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 用解析的依赖项实例化目标类型
|
||||||
|
return Activator.CreateInstance(controllerType, dependencyInstances.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance(controllerType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
13
Library.Framework/NodeFlow/Api.cs
Normal file
13
Library.Framework/NodeFlow/Api.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.NodeFlow
|
||||||
|
{
|
||||||
|
public interface IDynamicFlowNode
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
40
Library.Framework/NodeFlow/DynamicContext.cs
Normal file
40
Library.Framework/NodeFlow/DynamicContext.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.NodeFlow
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态流程上下文
|
||||||
|
/// </summary>
|
||||||
|
public class DynamicContext : IDynamicContext
|
||||||
|
{
|
||||||
|
public DynamicContext(ISereinIoc sereinIoc)
|
||||||
|
{
|
||||||
|
SereinIoc = sereinIoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeRunCts NodeRunCts { get; set; }
|
||||||
|
public ISereinIoc SereinIoc { get; }
|
||||||
|
public Task CreateTimingTask(Action action, int time = 100, int count = -1)
|
||||||
|
{
|
||||||
|
if(NodeRunCts == null)
|
||||||
|
{
|
||||||
|
|
||||||
|
NodeRunCts = SereinIoc.GetOrInstantiate<NodeRunCts>();
|
||||||
|
}
|
||||||
|
return Task.Factory.StartNew(async () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
NodeRunCts.Token.ThrowIfCancellationRequested();
|
||||||
|
await Task.Delay(time);
|
||||||
|
action.Invoke();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Library.Framework/NodeFlow/DynamicNodeFrameworkType.cs
Normal file
38
Library.Framework/NodeFlow/DynamicNodeFrameworkType.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.LibraryFramework.NodeFlow
|
||||||
|
{
|
||||||
|
public enum DynamicNodeFrameworkType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化
|
||||||
|
/// </summary>
|
||||||
|
Init,
|
||||||
|
/// <summary>
|
||||||
|
/// 开始载入
|
||||||
|
/// </summary>
|
||||||
|
Loading,
|
||||||
|
/// <summary>
|
||||||
|
/// 结束
|
||||||
|
/// </summary>
|
||||||
|
Exit,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发器
|
||||||
|
/// </summary>
|
||||||
|
Flipflop,
|
||||||
|
/// <summary>
|
||||||
|
/// 条件节点
|
||||||
|
/// </summary>
|
||||||
|
Condition,
|
||||||
|
/// <summary>
|
||||||
|
/// 动作节点
|
||||||
|
/// </summary>
|
||||||
|
Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
94
Library.Framework/NodeFlow/FlipflopContext.cs
Normal file
94
Library.Framework/NodeFlow/FlipflopContext.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Enums;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.NodeFlow
|
||||||
|
{
|
||||||
|
//public enum FfState
|
||||||
|
//{
|
||||||
|
// Succeed,
|
||||||
|
// Cancel,
|
||||||
|
// Error,
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public class FlipflopContext
|
||||||
|
//{
|
||||||
|
// public FlowStateType State { get; set; }
|
||||||
|
// public object? Data { get; set; }
|
||||||
|
// public FlipflopContext(FlowStateType ffState, object? data = null)
|
||||||
|
// {
|
||||||
|
// State = ffState;
|
||||||
|
// Data = data;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public static class FlipflopFunc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 传入触发器方法的返回类型,尝试获取Task[Flipflop[]] 中的泛型类型
|
||||||
|
/// </summary>
|
||||||
|
//public static Type GetFlipflopInnerType(Type type)
|
||||||
|
//{
|
||||||
|
// // 检查是否为泛型类型且为 Task<>
|
||||||
|
// if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
||||||
|
// {
|
||||||
|
// // 获取 Task<> 的泛型参数类型,即 Flipflop<>
|
||||||
|
// var innerType = type.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
// // 检查泛型参数是否为 Flipflop<>
|
||||||
|
// if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||||
|
// {
|
||||||
|
// // 获取 Flipflop<> 的泛型参数类型,即 T
|
||||||
|
// var flipflopInnerType = innerType.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
// // 返回 Flipflop<> 中的具体类型
|
||||||
|
// return flipflopInnerType;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // 如果不符合条件,返回 null
|
||||||
|
// return null;
|
||||||
|
//}
|
||||||
|
|
||||||
|
public static bool IsTaskOfFlipflop(Type type)
|
||||||
|
{
|
||||||
|
// 检查是否为泛型类型且为 Task<>
|
||||||
|
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
||||||
|
{
|
||||||
|
// 获取 Task<> 的泛型参数类型
|
||||||
|
var innerType = type.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
// 检查泛型参数是否为 Flipflop<>
|
||||||
|
if (innerType == typeof(FlipflopContext))
|
||||||
|
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发器上下文
|
||||||
|
/// </summary>
|
||||||
|
public class FlipflopContext : IFlipflopContext
|
||||||
|
{
|
||||||
|
public FlowStateType State { get; set; }
|
||||||
|
//public TResult? Data { get; set; }
|
||||||
|
public object Data { get; set; }
|
||||||
|
public FlipflopContext(FlowStateType ffState)
|
||||||
|
{
|
||||||
|
State = ffState;
|
||||||
|
}
|
||||||
|
public FlipflopContext(FlowStateType ffState, object data)
|
||||||
|
{
|
||||||
|
State = ffState;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
Library.Framework/NodeFlow/FlowStateType.cs
Normal file
25
Library.Framework/NodeFlow/FlowStateType.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.NodeFlow
|
||||||
|
{
|
||||||
|
|
||||||
|
//public enum FlowStateType
|
||||||
|
//{
|
||||||
|
// /// <summary>
|
||||||
|
// /// 成功(方法成功执行)
|
||||||
|
// /// </summary>
|
||||||
|
// Succeed,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误)
|
||||||
|
// /// </summary>
|
||||||
|
// Fail,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 异常(节点没有成功执行,执行时发生非预期的错误)
|
||||||
|
// /// </summary>
|
||||||
|
// Error,
|
||||||
|
//}
|
||||||
|
}
|
||||||
8
Library.Framework/NodeFlow/Tool/Attribute.cs
Normal file
8
Library.Framework/NodeFlow/Tool/Attribute.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Serein.Library.Api.Enums;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.NodeFlow.Tool
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
194
Library.Framework/NodeFlow/Tool/DynamicTool.cs
Normal file
194
Library.Framework/NodeFlow/Tool/DynamicTool.cs
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
namespace Serein.Library.Framework.NodeFlow.Tool
|
||||||
|
{
|
||||||
|
|
||||||
|
#region 锁、tsk工具 (已注释)
|
||||||
|
/*public class LockManager
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, LockQueue> _locks = new ConcurrentDictionary<string, LockQueue>();
|
||||||
|
|
||||||
|
public void CreateLock(string name)
|
||||||
|
{
|
||||||
|
_locks.TryAdd(name, new LockQueue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AcquireLockAsync(string name, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (!_locks.ContainsKey(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var lockQueue = _locks[name];
|
||||||
|
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
lock (lockQueue.Queue)
|
||||||
|
{
|
||||||
|
lockQueue.Queue.Enqueue(tcs);
|
||||||
|
if (lockQueue.Queue.Count == 1)
|
||||||
|
{
|
||||||
|
tcs.SetResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await tcs.Task.ConfigureAwait(false);
|
||||||
|
|
||||||
|
// 处理取消操作
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
cancellationToken.Register(() =>
|
||||||
|
{
|
||||||
|
lock (lockQueue.Queue)
|
||||||
|
{
|
||||||
|
if (lockQueue.Queue.Contains(tcs))
|
||||||
|
{
|
||||||
|
tcs.TrySetCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReleaseLock(string name)
|
||||||
|
{
|
||||||
|
if (!_locks.ContainsKey(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var lockQueue = _locks[name];
|
||||||
|
|
||||||
|
lock (lockQueue.Queue)
|
||||||
|
{
|
||||||
|
if (lockQueue.Queue.Count > 0)
|
||||||
|
{
|
||||||
|
lockQueue.Queue.Dequeue();
|
||||||
|
|
||||||
|
if (lockQueue.Queue.Count > 0)
|
||||||
|
{
|
||||||
|
var next = lockQueue.Queue.Peek();
|
||||||
|
next.SetResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LockQueue
|
||||||
|
{
|
||||||
|
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface ITaskResult
|
||||||
|
{
|
||||||
|
object Result { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TaskResult<T> : ITaskResult
|
||||||
|
{
|
||||||
|
public TaskResult(T result)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Result { get; }
|
||||||
|
|
||||||
|
object ITaskResult.Result => Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DynamicTasks
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentDictionary<string, Task<ITaskResult>> TaskGuidPairs = new();
|
||||||
|
public static Task<ITaskResult> GetTask(string Guid)
|
||||||
|
{
|
||||||
|
TaskGuidPairs.TryGetValue(Guid, out Task<ITaskResult> task);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool AddTask<T>(string Guid, T result)
|
||||||
|
{
|
||||||
|
var task = Task.FromResult<ITaskResult>(new TaskResult<T>(result));
|
||||||
|
|
||||||
|
return TaskGuidPairs.TryAdd(Guid, task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class TaskNodeManager
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, TaskQueue> _taskQueues = new ConcurrentDictionary<string, TaskQueue>();
|
||||||
|
|
||||||
|
public void CreateTaskNode(string name)
|
||||||
|
{
|
||||||
|
_taskQueues.TryAdd(name, new TaskQueue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task WaitForTaskNodeAsync(string name, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (!_taskQueues.ContainsKey(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var taskQueue = _taskQueues[name];
|
||||||
|
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
lock (taskQueue.Queue)
|
||||||
|
{
|
||||||
|
taskQueue.Queue.Enqueue(tcs);
|
||||||
|
if (taskQueue.Queue.Count == 1)
|
||||||
|
{
|
||||||
|
tcs.SetResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await tcs.Task.ConfigureAwait(false);
|
||||||
|
|
||||||
|
// 处理取消操作
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
cancellationToken.Register(() =>
|
||||||
|
{
|
||||||
|
lock (taskQueue.Queue)
|
||||||
|
{
|
||||||
|
if (taskQueue.Queue.Contains(tcs))
|
||||||
|
{
|
||||||
|
tcs.TrySetCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CompleteTaskNode(string name)
|
||||||
|
{
|
||||||
|
if (!_taskQueues.ContainsKey(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var taskQueue = _taskQueues[name];
|
||||||
|
|
||||||
|
lock (taskQueue.Queue)
|
||||||
|
{
|
||||||
|
if (taskQueue.Queue.Count > 0)
|
||||||
|
{
|
||||||
|
taskQueue.Queue.Dequeue();
|
||||||
|
|
||||||
|
if (taskQueue.Queue.Count > 0)
|
||||||
|
{
|
||||||
|
var next = taskQueue.Queue.Peek();
|
||||||
|
next.SetResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TaskQueue
|
||||||
|
{
|
||||||
|
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
65
Library.Framework/NodeFlow/Tool/TcsSignal.cs
Normal file
65
Library.Framework/NodeFlow/Tool/TcsSignal.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using Serein.Library.Enums;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Framework.NodeFlow.Tool
|
||||||
|
{
|
||||||
|
public class TcsSignalException : Exception
|
||||||
|
{
|
||||||
|
public FlowStateType FsState { get; set; }
|
||||||
|
public TcsSignalException(string message) : base(message)
|
||||||
|
{
|
||||||
|
FsState = FlowStateType.Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TcsSignal<TSignal> where TSignal : struct, Enum
|
||||||
|
{
|
||||||
|
//public ConcurrentDictionary<TSignal, Queue<TaskCompletionSource<object>>> TcsEvent { get; } = new();
|
||||||
|
public ConcurrentDictionary<TSignal, TaskCompletionSource<object>> TcsEvent { get; } = new ConcurrentDictionary<TSignal, TaskCompletionSource<object>>();
|
||||||
|
|
||||||
|
public ConcurrentDictionary<TSignal, object> TcsLock { get; } = new ConcurrentDictionary<TSignal, object>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发信号
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="signal">信号</param>
|
||||||
|
/// <param name="value">传递的参数</param>
|
||||||
|
/// <returns>是否成功触发</returns>
|
||||||
|
public bool TriggerSignal<T>(TSignal signal, T value)
|
||||||
|
{
|
||||||
|
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
||||||
|
lock (tcsLock)
|
||||||
|
{
|
||||||
|
if (TcsEvent.TryRemove(signal, out var waitTcs))
|
||||||
|
{
|
||||||
|
waitTcs.SetResult(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskCompletionSource<object> CreateTcs(TSignal signal)
|
||||||
|
{
|
||||||
|
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
||||||
|
lock (tcsLock)
|
||||||
|
{
|
||||||
|
var tcs = TcsEvent.GetOrAdd(signal, new TaskCompletionSource<object>());
|
||||||
|
return tcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelTask()
|
||||||
|
{
|
||||||
|
foreach (var tcs in TcsEvent.Values)
|
||||||
|
{
|
||||||
|
tcs.SetException(new TcsSignalException("任务取消"));
|
||||||
|
}
|
||||||
|
TcsEvent.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Library.Framework/Properties/AssemblyInfo.cs
Normal file
36
Library.Framework/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// 有关程序集的一般信息由以下
|
||||||
|
// 控制。更改这些特性值可修改
|
||||||
|
// 与程序集关联的信息。
|
||||||
|
[assembly: AssemblyTitle("Serein.Library.Framework")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Serein.Library.Framework")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2024")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// 将 ComVisible 设置为 false 会使此程序集中的类型
|
||||||
|
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
|
||||||
|
//请将此类型的 ComVisible 特性设置为 true。
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
||||||
|
[assembly: Guid("73b272e8-222d-4d08-a030-f1e1db70b9d1")]
|
||||||
|
|
||||||
|
// 程序集的版本信息由下列四个值组成:
|
||||||
|
//
|
||||||
|
// 主版本
|
||||||
|
// 次版本
|
||||||
|
// 生成号
|
||||||
|
// 修订号
|
||||||
|
//
|
||||||
|
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
|
||||||
|
//通过使用 "*",如下所示:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
63
Library.Framework/Serein.Library.Framework.csproj
Normal file
63
Library.Framework/Serein.Library.Framework.csproj
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{73B272E8-222D-4D08-A030-F1E1DB70B9D1}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Serein.Library.Framework</RootNamespace>
|
||||||
|
<AssemblyName>Serein.Library.Framework</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="NodeFlow\FlipflopContext.cs" />
|
||||||
|
<Compile Include="NodeFlow\DynamicContext.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Library\Serein.Library.csproj">
|
||||||
|
<Project>{5e19d0f2-913a-4d1c-a6f8-1e1227baa0e3}</Project>
|
||||||
|
<Name>Serein.Library</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
186
Library.Framework/Tool/DataHelper.cs
Normal file
186
Library.Framework/Tool/DataHelper.cs
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Tool
|
||||||
|
{
|
||||||
|
public static class DataHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 把Object转换为Json字符串
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ToJson(this object obj)
|
||||||
|
{
|
||||||
|
IsoDateTimeConverter val = new IsoDateTimeConverter();
|
||||||
|
val.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
IsoDateTimeConverter val2 = val;
|
||||||
|
return JsonConvert.SerializeObject(obj, (JsonConverter[])(object)new JsonConverter[1] { (JsonConverter)val2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 把Json文本转为实体
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T FromJSON<T>(this string input)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (typeof(T).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.DeserializeObject<T>(input);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
// return default(T);
|
||||||
|
|
||||||
|
return default;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<T> IListToList<T>(IList list)
|
||||||
|
{
|
||||||
|
T[] array = new T[list.Count];
|
||||||
|
list.CopyTo(array, 0);
|
||||||
|
return new List<T>(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DataTable GetNewDataTable(DataTable dt, string condition)
|
||||||
|
{
|
||||||
|
if (!IsExistRows(dt))
|
||||||
|
{
|
||||||
|
if (condition.Trim() == "")
|
||||||
|
{
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTable dataTable = new DataTable();
|
||||||
|
dataTable = dt.Clone();
|
||||||
|
DataRow[] array = dt.Select(condition);
|
||||||
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
dataTable.ImportRow(array[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsExistRows(DataTable dt)
|
||||||
|
{
|
||||||
|
if (dt != null && dt.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Hashtable DataTableToHashtable(DataTable dt)
|
||||||
|
{
|
||||||
|
Hashtable hashtable = new Hashtable();
|
||||||
|
foreach (DataRow row in dt.Rows)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < dt.Columns.Count; i++)
|
||||||
|
{
|
||||||
|
string columnName = dt.Columns[i].ColumnName;
|
||||||
|
hashtable[columnName] = row[columnName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashtable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DataTable ListToDataTable<T>(List<T> entitys)
|
||||||
|
{
|
||||||
|
if (entitys == null || entitys.Count < 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Type type = entitys[0].GetType();
|
||||||
|
|
||||||
|
PropertyInfo[] properties = type.GetProperties();
|
||||||
|
DataTable dataTable = new DataTable();
|
||||||
|
for (int i = 0; i < properties.Length; i++)
|
||||||
|
{
|
||||||
|
dataTable.Columns.Add(properties[i].Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (T entity in entitys)
|
||||||
|
{
|
||||||
|
|
||||||
|
object obj = entity;
|
||||||
|
|
||||||
|
|
||||||
|
if (obj.GetType() != type)
|
||||||
|
{
|
||||||
|
throw new Exception("要转换的集合元素类型不一致");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object[] array = new object[properties.Length];
|
||||||
|
for (int j = 0; j < properties.Length; j++)
|
||||||
|
{
|
||||||
|
|
||||||
|
array[j] = properties[j].GetValue(obj, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dataTable.Rows.Add(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DataTableToXML(DataTable dt)
|
||||||
|
{
|
||||||
|
if (dt != null && dt.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
dt.WriteXml((TextWriter)stringWriter);
|
||||||
|
return stringWriter.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DataSetToXML(DataSet ds)
|
||||||
|
{
|
||||||
|
if (ds != null)
|
||||||
|
{
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
ds.WriteXml((TextWriter)stringWriter);
|
||||||
|
return stringWriter.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
Library.Framework/packages.config
Normal file
4
Library.Framework/packages.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net45" />
|
||||||
|
</packages>
|
||||||
13
Library/Api/IDynamicContext.cs
Normal file
13
Library/Api/IDynamicContext.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Serein.Library.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Api
|
||||||
|
{
|
||||||
|
public interface IDynamicContext
|
||||||
|
{
|
||||||
|
NodeRunCts NodeRunCts { get; set; }
|
||||||
|
ISereinIoc SereinIoc { get; }
|
||||||
|
Task CreateTimingTask(Action action, int time = 100, int count = -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Library/Api/IFlipflopContext.cs
Normal file
10
Library/Api/IFlipflopContext.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Serein.Library.Enums;
|
||||||
|
|
||||||
|
namespace Serein.Library.Api
|
||||||
|
{
|
||||||
|
public interface IFlipflopContext
|
||||||
|
{
|
||||||
|
FlowStateType State { get; set; }
|
||||||
|
object Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Library/Api/ISereinIoc.cs
Normal file
36
Library/Api/ISereinIoc.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Serein.Library.Api
|
||||||
|
{
|
||||||
|
public interface ISereinIoc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或创建类型的实例(不注入依赖项)
|
||||||
|
/// </summary>
|
||||||
|
object GetOrCreateServiceInstance(Type serviceType, params object[] parameters);
|
||||||
|
T CreateServiceInstance<T>(params object[] parameters);
|
||||||
|
ISereinIoc Reset(); // 清空
|
||||||
|
ISereinIoc Register(Type type, params object[] parameters);
|
||||||
|
ISereinIoc Register<T>(params object[] parameters);
|
||||||
|
ISereinIoc Register<TService, TImplementation>(params object[] parameters) where TImplementation : TService;
|
||||||
|
T GetOrInstantiate<T>();
|
||||||
|
object GetOrInstantiate(Type type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建目标类型的对象, 并注入依赖项
|
||||||
|
/// </summary>
|
||||||
|
object Instantiate(Type type, params object[] parameters);
|
||||||
|
ISereinIoc Build();
|
||||||
|
ISereinIoc Run<T>(Action<T> action);
|
||||||
|
ISereinIoc Run<T1, T2>(Action<T1, T2> action);
|
||||||
|
ISereinIoc Run<T1, T2, T3>(Action<T1, T2, T3> action);
|
||||||
|
ISereinIoc Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action);
|
||||||
|
ISereinIoc Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action);
|
||||||
|
ISereinIoc Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action);
|
||||||
|
ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action);
|
||||||
|
ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
Library/Enums/FlowStateType.cs
Normal file
25
Library/Enums/FlowStateType.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Enums
|
||||||
|
{
|
||||||
|
|
||||||
|
public enum FlowStateType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 成功(方法成功执行)
|
||||||
|
/// </summary>
|
||||||
|
Succeed,
|
||||||
|
/// <summary>
|
||||||
|
/// 失败(方法没有成功执行,不过执行时没有发生非预期的错误)
|
||||||
|
/// </summary>
|
||||||
|
Fail,
|
||||||
|
/// <summary>
|
||||||
|
/// 异常(节点没有成功执行,执行时发生非预期的错误)
|
||||||
|
/// </summary>
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Library/Enums/NodeType.cs
Normal file
38
Library/Enums/NodeType.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Enums
|
||||||
|
{
|
||||||
|
public enum NodeType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化
|
||||||
|
/// </summary>
|
||||||
|
Init,
|
||||||
|
/// <summary>
|
||||||
|
/// 开始载入
|
||||||
|
/// </summary>
|
||||||
|
Loading,
|
||||||
|
/// <summary>
|
||||||
|
/// 结束
|
||||||
|
/// </summary>
|
||||||
|
Exit,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发器
|
||||||
|
/// </summary>
|
||||||
|
Flipflop,
|
||||||
|
/// <summary>
|
||||||
|
/// 条件节点
|
||||||
|
/// </summary>
|
||||||
|
Condition,
|
||||||
|
/// <summary>
|
||||||
|
/// 动作节点
|
||||||
|
/// </summary>
|
||||||
|
Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
49
Library/NodeAttribute.cs
Normal file
49
Library/NodeAttribute.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Serein.Library.Enums;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Serein.Library.Attributes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表示该属性为自动注入依赖项
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
public sealed class AutoInjectionAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用来判断一个类是否需要注册并构建实例(单例模式场景使用)
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class DynamicFlowAttribute : Attribute
|
||||||
|
{
|
||||||
|
public DynamicFlowAttribute(bool scan = true)
|
||||||
|
{
|
||||||
|
Scan = scan;
|
||||||
|
}
|
||||||
|
public bool Scan { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标记一个方法是什么类型,加载dll后用来拖拽到画布中
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||||
|
public class NodeActionAttribute : Attribute
|
||||||
|
{
|
||||||
|
public NodeActionAttribute(NodeType methodDynamicType,
|
||||||
|
string methodTips = "",
|
||||||
|
bool scan = true,
|
||||||
|
string lockName = "")
|
||||||
|
{
|
||||||
|
Scan = scan;
|
||||||
|
MethodDynamicType = methodDynamicType;
|
||||||
|
MethodTips = methodTips;
|
||||||
|
LockName = lockName;
|
||||||
|
}
|
||||||
|
public bool Scan { get; set; }
|
||||||
|
public string MethodTips { get; }
|
||||||
|
public NodeType MethodDynamicType { get; }
|
||||||
|
public string LockName { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,28 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="Flow\**" />
|
|
||||||
<Compile Remove="obj\**" />
|
|
||||||
<EmbeddedResource Remove="Flow\**" />
|
|
||||||
<EmbeddedResource Remove="obj\**" />
|
|
||||||
<None Remove="Flow\**" />
|
|
||||||
<None Remove="obj\**" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="ServiceContainer.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.166" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
378
Library/Utils/SereinIoc.cs
Normal file
378
Library/Utils/SereinIoc.cs
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Attributes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Serein.Library.Utils
|
||||||
|
{
|
||||||
|
|
||||||
|
public class SereinIoc : ISereinIoc
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, object> _dependencies;
|
||||||
|
private readonly ConcurrentDictionary<string, Type> _typeMappings;
|
||||||
|
private readonly List<Type> _waitingForInstantiation;
|
||||||
|
|
||||||
|
public SereinIoc()
|
||||||
|
{
|
||||||
|
|
||||||
|
_dependencies = new ConcurrentDictionary<string, object>
|
||||||
|
{
|
||||||
|
[typeof(ISereinIoc).FullName] = this
|
||||||
|
};
|
||||||
|
|
||||||
|
_typeMappings = new ConcurrentDictionary<string, Type>();
|
||||||
|
_waitingForInstantiation = new List<Type>();
|
||||||
|
}
|
||||||
|
public object GetOrCreateServiceInstance(Type type, params object[] parameters)
|
||||||
|
{
|
||||||
|
Register(type);
|
||||||
|
object instance;
|
||||||
|
|
||||||
|
if (_dependencies.ContainsKey(type.FullName))
|
||||||
|
{
|
||||||
|
instance = _dependencies[type.FullName];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
instance = Activator.CreateInstance(type);
|
||||||
|
|
||||||
|
|
||||||
|
_dependencies[type.FullName] = instance;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
|
||||||
|
}
|
||||||
|
public T CreateServiceInstance<T>(params object[] parameters)
|
||||||
|
{
|
||||||
|
return (T)GetOrCreateServiceInstance(typeof(T), parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Reset()
|
||||||
|
{
|
||||||
|
foreach(var instancei in _dependencies.Values)
|
||||||
|
{
|
||||||
|
if (typeof(IDisposable).IsAssignableFrom(instancei.GetType()) && instancei is IDisposable disposable)
|
||||||
|
{
|
||||||
|
disposable.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_dependencies.Clear();
|
||||||
|
_waitingForInstantiation.Clear();
|
||||||
|
//_typeMappings.Clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Register(Type type, params object[] parameters)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!_typeMappings.ContainsKey(type.FullName))
|
||||||
|
{
|
||||||
|
_typeMappings[type.FullName] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public ISereinIoc Register<T>(params object[] parameters)
|
||||||
|
{
|
||||||
|
Register(typeof(T), parameters);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Register<TService, TImplementation>(params object[] parameters)
|
||||||
|
where TImplementation : TService
|
||||||
|
{
|
||||||
|
_typeMappings[typeof(TService).FullName] = typeof(TImplementation);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetOrInstantiate(Type type)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (!_dependencies.TryGetValue(type.FullName, out object value))
|
||||||
|
{
|
||||||
|
Register(type);
|
||||||
|
|
||||||
|
value = Instantiate(type);
|
||||||
|
|
||||||
|
InjectDependencies(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public T GetOrInstantiate<T>()
|
||||||
|
{
|
||||||
|
if(!_dependencies.TryGetValue(typeof(T).FullName, out object value))
|
||||||
|
{
|
||||||
|
Register<T>();
|
||||||
|
|
||||||
|
value = Instantiate(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)value;
|
||||||
|
//throw new InvalidOperationException("目标类型未创建实例");
|
||||||
|
}
|
||||||
|
public ISereinIoc Build()
|
||||||
|
{
|
||||||
|
foreach (var type in _typeMappings.Values)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(!_dependencies.ContainsKey(type.FullName))
|
||||||
|
{
|
||||||
|
|
||||||
|
_dependencies[type.FullName] = Activator.CreateInstance(type);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var instance in _dependencies.Values)
|
||||||
|
{
|
||||||
|
|
||||||
|
InjectDependencies(instance); // 替换占位符
|
||||||
|
}
|
||||||
|
|
||||||
|
//var instance = Instantiate(item.Value);
|
||||||
|
|
||||||
|
TryInstantiateWaitingDependencies();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Instantiate(Type controllerType, params object[] parameters)
|
||||||
|
{
|
||||||
|
var instance = Activator.CreateInstance(controllerType, parameters);
|
||||||
|
if(instance != null)
|
||||||
|
{
|
||||||
|
InjectDependencies(instance);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InjectDependencies(object instance)
|
||||||
|
{
|
||||||
|
var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToArray()
|
||||||
|
.Where(p => p.CanWrite && p.GetCustomAttribute<AutoInjectionAttribute>() != null);
|
||||||
|
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
var propertyType = property.PropertyType;
|
||||||
|
|
||||||
|
if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
|
||||||
|
{
|
||||||
|
property.SetValue(instance, dependencyInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryInstantiateWaitingDependencies()
|
||||||
|
{
|
||||||
|
foreach (var waitingType in _waitingForInstantiation.ToList())
|
||||||
|
{
|
||||||
|
if (_typeMappings.TryGetValue(waitingType.FullName, out var implementationType))
|
||||||
|
{
|
||||||
|
var instance = Instantiate(implementationType);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
_dependencies[waitingType.FullName] = instance;
|
||||||
|
|
||||||
|
_waitingForInstantiation.Remove(waitingType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region run()
|
||||||
|
public ISereinIoc Run<T>(Action<T> action)
|
||||||
|
{
|
||||||
|
var service = GetOrInstantiate<T>();
|
||||||
|
if (service != null)
|
||||||
|
{
|
||||||
|
action(service);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2>(Action<T1, T2> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
|
||||||
|
action(service1, service2);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3>(Action<T1, T2, T3> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
action(service1, service2, service3);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
action(service1, service2, service3, service4);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
var service5 = GetOrInstantiate<T5>();
|
||||||
|
action(service1, service2, service3, service4, service5);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
var service5 = GetOrInstantiate<T5>();
|
||||||
|
var service6 = GetOrInstantiate<T6>();
|
||||||
|
action(service1, service2, service3, service4, service5, service6);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
var service5 = GetOrInstantiate<T5>();
|
||||||
|
var service6 = GetOrInstantiate<T6>();
|
||||||
|
var service7 = GetOrInstantiate<T7>();
|
||||||
|
action(service1, service2, service3, service4, service5, service6, service7);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||||
|
{
|
||||||
|
var service1 = GetOrInstantiate<T1>();
|
||||||
|
var service2 = GetOrInstantiate<T2>();
|
||||||
|
var service3 = GetOrInstantiate<T3>();
|
||||||
|
var service4 = GetOrInstantiate<T4>();
|
||||||
|
var service5 = GetOrInstantiate<T5>();
|
||||||
|
var service6 = GetOrInstantiate<T6>();
|
||||||
|
var service7 = GetOrInstantiate<T7>();
|
||||||
|
var service8 = GetOrInstantiate<T8>();
|
||||||
|
action(service1, service2, service3, service4, service5, service6, service7, service8);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* public interface IServiceContainer
|
||||||
|
{
|
||||||
|
ServiceContainer Register<T>(params object[] parameters);
|
||||||
|
ServiceContainer Register<TService, TImplementation>(params object[] parameters) where TImplementation : TService;
|
||||||
|
TService Resolve<TService>();
|
||||||
|
void Get<T>(Action<T> action);
|
||||||
|
object Instantiate(Type type, params object[] parameters);
|
||||||
|
|
||||||
|
}
|
||||||
|
public class ServiceContainer : IServiceContainer
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Type, object> _dependencies;
|
||||||
|
public ServiceContainer()
|
||||||
|
{
|
||||||
|
_dependencies = new Dictionary<Type, object>
|
||||||
|
{
|
||||||
|
[typeof(IServiceContainer)] = this
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Get<T>(Action<T> action)
|
||||||
|
{
|
||||||
|
var service = Resolve<T>();
|
||||||
|
action(service);
|
||||||
|
}
|
||||||
|
public ServiceContainer Register<T>(params object[] parameters)
|
||||||
|
{
|
||||||
|
var instance = Instantiate(typeof(T), parameters);
|
||||||
|
_dependencies[typeof(T)] = instance;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceContainer Register<TService, TImplementation>(params object[] parameters)
|
||||||
|
where TImplementation : TService
|
||||||
|
{
|
||||||
|
|
||||||
|
_dependencies[typeof(TService)] = Instantiate(typeof(TImplementation), parameters);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public TService Resolve<TService>()
|
||||||
|
{
|
||||||
|
return (TService)_dependencies[typeof(TService)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Instantiate(Type controllerType, params object[] parameters)
|
||||||
|
{
|
||||||
|
var constructors = controllerType.GetConstructors(); // 获取控制器的所有构造函数
|
||||||
|
|
||||||
|
// 查找具有最多参数的构造函数
|
||||||
|
var constructor = constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
|
||||||
|
|
||||||
|
if (constructor != null)
|
||||||
|
{
|
||||||
|
if (parameters.Length > 0)
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance(controllerType, parameters);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var tmpParameters = constructor.GetParameters();
|
||||||
|
var dependencyInstances = new List<object>();
|
||||||
|
|
||||||
|
foreach (var parameter in tmpParameters)
|
||||||
|
{
|
||||||
|
var parameterType = parameter.ParameterType;
|
||||||
|
_dependencies.TryGetValue(parameterType, out var dependencyInstance);
|
||||||
|
dependencyInstances.Add(dependencyInstance);
|
||||||
|
if (dependencyInstance == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 用解析的依赖项实例化目标类型
|
||||||
|
return Activator.CreateInstance(controllerType, dependencyInstances.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance(controllerType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
64
Library/Utils/TcsSignal.cs
Normal file
64
Library/Utils/TcsSignal.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace Serein.Library.Core.NodeFlow.Tool
|
||||||
|
{
|
||||||
|
//public class TcsSignalException : Exception
|
||||||
|
//{
|
||||||
|
// public FlowStateType FsState { get; set; }
|
||||||
|
// public TcsSignalException(string? message) : base(message)
|
||||||
|
// {
|
||||||
|
// FsState = FlowStateType.Error;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public class TcsSignal<TSignal> where TSignal : struct, Enum
|
||||||
|
{
|
||||||
|
//public ConcurrentDictionary<TSignal, Queue<TaskCompletionSource<object>>> TcsEvent { get; } = new();
|
||||||
|
public ConcurrentDictionary<TSignal, TaskCompletionSource<object>> TcsEvent { get; } = new ConcurrentDictionary<TSignal, TaskCompletionSource<object>>();
|
||||||
|
|
||||||
|
public ConcurrentDictionary<TSignal, object> TcsLock { get; } = new ConcurrentDictionary<TSignal, object>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发信号
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="signal">信号</param>
|
||||||
|
/// <param name="value">传递的参数</param>
|
||||||
|
/// <returns>是否成功触发</returns>
|
||||||
|
public bool TriggerSignal<T>(TSignal signal, T value)
|
||||||
|
{
|
||||||
|
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
||||||
|
lock (tcsLock)
|
||||||
|
{
|
||||||
|
if (TcsEvent.TryRemove(signal, out var waitTcs))
|
||||||
|
{
|
||||||
|
waitTcs.SetResult(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskCompletionSource<object> CreateTcs(TSignal signal)
|
||||||
|
{
|
||||||
|
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
||||||
|
lock (tcsLock)
|
||||||
|
{
|
||||||
|
var tcs = TcsEvent.GetOrAdd(signal, new TaskCompletionSource<object>());
|
||||||
|
return tcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelTask()
|
||||||
|
{
|
||||||
|
foreach (var tcs in TcsEvent.Values)
|
||||||
|
{
|
||||||
|
tcs.SetException(new Exception("任务取消"));
|
||||||
|
}
|
||||||
|
TcsEvent.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Library/Utils/Utils.cs
Normal file
9
Library/Utils/Utils.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Serein.Library.Utils
|
||||||
|
{
|
||||||
|
public class NodeRunCts : CancellationTokenSource
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +1,31 @@
|
|||||||
using Serein.Library.Http;
|
using Serein.Library.Api;
|
||||||
using Serein.NodeFlow;
|
using Serein.Library.Enums;
|
||||||
using Serein.NodeFlow.Model;
|
using Serein.Library.Attributes;
|
||||||
using Serein.NodeFlow.Tool;
|
using Serein.Library.Core.NodeFlow;
|
||||||
|
using Serein.Library.Core.NodeFlow.Tool;
|
||||||
using static MyDll.PlcDevice;
|
using static MyDll.PlcDevice;
|
||||||
namespace MyDll
|
namespace MyDll
|
||||||
{
|
{
|
||||||
# region Web Api 层
|
# region Web Api 层
|
||||||
public class ApiController: ControllerBase
|
//public class ApiController: ControllerBase
|
||||||
{
|
//{
|
||||||
[AutoInjection]
|
// [AutoInjection]
|
||||||
public required PlcDevice PLCDevice { get; set; }
|
// public required PlcDevice PLCDevice { get; set; }
|
||||||
|
|
||||||
// example => http://127.0.0.1:8089/api/trigger?type=超宽光电信号&value=网络触发
|
// // example => http://127.0.0.1:8089/api/trigger?type=超宽光电信号&value=网络触发
|
||||||
[ApiPost]
|
// [ApiPost]
|
||||||
public dynamic Trigger([IsUrlData] string type, [IsUrlData]string value)
|
// public dynamic Trigger([IsUrlData] string type, [IsUrlData]string value)
|
||||||
{
|
// {
|
||||||
if (Enum.TryParse(type, out SignalType result) && Enum.IsDefined(typeof(SignalType), result))
|
// if (Enum.TryParse(type, out SignalType result) && Enum.IsDefined(typeof(SignalType), result))
|
||||||
{
|
// {
|
||||||
PLCDevice.TriggerSignal(result, value);// 通过 Web Api 模拟外部输入信号
|
// PLCDevice.TriggerSignal(result, value);// 通过 Web Api 模拟外部输入信号
|
||||||
return new {state = "succeed" };
|
// return new {state = "succeed" };
|
||||||
}
|
// }
|
||||||
return new { state = "fail" };
|
// return new { state = "fail" };
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
//}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 设备层
|
#region 设备层
|
||||||
@@ -69,18 +70,18 @@ namespace MyDll
|
|||||||
public class LogicControl
|
public class LogicControl
|
||||||
{
|
{
|
||||||
[AutoInjection]
|
[AutoInjection]
|
||||||
public required PlcDevice MyPlc { get; set; }
|
public PlcDevice MyPlc { get; set; }
|
||||||
|
|
||||||
|
|
||||||
#region 初始化、初始化完成以及退出的事件
|
#region 初始化、初始化完成以及退出的事件
|
||||||
[MethodDetail(DynamicNodeType.Init)]
|
[NodeAction(NodeType.Init)]
|
||||||
public void Init(DynamicContext context)
|
public void Init(IDynamicContext context)
|
||||||
{
|
{
|
||||||
context.InitService<PlcDevice>();
|
context.SereinIoc.Register<PlcDevice>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Loading)]
|
[NodeAction(NodeType.Loading)]
|
||||||
public void Loading(DynamicContext context)
|
public void Loading(IDynamicContext context)
|
||||||
{
|
{
|
||||||
#region 初始化Web Api、Db
|
#region 初始化Web Api、Db
|
||||||
|
|
||||||
@@ -124,8 +125,8 @@ namespace MyDll
|
|||||||
Console.WriteLine("初始化完成");
|
Console.WriteLine("初始化完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Exit)]
|
[NodeAction(NodeType.Exit)]
|
||||||
public void Exit(DynamicContext context)
|
public void Exit(IDynamicContext context)
|
||||||
{
|
{
|
||||||
MyPlc.Disconnect();
|
MyPlc.Disconnect();
|
||||||
MyPlc.CancelTask();
|
MyPlc.CancelTask();
|
||||||
@@ -135,8 +136,8 @@ namespace MyDll
|
|||||||
|
|
||||||
#region 触发器
|
#region 触发器
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Flipflop, "等待信号触发")]
|
[NodeAction(NodeType.Flipflop, "等待信号触发")]
|
||||||
public async Task<FlipflopContext> WaitTask(SignalType triggerType = SignalType.光电1)
|
public async Task<IFlipflopContext> WaitTask(SignalType triggerType = SignalType.光电1)
|
||||||
{
|
{
|
||||||
/*if (!Enum.TryParse(triggerValue, out SignalType triggerType) && Enum.IsDefined(typeof(SignalType), triggerType))
|
/*if (!Enum.TryParse(triggerValue, out SignalType triggerType) && Enum.IsDefined(typeof(SignalType), triggerType))
|
||||||
{
|
{
|
||||||
@@ -144,50 +145,25 @@ namespace MyDll
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//Console.WriteLine($"{Environment.NewLine}订阅信号 - {triggerValue}");
|
|
||||||
|
|
||||||
var tcs = MyPlc.CreateTcs(triggerType);
|
var tcs = MyPlc.CreateTcs(triggerType);
|
||||||
var result = await tcs.Task;
|
var result = await tcs.Task;
|
||||||
//Interlocked.Increment(ref MyPlc.Count); // 原子自增
|
|
||||||
//Console.WriteLine($"信号触发[{triggerType}] : {MyPlc.Count}{Environment.NewLine} thread :{Thread.CurrentThread.ManagedThreadId}{Environment.NewLine}");
|
|
||||||
return new FlipflopContext(FlowStateType.Succeed, MyPlc.Count);
|
return new FlipflopContext(FlowStateType.Succeed, MyPlc.Count);
|
||||||
}
|
}
|
||||||
catch (TcsSignalException)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// await Console.Out.WriteLineAsync($"取消等待信号[{triggerType}]");
|
// await Console.Out.WriteLineAsync($"取消等待信号[{triggerType}]");
|
||||||
return new FlipflopContext(FlowStateType.Error);
|
return new FlipflopContext(FlowStateType.Error);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
[MethodDetail(DynamicNodeType.Flipflop, "等待信号触发")]
|
|
||||||
public async Task<FlipflopContext> WaitTask2(string triggerValue = nameof(SignalType.光电1))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!Enum.TryParse(triggerValue, out SignalType triggerType) && Enum.IsDefined(typeof(SignalType), triggerType))
|
|
||||||
{
|
|
||||||
throw new TcsSignalException("parameter[triggerValue] is not a value in an enumeration");
|
|
||||||
}
|
|
||||||
|
|
||||||
var tcs = MyPlc.CreateTcs(triggerType);
|
|
||||||
var result = await tcs.Task;
|
|
||||||
|
|
||||||
Interlocked.Increment(ref MyPlc.Count); // 原子自增
|
|
||||||
Console.WriteLine($"信号触发[{triggerType}] : {MyPlc.Count}");
|
|
||||||
return new FlipflopContext(FlowStateType.Succeed, MyPlc.Count);
|
|
||||||
}
|
|
||||||
catch(TcsSignalException ex)
|
|
||||||
{
|
|
||||||
// await Console.Out.WriteLineAsync($"取消等待信号[{triggerValue}]");
|
|
||||||
return new FlipflopContext(ex.FsState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 动作
|
#region 动作
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action, "初始化")]
|
[NodeAction(NodeType.Action, "初始化")]
|
||||||
public PlcDevice PlcInit(string ip = "192.168.1.1",
|
public PlcDevice PlcInit(string ip = "192.168.1.1",
|
||||||
int port = 6688,
|
int port = 6688,
|
||||||
string tips = "测试")
|
string tips = "测试")
|
||||||
@@ -197,7 +173,7 @@ namespace MyDll
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action, "自增")]
|
[NodeAction(NodeType.Action, "自增")]
|
||||||
public PlcDevice 自增(int number = 1)
|
public PlcDevice 自增(int number = 1)
|
||||||
{
|
{
|
||||||
MyPlc.Count += number;
|
MyPlc.Count += number;
|
||||||
@@ -205,9 +181,9 @@ namespace MyDll
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action, "模拟循环触发")]
|
[NodeAction(NodeType.Action, "模拟循环触发")]
|
||||||
public void 模拟循环触发(DynamicContext context,
|
public void 模拟循环触发(IDynamicContext context,
|
||||||
int time = 20,
|
int time = 200,
|
||||||
int count = 5,
|
int count = 5,
|
||||||
SignalType signal = SignalType.光电1)
|
SignalType signal = SignalType.光电1)
|
||||||
{
|
{
|
||||||
@@ -217,24 +193,25 @@ namespace MyDll
|
|||||||
};
|
};
|
||||||
_ = context.CreateTimingTask(action, time, count);
|
_ = context.CreateTimingTask(action, time, count);
|
||||||
}
|
}
|
||||||
[MethodDetail(DynamicNodeType.Action, "重置计数")]
|
[NodeAction(NodeType.Action, "重置计数")]
|
||||||
public void 重置计数()
|
public void 重置计数()
|
||||||
{
|
{
|
||||||
MyPlc.Count = 0;
|
MyPlc.Count = 0;
|
||||||
}
|
}
|
||||||
[MethodDetail(DynamicNodeType.Action, "触发光电")]
|
|
||||||
|
[NodeAction(NodeType.Action, "触发光电1")]
|
||||||
public void 光电1信号触发(int data)
|
public void 光电1信号触发(int data)
|
||||||
{
|
{
|
||||||
MyPlc.Write($"信号源[光电1] - 模拟写入 : {data}{Environment.NewLine}");
|
MyPlc.Write($"信号源[光电1] - 模拟写入 : {data}{Environment.NewLine}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action, "触发光电")]
|
[NodeAction(NodeType.Action, "触发光电2")]
|
||||||
public void 光电2信号触发(int data)
|
public void 光电2信号触发(int data)
|
||||||
{
|
{
|
||||||
MyPlc.Write($"信号源[光电2] - 模拟写入 : {data}{Environment.NewLine}");
|
MyPlc.Write($"信号源[光电2] - 模拟写入 : {data}{Environment.NewLine}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action, "触发光电")]
|
[NodeAction(NodeType.Action, "触发光电3")]
|
||||||
public void 光电3信号触发(int data)
|
public void 光电3信号触发(int data)
|
||||||
{
|
{
|
||||||
MyPlc.Write($"信号源[光电3] - 模拟写入 : {data}{Environment.NewLine}");
|
MyPlc.Write($"信号源[光电3] - 模拟写入 : {data}{Environment.NewLine}");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Serein.Library.IOC;
|
|
||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -19,36 +19,97 @@ namespace Serein.NodeFlow
|
|||||||
// Cancel,
|
// Cancel,
|
||||||
// Error,
|
// Error,
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
//public class FlipflopContext
|
||||||
|
//{
|
||||||
|
// public FlowStateType State { get; set; }
|
||||||
|
// public object? Data { get; set; }
|
||||||
|
// public FlipflopContext(FlowStateType ffState, object? data = null)
|
||||||
|
// {
|
||||||
|
// State = ffState;
|
||||||
|
// Data = data;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public static class FlipflopFunc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 传入触发器方法的返回类型,尝试获取Task[Flipflop[]] 中的泛型类型
|
||||||
|
/// </summary>
|
||||||
|
//public static Type GetFlipflopInnerType(Type type)
|
||||||
|
//{
|
||||||
|
// // 检查是否为泛型类型且为 Task<>
|
||||||
|
// if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
||||||
|
// {
|
||||||
|
// // 获取 Task<> 的泛型参数类型,即 Flipflop<>
|
||||||
|
// var innerType = type.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
// // 检查泛型参数是否为 Flipflop<>
|
||||||
|
// if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||||
|
// {
|
||||||
|
// // 获取 Flipflop<> 的泛型参数类型,即 T
|
||||||
|
// var flipflopInnerType = innerType.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
// // 返回 Flipflop<> 中的具体类型
|
||||||
|
// return flipflopInnerType;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // 如果不符合条件,返回 null
|
||||||
|
// return null;
|
||||||
|
//}
|
||||||
|
|
||||||
|
public static bool IsTaskOfFlipflop(Type type)
|
||||||
|
{
|
||||||
|
// 检查是否为泛型类型且为 Task<>
|
||||||
|
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
||||||
|
{
|
||||||
|
// 获取 Task<> 的泛型参数类型
|
||||||
|
var innerType = type.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
// 检查泛型参数是否为 Flipflop<>
|
||||||
|
if (innerType == typeof(FlipflopContext))
|
||||||
|
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 触发器上下文
|
/// 触发器上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FlipflopContext
|
public class FlipflopContext//<TResult>
|
||||||
{
|
{
|
||||||
public FlowStateType State { get; set; }
|
public LibraryCore.NodeFlow.FlowStateType State { get; set; }
|
||||||
|
//public TResult? Data { get; set; }
|
||||||
public object? Data { get; set; }
|
public object? Data { get; set; }
|
||||||
/*public FlipflopContext()
|
public FlipflopContext(FlowStateType ffState)
|
||||||
{
|
{
|
||||||
State = FfState.Cancel;
|
State = ffState;
|
||||||
}*/
|
}
|
||||||
public FlipflopContext(FlowStateType ffState, object? data = null)
|
public FlipflopContext(FlowStateType ffState, object data)
|
||||||
{
|
{
|
||||||
State = ffState;
|
State = ffState;
|
||||||
Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 动态流程上下文
|
/// 动态流程上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public class DynamicContext(IServiceContainer serviceContainer)
|
public class DynamicContext(ISereinIoc serviceContainer)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly string contextGuid = "";//System.Guid.NewGuid().ToString();
|
private readonly string contextGuid = "";//System.Guid.NewGuid().ToString();
|
||||||
|
|
||||||
public IServiceContainer ServiceContainer { get; } = serviceContainer;
|
public ISereinIoc ServiceContainer { get; } = serviceContainer;
|
||||||
private List<Type> InitServices { get; set; } = [];
|
private List<Type> InitServices { get; set; } = [];
|
||||||
|
|
||||||
// private ConcurrentDictionary<string, object?> ContextData { get; set; } = [];
|
// private ConcurrentDictionary<string, object?> ContextData { get; set; } = [];
|
||||||
@@ -152,7 +213,7 @@ namespace Serein.NodeFlow
|
|||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
NodeRunCts.Token.ThrowIfCancellationRequested();
|
NodeRunCts.Token.ThrowIfCancellationRequested();
|
||||||
await time;
|
await Task.Delay(time);
|
||||||
action.Invoke();
|
action.Invoke();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
38
NodeFlow/DynamicNodeFrameworkType.cs
Normal file
38
NodeFlow/DynamicNodeFrameworkType.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow
|
||||||
|
{
|
||||||
|
//public enum DynamicNodeType
|
||||||
|
//{
|
||||||
|
// /// <summary>
|
||||||
|
// /// 初始化
|
||||||
|
// /// </summary>
|
||||||
|
// Init,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 开始载入
|
||||||
|
// /// </summary>
|
||||||
|
// Loading,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 结束
|
||||||
|
// /// </summary>
|
||||||
|
// Exit,
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 触发器
|
||||||
|
// /// </summary>
|
||||||
|
// Flipflop,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 条件节点
|
||||||
|
// /// </summary>
|
||||||
|
// Condition,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 动作节点
|
||||||
|
// /// </summary>
|
||||||
|
// Action,
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
24
NodeFlow/FlowStateType.cs
Normal file
24
NodeFlow/FlowStateType.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow
|
||||||
|
{
|
||||||
|
//public enum FlowStateType
|
||||||
|
//{
|
||||||
|
// /// <summary>
|
||||||
|
// /// 成功(方法成功执行)
|
||||||
|
// /// </summary>
|
||||||
|
// Succeed,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误)
|
||||||
|
// /// </summary>
|
||||||
|
// Fail,
|
||||||
|
// /// <summary>
|
||||||
|
// /// 异常(节点没有成功执行,执行时发生非预期的错误)
|
||||||
|
// /// </summary>
|
||||||
|
// Error,
|
||||||
|
//}
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
namespace Serein.NodeFlow
|
using Serein.Library.Enums;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显式参数
|
/// 显式参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -63,6 +67,10 @@
|
|||||||
|
|
||||||
public class MethodDetails
|
public class MethodDetails
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 拷贝
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public MethodDetails Clone()
|
public MethodDetails Clone()
|
||||||
{
|
{
|
||||||
return new MethodDetails
|
return new MethodDetails
|
||||||
@@ -76,7 +84,7 @@
|
|||||||
ReturnType = ReturnType,
|
ReturnType = ReturnType,
|
||||||
MethodName = MethodName,
|
MethodName = MethodName,
|
||||||
MethodLockName = MethodLockName,
|
MethodLockName = MethodLockName,
|
||||||
|
IsNetFramework = IsNetFramework,
|
||||||
ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(),
|
ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -114,7 +122,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 节点类型
|
/// 节点类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DynamicNodeType MethodDynamicType { get; set; }
|
public NodeType MethodDynamicType { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 锁名称
|
/// 锁名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -135,98 +143,97 @@
|
|||||||
|
|
||||||
public ExplicitData[] ExplicitDatas { get; set; }
|
public ExplicitData[] ExplicitDatas { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 出参类型
|
/// 出参类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public Type ReturnType { get; set; }
|
public Type ReturnType { get; set; }
|
||||||
|
|
||||||
|
public bool IsNetFramework { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public bool IsCanConnect(Type returnType)
|
//public bool IsCanConnect(Type returnType)
|
||||||
{
|
//{
|
||||||
if (ExplicitDatas.Length == 0)
|
// if (ExplicitDatas.Length == 0)
|
||||||
{
|
// {
|
||||||
// 目标不需要传参,可以舍弃结果?
|
// // 目标不需要传参,可以舍弃结果?
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
var types = ExplicitDatas.Select(it => it.DataType).ToArray();
|
// var types = ExplicitDatas.Select(it => it.DataType).ToArray();
|
||||||
// 检查返回类型是否是元组类型
|
// // 检查返回类型是否是元组类型
|
||||||
if (returnType.IsGenericType && IsValueTuple(returnType))
|
// if (returnType.IsGenericType && IsValueTuple(returnType))
|
||||||
{
|
// {
|
||||||
|
|
||||||
return CompareGenericArguments(returnType, types);
|
// return CompareGenericArguments(returnType, types);
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
int index = 0;
|
// int index = 0;
|
||||||
if (types[index] == typeof(DynamicContext))
|
// if (types[index] == typeof(DynamicContext))
|
||||||
{
|
// {
|
||||||
index++;
|
// index++;
|
||||||
if (types.Length == 1)
|
// if (types.Length == 1)
|
||||||
{
|
// {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// 被连接节点检查自己需要的参数类型,与发起连接的节点比较返回值类型
|
// // 被连接节点检查自己需要的参数类型,与发起连接的节点比较返回值类型
|
||||||
if (returnType == types[index])
|
// if (returnType == types[index])
|
||||||
{
|
// {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return false;
|
// return false;
|
||||||
}
|
//}
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 检查元组类型
|
///// 检查元组类型
|
||||||
/// </summary>
|
///// </summary>
|
||||||
/// <param name="type"></param>
|
///// <param name="type"></param>
|
||||||
/// <returns></returns>
|
///// <returns></returns>
|
||||||
private bool IsValueTuple(Type type)
|
//private bool IsValueTuple(Type type)
|
||||||
{
|
//{
|
||||||
if (!type.IsGenericType) return false;
|
// if (!type.IsGenericType) return false;
|
||||||
|
|
||||||
var genericTypeDef = type.GetGenericTypeDefinition();
|
// var genericTypeDef = type.GetGenericTypeDefinition();
|
||||||
return genericTypeDef == typeof(ValueTuple<>) ||
|
// return genericTypeDef == typeof(ValueTuple<>) ||
|
||||||
genericTypeDef == typeof(ValueTuple<,>) ||
|
// genericTypeDef == typeof(ValueTuple<,>) ||
|
||||||
genericTypeDef == typeof(ValueTuple<,,>) ||
|
// genericTypeDef == typeof(ValueTuple<,,>) ||
|
||||||
genericTypeDef == typeof(ValueTuple<,,,>) ||
|
// genericTypeDef == typeof(ValueTuple<,,,>) ||
|
||||||
genericTypeDef == typeof(ValueTuple<,,,,>) ||
|
// genericTypeDef == typeof(ValueTuple<,,,,>) ||
|
||||||
genericTypeDef == typeof(ValueTuple<,,,,,>) ||
|
// genericTypeDef == typeof(ValueTuple<,,,,,>) ||
|
||||||
genericTypeDef == typeof(ValueTuple<,,,,,,>) ||
|
// genericTypeDef == typeof(ValueTuple<,,,,,,>) ||
|
||||||
genericTypeDef == typeof(ValueTuple<,,,,,,,>);
|
// genericTypeDef == typeof(ValueTuple<,,,,,,,>);
|
||||||
}
|
//}
|
||||||
|
|
||||||
private bool CompareGenericArguments(Type returnType, Type[] parameterTypes)
|
//private bool CompareGenericArguments(Type returnType, Type[] parameterTypes)
|
||||||
{
|
//{
|
||||||
var genericArguments = returnType.GetGenericArguments();
|
// var genericArguments = returnType.GetGenericArguments();
|
||||||
var length = parameterTypes.Length;
|
// var length = parameterTypes.Length;
|
||||||
|
|
||||||
for (int i = 0; i < genericArguments.Length; i++)
|
// for (int i = 0; i < genericArguments.Length; i++)
|
||||||
{
|
// {
|
||||||
if (i >= length) return false;
|
// if (i >= length) return false;
|
||||||
|
|
||||||
if (IsValueTuple(genericArguments[i]))
|
// if (IsValueTuple(genericArguments[i]))
|
||||||
{
|
// {
|
||||||
// 如果当前参数也是 ValueTuple,递归检查嵌套的泛型参数
|
// // 如果当前参数也是 ValueTuple,递归检查嵌套的泛型参数
|
||||||
if (!CompareGenericArguments(genericArguments[i], parameterTypes.Skip(i).ToArray()))
|
// if (!CompareGenericArguments(genericArguments[i], parameterTypes.Skip(i).ToArray()))
|
||||||
{
|
// {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else if (genericArguments[i] != parameterTypes[i])
|
// else if (genericArguments[i] != parameterTypes[i])
|
||||||
{
|
// {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return true;
|
// return true;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
namespace Serein.NodeFlow.Model
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Enums;
|
||||||
|
using Serein.Library.Core.NodeFlow;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Model
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 组合条件节点(用于条件区域)
|
/// 组合条件节点(用于条件区域)
|
||||||
@@ -19,7 +23,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override object? Execute(DynamicContext context)
|
public override object? Execute(IDynamicContext context)
|
||||||
{
|
{
|
||||||
// bool allTrue = ConditionNodes.All(condition => Judge(context,condition.MethodDetails));
|
// bool allTrue = ConditionNodes.All(condition => Judge(context,condition.MethodDetails));
|
||||||
// bool IsAllTrue = true; // 初始化为 true
|
// bool IsAllTrue = true; // 初始化为 true
|
||||||
@@ -50,7 +54,7 @@
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
private FlowStateType Judge(DynamicContext context, SingleConditionNode node)
|
private FlowStateType Judge(IDynamicContext context, SingleConditionNode node)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Serein.NodeFlow;
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Enums;
|
||||||
|
using Serein.Library.Core.NodeFlow;
|
||||||
using Serein.NodeFlow.Tool;
|
using Serein.NodeFlow.Tool;
|
||||||
|
using Serein.NodeFlow.Tool.SerinExpression;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Model
|
namespace Serein.NodeFlow.Model
|
||||||
{
|
{
|
||||||
@@ -25,21 +28,6 @@ namespace Serein.NodeFlow.Model
|
|||||||
Upstream,
|
Upstream,
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum FlowStateType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 成功(方法成功执行)
|
|
||||||
/// </summary>
|
|
||||||
Succeed,
|
|
||||||
/// <summary>
|
|
||||||
/// 失败(方法没有成功执行,不过执行时没有发生非预期的错误)
|
|
||||||
/// </summary>
|
|
||||||
Fail,
|
|
||||||
/// <summary>
|
|
||||||
/// 异常(节点没有
|
|
||||||
/// </summary>
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
||||||
@@ -103,7 +91,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">流程上下文</param>
|
/// <param name="context">流程上下文</param>
|
||||||
/// <returns>节点传回数据对象</returns>
|
/// <returns>节点传回数据对象</returns>
|
||||||
public virtual object? Execute(DynamicContext context)
|
public virtual object? Execute(IDynamicContext context)
|
||||||
{
|
{
|
||||||
MethodDetails md = MethodDetails;
|
MethodDetails md = MethodDetails;
|
||||||
object? result = null;
|
object? result = null;
|
||||||
@@ -155,7 +143,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns>节点传回数据对象</returns>
|
/// <returns>节点传回数据对象</returns>
|
||||||
/// <exception cref="Exception"></exception>
|
/// <exception cref="Exception"></exception>
|
||||||
public virtual async Task<object?> ExecuteAsync(DynamicContext context)
|
public virtual async Task<object?> ExecuteAsync(IDynamicContext context)
|
||||||
{
|
{
|
||||||
MethodDetails md = MethodDetails;
|
MethodDetails md = MethodDetails;
|
||||||
object? result = null;
|
object? result = null;
|
||||||
@@ -165,18 +153,18 @@ namespace Serein.NodeFlow.Model
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlipflopContext flipflopContext = null;
|
IFlipflopContext flipflopContext = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 调用委托并获取结果
|
// 调用委托并获取结果
|
||||||
if (md.ExplicitDatas.Length == 0)
|
if (md.ExplicitDatas.Length == 0)
|
||||||
{
|
{
|
||||||
flipflopContext = await ((Func<object, Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance);
|
flipflopContext = await ((Func<object, Task<IFlipflopContext>>)del).Invoke(MethodDetails.ActingInstance);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
object?[]? parameters = GetParameters(context, MethodDetails);
|
object?[]? parameters = GetParameters(context, MethodDetails);
|
||||||
flipflopContext = await ((Func<object, object[], Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance, parameters);
|
flipflopContext = await ((Func<object, object[], Task<IFlipflopContext>>)del).Invoke(MethodDetails.ActingInstance, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flipflopContext != null)
|
if (flipflopContext != null)
|
||||||
@@ -186,6 +174,10 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
result = flipflopContext.Data;
|
result = flipflopContext.Data;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -202,9 +194,9 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task StartExecution(DynamicContext context)
|
public async Task StartExecution(IDynamicContext context)
|
||||||
{
|
{
|
||||||
var cts = context.ServiceContainer.GetOrInstantiate<CancellationTokenSource>();
|
var cts = context.SereinIoc.GetOrInstantiate<CancellationTokenSource>();
|
||||||
|
|
||||||
Stack<NodeBase> stack = [];
|
Stack<NodeBase> stack = [];
|
||||||
stack.Push(this);
|
stack.Push(this);
|
||||||
@@ -217,7 +209,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
// 设置方法执行的对象
|
// 设置方法执行的对象
|
||||||
if (currentNode.MethodDetails != null)
|
if (currentNode.MethodDetails != null)
|
||||||
{
|
{
|
||||||
currentNode.MethodDetails.ActingInstance ??= context.ServiceContainer.GetOrInstantiate(MethodDetails.ActingInstanceType);
|
currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrInstantiate(MethodDetails.ActingInstanceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取上游分支,首先执行一次
|
// 获取上游分支,首先执行一次
|
||||||
@@ -228,7 +220,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
await upstreamNodes[i].StartExecution(context);
|
await upstreamNodes[i].StartExecution(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == DynamicNodeType.Flipflop)
|
if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == NodeType.Flipflop)
|
||||||
{
|
{
|
||||||
// 触发器节点
|
// 触发器节点
|
||||||
currentNode.FlowData = await currentNode.ExecuteAsync(context);
|
currentNode.FlowData = await currentNode.ExecuteAsync(context);
|
||||||
@@ -259,7 +251,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取对应的参数数组
|
/// 获取对应的参数数组
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object[]? GetParameters(DynamicContext context, MethodDetails md)
|
public object[]? GetParameters(IDynamicContext context, MethodDetails md)
|
||||||
{
|
{
|
||||||
// 用正确的大小初始化参数数组
|
// 用正确的大小初始化参数数组
|
||||||
var types = md.ExplicitDatas.Select(it => it.DataType).ToArray();
|
var types = md.ExplicitDatas.Select(it => it.DataType).ToArray();
|
||||||
@@ -278,7 +270,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
|
|
||||||
var f1 = PreviousNode?.FlowData?.GetType();
|
var f1 = PreviousNode?.FlowData?.GetType();
|
||||||
var f2 = mdEd.DataType;
|
var f2 = mdEd.DataType;
|
||||||
if (type == typeof(DynamicContext))
|
if (type == typeof(IDynamicContext))
|
||||||
{
|
{
|
||||||
parameters[i] = context;
|
parameters[i] = context;
|
||||||
}
|
}
|
||||||
@@ -292,37 +284,79 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
else if (mdEd.IsExplicitData) // 显式参数
|
else if (mdEd.IsExplicitData) // 显式参数
|
||||||
{
|
{
|
||||||
if (mdEd.DataType.IsEnum)
|
// 判断是否使用表达式解析
|
||||||
|
if (mdEd.DataValue[0] == '@')
|
||||||
{
|
{
|
||||||
var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue);
|
var expResult = SerinExpressionEvaluator.Evaluate(mdEd.DataValue, PreviousNode?.FlowData, out bool isChange);
|
||||||
parameters[i] = enumValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(string))
|
if (mdEd.DataType.IsEnum)
|
||||||
{
|
{
|
||||||
parameters[i] = mdEd.DataValue;
|
var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue);
|
||||||
}
|
parameters[i] = enumValue;
|
||||||
else if (mdEd.ExplicitType == typeof(bool))
|
}
|
||||||
{
|
else if (mdEd.ExplicitType == typeof(string))
|
||||||
parameters[i] = bool.Parse(mdEd.DataValue);
|
{
|
||||||
}
|
parameters[i] = Convert.ChangeType(expResult, typeof(string));
|
||||||
else if (mdEd.ExplicitType == typeof(int))
|
}
|
||||||
{
|
else if (mdEd.ExplicitType == typeof(bool))
|
||||||
parameters[i] = int.Parse(mdEd.DataValue);
|
{
|
||||||
}
|
parameters[i] = Convert.ChangeType(expResult, typeof(bool));
|
||||||
else if (mdEd.ExplicitType == typeof(double))
|
}
|
||||||
{
|
else if (mdEd.ExplicitType == typeof(int))
|
||||||
parameters[i] = double.Parse(mdEd.DataValue);
|
{
|
||||||
|
parameters[i] = Convert.ChangeType(expResult, typeof(int));
|
||||||
|
}
|
||||||
|
else if (mdEd.ExplicitType == typeof(double))
|
||||||
|
{
|
||||||
|
parameters[i] = Convert.ChangeType(expResult, typeof(double));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parameters[i] = expResult;
|
||||||
|
//parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parameters[i] = "";
|
if (mdEd.DataType.IsEnum)
|
||||||
|
{
|
||||||
|
var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue);
|
||||||
|
parameters[i] = enumValue;
|
||||||
|
}
|
||||||
|
else if (mdEd.ExplicitType == typeof(string))
|
||||||
|
{
|
||||||
|
parameters[i] = mdEd.DataValue;
|
||||||
|
}
|
||||||
|
else if (mdEd.ExplicitType == typeof(bool))
|
||||||
|
{
|
||||||
|
parameters[i] = bool.Parse(mdEd.DataValue);
|
||||||
|
}
|
||||||
|
else if (mdEd.ExplicitType == typeof(int))
|
||||||
|
{
|
||||||
|
parameters[i] = int.Parse(mdEd.DataValue);
|
||||||
|
}
|
||||||
|
else if (mdEd.ExplicitType == typeof(double))
|
||||||
|
{
|
||||||
|
parameters[i] = double.Parse(mdEd.DataValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parameters[i] = "";
|
||||||
|
|
||||||
//parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType);
|
//parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (f1 != null && f2 != null && f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName))
|
else if (f1 != null && f2 != null)
|
||||||
{
|
{
|
||||||
parameters[i] = PreviousNode?.FlowData;
|
if(f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName))
|
||||||
|
{
|
||||||
|
parameters[i] = PreviousNode?.FlowData;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
|
||||||
using Serein.Library.SerinExpression;
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Enums;
|
||||||
|
using Serein.Library.Core.NodeFlow;
|
||||||
|
using Serein.NodeFlow.Tool.SerinExpression;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Model
|
namespace Serein.NodeFlow.Model
|
||||||
{
|
{
|
||||||
@@ -24,7 +27,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
public string Expression { get; set; }
|
public string Expression { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public override object? Execute(DynamicContext context)
|
public override object? Execute(IDynamicContext context)
|
||||||
{
|
{
|
||||||
// 接收上一节点参数or自定义参数内容
|
// 接收上一节点参数or自定义参数内容
|
||||||
object? result;
|
object? result;
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
using Serein.Library.SerinExpression;
|
using Serein.Library.Api;
|
||||||
using Serein.NodeFlow;
|
using Serein.Library.Enums;
|
||||||
using System;
|
using Serein.Library.Core.NodeFlow;
|
||||||
using System.Collections.Generic;
|
using Serein.NodeFlow.Tool.SerinExpression;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Model
|
namespace Serein.NodeFlow.Model
|
||||||
{
|
{
|
||||||
@@ -19,7 +16,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
public string Expression { get; set; }
|
public string Expression { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public override object? Execute(DynamicContext context)
|
public override object? Execute(IDynamicContext context)
|
||||||
{
|
{
|
||||||
var data = PreviousNode?.FlowData;
|
var data = PreviousNode?.FlowData;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using Serein.NodeFlow;
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Core.NodeFlow;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Model
|
namespace Serein.NodeFlow.Model
|
||||||
{
|
{
|
||||||
|
|
||||||
public class SingleFlipflopNode : NodeBase
|
public class SingleFlipflopNode : NodeBase
|
||||||
{
|
{
|
||||||
public override object Execute(DynamicContext context)
|
public override object Execute(IDynamicContext context)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("无法以非await/async的形式调用触发器");
|
throw new NotImplementedException("无法以非await/async的形式调用触发器");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using Serein.Library.Http;
|
using Serein.Library.Api;
|
||||||
using Serein.Library.IOC;
|
using Serein.Library.Enums;
|
||||||
using Serein.NodeFlow;
|
using Serein.Library.Core.NodeFlow;
|
||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
using Serein.NodeFlow.Tool;
|
using Serein.NodeFlow.Tool;
|
||||||
using SqlSugar;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow
|
namespace Serein.NodeFlow
|
||||||
{
|
{
|
||||||
@@ -14,16 +13,16 @@ namespace Serein.NodeFlow
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class NodeFlowStarter(IServiceContainer serviceContainer, List<MethodDetails> methodDetails)
|
public class NodeFlowStarter(ISereinIoc serviceContainer, List<MethodDetails> methodDetails)
|
||||||
|
|
||||||
{
|
{
|
||||||
private readonly IServiceContainer ServiceContainer = serviceContainer;
|
private readonly ISereinIoc ServiceContainer = serviceContainer;
|
||||||
private readonly List<MethodDetails> methodDetails = methodDetails;
|
private readonly List<MethodDetails> methodDetails = methodDetails;
|
||||||
|
|
||||||
private Action ExitAction = null;
|
private Action ExitAction = null;
|
||||||
|
|
||||||
|
|
||||||
private DynamicContext context = null;
|
private IDynamicContext context = null;
|
||||||
|
|
||||||
|
|
||||||
public NodeRunTcs MainCts;
|
public NodeRunTcs MainCts;
|
||||||
@@ -42,19 +41,26 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
var startNode = nodes.FirstOrDefault(p => p.IsStart);
|
var startNode = nodes.FirstOrDefault(p => p.IsStart);
|
||||||
if (startNode == null) { return; }
|
if (startNode == null) { return; }
|
||||||
context = new(ServiceContainer);
|
if (false)
|
||||||
|
{
|
||||||
|
context = new Serein.Library.Core.NodeFlow.DynamicContext(ServiceContainer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context = new Serein.Library.Framework.NodeFlow.DynamicContext(ServiceContainer);
|
||||||
|
}
|
||||||
|
|
||||||
MainCts = ServiceContainer.CreateServiceInstance<NodeRunTcs>();
|
MainCts = ServiceContainer.CreateServiceInstance<NodeRunTcs>();
|
||||||
|
|
||||||
var initMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Init).ToList();
|
var initMethods = methodDetails.Where(it => it.MethodDynamicType == NodeType.Init).ToList();
|
||||||
var loadingMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Loading).ToList();
|
var loadingMethods = methodDetails.Where(it => it.MethodDynamicType == NodeType.Loading).ToList();
|
||||||
var exitMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Exit).ToList();
|
var exitMethods = methodDetails.Where(it => it.MethodDynamicType == NodeType.Exit).ToList();
|
||||||
ExitAction = () =>
|
ExitAction = () =>
|
||||||
{
|
{
|
||||||
ServiceContainer.Run<WebServer>((web) =>
|
//ServiceContainer.Run<WebServer>((web) =>
|
||||||
{
|
//{
|
||||||
web?.Stop();
|
// web?.Stop();
|
||||||
});
|
//});
|
||||||
foreach (MethodDetails? md in exitMethods)
|
foreach (MethodDetails? md in exitMethods)
|
||||||
{
|
{
|
||||||
object?[]? args = [context];
|
object?[]? args = [context];
|
||||||
@@ -77,7 +83,7 @@ namespace Serein.NodeFlow
|
|||||||
object?[]? data = [md.ActingInstance, args];
|
object?[]? data = [md.ActingInstance, args];
|
||||||
md.MethodDelegate.DynamicInvoke(data);
|
md.MethodDelegate.DynamicInvoke(data);
|
||||||
}
|
}
|
||||||
context.Biuld();
|
context.SereinIoc.Build();
|
||||||
|
|
||||||
foreach (var md in loadingMethods) // 加载
|
foreach (var md in loadingMethods) // 加载
|
||||||
{
|
{
|
||||||
@@ -87,7 +93,7 @@ namespace Serein.NodeFlow
|
|||||||
md.MethodDelegate.DynamicInvoke(data);
|
md.MethodDelegate.DynamicInvoke(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
var flipflopNodes = nodes.Where(it => it.MethodDetails?.MethodDynamicType == DynamicNodeType.Flipflop
|
var flipflopNodes = nodes.Where(it => it.MethodDetails?.MethodDynamicType == NodeType.Flipflop
|
||||||
&& it.PreviousNodes.Count == 0
|
&& it.PreviousNodes.Count == 0
|
||||||
&& it.IsStart != true).ToArray();
|
&& it.IsStart != true).ToArray();
|
||||||
|
|
||||||
@@ -126,15 +132,17 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var func = md.ExplicitDatas.Length == 0 ? (Func<object, object, Task<FlipflopContext>>)del : (Func<object, object[], Task<FlipflopContext>>)del;
|
//var func = md.ExplicitDatas.Length == 0 ? (Func<object, object, Task<FlipflopContext<dynamic>>>)del : (Func<object, object[], Task<FlipflopContext<dynamic>>>)del;
|
||||||
|
var func = md.ExplicitDatas.Length == 0 ? (Func<object, object, Task<IFlipflopContext>>)del : (Func<object, object[], Task<IFlipflopContext>>)del;
|
||||||
|
|
||||||
while (!MainCts.IsCancellationRequested) // 循环中直到栈为空才会退出
|
while (!MainCts.IsCancellationRequested) // 循环中直到栈为空才会退出
|
||||||
{
|
{
|
||||||
object?[]? parameters = singleFlipFlopNode.GetParameters(context, md);
|
object?[]? parameters = singleFlipFlopNode.GetParameters(context, md);
|
||||||
// 调用委托并获取结果
|
// 调用委托并获取结果
|
||||||
|
|
||||||
FlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters);
|
IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters);
|
||||||
|
|
||||||
if (flipflopContext == null)
|
if (flipflopContext == null)
|
||||||
{
|
{
|
||||||
|
|||||||
36
NodeFlow/Serein - Backup.NodeFlow.csproj
Normal file
36
NodeFlow/Serein - Backup.NodeFlow.csproj
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="bin\**" />
|
||||||
|
<Compile Remove="SerinExpression\**" />
|
||||||
|
<EmbeddedResource Remove="bin\**" />
|
||||||
|
<EmbeddedResource Remove="SerinExpression\**" />
|
||||||
|
<None Remove="bin\**" />
|
||||||
|
<None Remove="SerinExpression\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="DynamicContext.cs" />
|
||||||
|
<Compile Remove="Tool\Attribute.cs" />
|
||||||
|
<Compile Remove="Tool\DynamicTool.cs" />
|
||||||
|
<Compile Remove="Tool\TcsSignal.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LibraryCore\Serein.Library.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Library.Framework\Serein.Library.Framework.csproj" />
|
||||||
|
<ProjectReference Include="..\Serein.Library.Api\Serein.Library.Api.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -17,6 +17,19 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Remove="DynamicContext.cs" />
|
||||||
|
<Compile Remove="Tool\Attribute.cs" />
|
||||||
|
<Compile Remove="Tool\DynamicTool.cs" />
|
||||||
|
<Compile Remove="Tool\TcsSignal.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Library.Core\Serein.Library.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Library.Framework\Serein.Library.Framework.csproj" />
|
||||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Serein.NodeFlow
|
namespace Serein.NodeFlow.Tool
|
||||||
{
|
{
|
||||||
|
|
||||||
public enum DynamicNodeType
|
public enum DynamicNodeType
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Serein.Library.IOC;
|
using Serein.Library.Api;
|
||||||
using Serein.NodeFlow;
|
using Serein.Library.Attributes;
|
||||||
|
using Serein.Library.Core.NodeFlow;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@@ -27,16 +28,16 @@ public static class DelegateGenerator
|
|||||||
/// <param name="serviceContainer"></param>
|
/// <param name="serviceContainer"></param>
|
||||||
/// <param name="type"></param>
|
/// <param name="type"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static ConcurrentDictionary<string, MethodDetails> GenerateMethodDetails(IServiceContainer serviceContainer, Type type)
|
public static ConcurrentDictionary<string, MethodDetails> GenerateMethodDetails(ISereinIoc serviceContainer, Type type, bool isNetFramework)
|
||||||
{
|
{
|
||||||
var methodDetailsDictionary = new ConcurrentDictionary<string, MethodDetails>();
|
var methodDetailsDictionary = new ConcurrentDictionary<string, MethodDetails>();
|
||||||
var assemblyName = type.Assembly.GetName().Name;
|
var assemblyName = type.Assembly.GetName().Name;
|
||||||
var methods = GetMethodsToProcess(type);
|
var methods = GetMethodsToProcess(type, isNetFramework);
|
||||||
|
|
||||||
foreach (var method in methods)
|
foreach (var method in methods)
|
||||||
{
|
{
|
||||||
|
|
||||||
var methodDetails = CreateMethodDetails(serviceContainer, type, method, assemblyName);
|
var methodDetails = CreateMethodDetails(serviceContainer, type, method, assemblyName, isNetFramework);
|
||||||
|
|
||||||
methodDetailsDictionary.TryAdd(methodDetails.MethodName, methodDetails);
|
methodDetailsDictionary.TryAdd(methodDetails.MethodName, methodDetails);
|
||||||
}
|
}
|
||||||
@@ -47,48 +48,56 @@ public static class DelegateGenerator
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取处理方法
|
/// 获取处理方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static IEnumerable<MethodInfo> GetMethodsToProcess(Type type)
|
private static IEnumerable<MethodInfo> GetMethodsToProcess(Type type, bool isNetFramework)
|
||||||
{
|
{
|
||||||
return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
if (isNetFramework)
|
||||||
.Where(m => m.GetCustomAttribute<MethodDetailAttribute>()?.Scan == true);
|
{
|
||||||
|
|
||||||
|
return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
|
.Where(m => m.GetCustomAttribute<NodeActionAttribute>()?.Scan == true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
|
.Where(m => m.GetCustomAttribute<NodeActionAttribute>()?.Scan == true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建方法信息
|
/// 创建方法信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static MethodDetails CreateMethodDetails(IServiceContainer serviceContainer, Type type, MethodInfo method, string assemblyName)
|
private static MethodDetails CreateMethodDetails(ISereinIoc serviceContainer, Type type, MethodInfo method, string assemblyName, bool isNetFramework)
|
||||||
{
|
{
|
||||||
var methodName = method.Name;
|
|
||||||
var attribute = method.GetCustomAttribute<MethodDetailAttribute>();
|
var methodName = method.Name;
|
||||||
|
var attribute = method.GetCustomAttribute<NodeActionAttribute>();
|
||||||
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
|
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
|
||||||
// 生成委托
|
// 生成委托
|
||||||
var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
|
var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
|
||||||
method, // 方法信息
|
method, // 方法信息
|
||||||
method.GetParameters(),// 方法参数
|
method.GetParameters(),// 方法参数
|
||||||
method.ReturnType);// 返回值
|
method.ReturnType);// 返回值
|
||||||
|
|
||||||
|
|
||||||
var dllTypeName = $"{assemblyName}.{type.Name}";
|
var dllTypeName = $"{assemblyName}.{type.Name}";
|
||||||
serviceContainer.Register(type);
|
serviceContainer.Register(type);
|
||||||
object instance = serviceContainer.GetOrCreateServiceInstance(type);
|
object instance = serviceContainer.GetOrCreateServiceInstance(type);
|
||||||
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
|
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return new MethodDetails
|
|
||||||
{
|
|
||||||
ActingInstanceType = type,
|
|
||||||
ActingInstance = instance,
|
|
||||||
MethodName = dllTypeMethodName,
|
|
||||||
MethodDelegate = methodDelegate,
|
|
||||||
MethodDynamicType = attribute.MethodDynamicType,
|
|
||||||
MethodLockName = attribute.LockName,
|
|
||||||
MethodTips = attribute.MethodTips,
|
|
||||||
ExplicitDatas = explicitDataOfParameters,
|
|
||||||
ReturnType = method.ReturnType,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
return new MethodDetails
|
||||||
|
{
|
||||||
|
ActingInstanceType = type,
|
||||||
|
ActingInstance = instance,
|
||||||
|
MethodName = dllTypeMethodName,
|
||||||
|
MethodDelegate = methodDelegate,
|
||||||
|
MethodDynamicType = attribute.MethodDynamicType,
|
||||||
|
MethodLockName = attribute.LockName,
|
||||||
|
MethodTips = attribute.MethodTips,
|
||||||
|
ExplicitDatas = explicitDataOfParameters,
|
||||||
|
ReturnType = method.ReturnType,
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExplicitData[] GetExplicitDataOfParameters(ParameterInfo[] parameters)
|
private static ExplicitData[] GetExplicitDataOfParameters(ParameterInfo[] parameters)
|
||||||
@@ -162,7 +171,8 @@ public static class DelegateGenerator
|
|||||||
return ExpressionHelper.MethodCaller(type, methodInfo, parameterTypes);
|
return ExpressionHelper.MethodCaller(type, methodInfo, parameterTypes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (returnType == typeof(Task<FlipflopContext>)) // 触发器
|
// else if (returnType == typeof(Task<FlipflopContext)) // 触发器
|
||||||
|
else if (FlipflopFunc.IsTaskOfFlipflop(returnType)) // 触发器
|
||||||
{
|
{
|
||||||
if (parameterCount == 0)
|
if (parameterCount == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
using System.Collections.Concurrent;
|
using Serein.Library.Api;
|
||||||
|
using Serein.Library.Core.NodeFlow;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Serein.NodeFlow;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool
|
namespace Serein.NodeFlow.Tool
|
||||||
{
|
{
|
||||||
@@ -325,12 +326,22 @@ namespace Serein.NodeFlow.Tool
|
|||||||
convertedArgs
|
convertedArgs
|
||||||
);
|
);
|
||||||
|
|
||||||
// 创建 lambda 表达式
|
// 创建 lambda 表达式
|
||||||
var lambda = Expression.Lambda<Func<object, object[], Task<FlipflopContext>>>(
|
var lambda = Expression.Lambda<Func<object, object[], Task<IFlipflopContext>>>(
|
||||||
Expression.Convert(methodCall, typeof(Task<FlipflopContext>)),
|
Expression.Convert(methodCall, typeof(Task<IFlipflopContext>)),
|
||||||
instanceParam,
|
instanceParam,
|
||||||
argsParam
|
argsParam
|
||||||
);
|
);
|
||||||
|
//获取返回类型
|
||||||
|
//var returnType = methodInfo.ReturnType;
|
||||||
|
//var lambda = Expression.Lambda(
|
||||||
|
// typeof(Func<,,>).MakeGenericType(typeof(object), typeof(object[]), returnType),
|
||||||
|
// Expression.Convert(methodCall, returnType),
|
||||||
|
// instanceParam,
|
||||||
|
// argsParam
|
||||||
|
// );
|
||||||
|
|
||||||
|
|
||||||
//var resule = task.DynamicInvoke((object)[Activator.CreateInstance(type), [new DynamicContext(null)]]);
|
//var resule = task.DynamicInvoke((object)[Activator.CreateInstance(type), [new DynamicContext(null)]]);
|
||||||
return lambda.Compile();
|
return lambda.Compile();
|
||||||
}
|
}
|
||||||
|
|||||||
337
NodeFlow/Tool/SerinExpression/ConditionResolver.cs
Normal file
337
NodeFlow/Tool/SerinExpression/ConditionResolver.cs
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SerinExpression
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 条件解析抽象类
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ConditionResolver
|
||||||
|
{
|
||||||
|
public abstract bool Evaluate(object obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PassConditionResolver : ConditionResolver
|
||||||
|
{
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
return Op switch
|
||||||
|
{
|
||||||
|
Operator.Pass => true,
|
||||||
|
Operator.NotPass => false,
|
||||||
|
_ => throw new NotSupportedException("不支持的条件类型")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
Pass,
|
||||||
|
NotPass,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ValueTypeConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 不进行任何操作
|
||||||
|
/// </summary>
|
||||||
|
Node,
|
||||||
|
/// <summary>
|
||||||
|
/// 大于
|
||||||
|
/// </summary>
|
||||||
|
GreaterThan,
|
||||||
|
/// <summary>
|
||||||
|
/// 小于
|
||||||
|
/// </summary>
|
||||||
|
LessThan,
|
||||||
|
/// <summary>
|
||||||
|
/// 等于
|
||||||
|
/// </summary>
|
||||||
|
Equal,
|
||||||
|
/// <summary>
|
||||||
|
/// 大于或等于
|
||||||
|
/// </summary>
|
||||||
|
GreaterThanOrEqual,
|
||||||
|
/// <summary>
|
||||||
|
/// 小于或等于
|
||||||
|
/// </summary>
|
||||||
|
LessThanOrEqual,
|
||||||
|
/// <summary>
|
||||||
|
/// 在两者之间
|
||||||
|
/// </summary>
|
||||||
|
InRange,
|
||||||
|
/// <summary>
|
||||||
|
/// 不在两者之间
|
||||||
|
/// </summary>
|
||||||
|
OutOfRange
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
public T Value { get; set; }
|
||||||
|
public T RangeStart { get; set; }
|
||||||
|
public T RangeEnd { get; set; }
|
||||||
|
|
||||||
|
public string ArithmeticExpression { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
if (obj is T typedObj)
|
||||||
|
{
|
||||||
|
double numericValue = Convert.ToDouble(typedObj);
|
||||||
|
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
||||||
|
{
|
||||||
|
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T));
|
||||||
|
|
||||||
|
return Op switch
|
||||||
|
{
|
||||||
|
Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0,
|
||||||
|
Operator.LessThan => evaluatedValue.CompareTo(Value) < 0,
|
||||||
|
Operator.Equal => evaluatedValue.CompareTo(Value) == 0,
|
||||||
|
Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0,
|
||||||
|
Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0,
|
||||||
|
Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0,
|
||||||
|
Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0,
|
||||||
|
_ => throw new NotSupportedException("不支持的条件类型")
|
||||||
|
};
|
||||||
|
/* switch (Op)
|
||||||
|
{
|
||||||
|
case Operator.GreaterThan:
|
||||||
|
return evaluatedValue.CompareTo(Value) > 0;
|
||||||
|
case Operator.LessThan:
|
||||||
|
return evaluatedValue.CompareTo(Value) < 0;
|
||||||
|
case Operator.Equal:
|
||||||
|
return evaluatedValue.CompareTo(Value) == 0;
|
||||||
|
case Operator.GreaterThanOrEqual:
|
||||||
|
return evaluatedValue.CompareTo(Value) >= 0;
|
||||||
|
case Operator.LessThanOrEqual:
|
||||||
|
return evaluatedValue.CompareTo(Value) <= 0;
|
||||||
|
case Operator.InRange:
|
||||||
|
return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
|
||||||
|
case Operator.OutOfRange:
|
||||||
|
return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BoolConditionResolver : ConditionResolver
|
||||||
|
{
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是
|
||||||
|
/// </summary>
|
||||||
|
Is
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
public bool Value { get; set; }
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (obj is bool boolObj)
|
||||||
|
{
|
||||||
|
return boolObj == Value;
|
||||||
|
/*switch (Op)
|
||||||
|
{
|
||||||
|
case Operator.Is:
|
||||||
|
return boolObj == Value;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StringConditionResolver : ConditionResolver
|
||||||
|
{
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 出现过
|
||||||
|
/// </summary>
|
||||||
|
Contains,
|
||||||
|
/// <summary>
|
||||||
|
/// 没有出现过
|
||||||
|
/// </summary>
|
||||||
|
DoesNotContain,
|
||||||
|
/// <summary>
|
||||||
|
/// 相等
|
||||||
|
/// </summary>
|
||||||
|
Equal,
|
||||||
|
/// <summary>
|
||||||
|
/// 不相等
|
||||||
|
/// </summary>
|
||||||
|
NotEqual,
|
||||||
|
/// <summary>
|
||||||
|
/// 起始字符串等于
|
||||||
|
/// </summary>
|
||||||
|
StartsWith,
|
||||||
|
/// <summary>
|
||||||
|
/// 结束字符串等于
|
||||||
|
/// </summary>
|
||||||
|
EndsWith
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
if (obj is string strObj)
|
||||||
|
{
|
||||||
|
return Op switch
|
||||||
|
{
|
||||||
|
Operator.Contains => strObj.Contains(Value),
|
||||||
|
Operator.DoesNotContain => !strObj.Contains(Value),
|
||||||
|
Operator.Equal => strObj == Value,
|
||||||
|
Operator.NotEqual => strObj != Value,
|
||||||
|
Operator.StartsWith => strObj.StartsWith(Value),
|
||||||
|
Operator.EndsWith => strObj.EndsWith(Value),
|
||||||
|
_ => throw new NotSupportedException("不支持的条件类型"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/* switch (Op)
|
||||||
|
{
|
||||||
|
case Operator.Contains:
|
||||||
|
return strObj.Contains(Value);
|
||||||
|
case Operator.DoesNotContain:
|
||||||
|
return !strObj.Contains(Value);
|
||||||
|
case Operator.Equal:
|
||||||
|
return strObj == Value;
|
||||||
|
case Operator.NotEqual:
|
||||||
|
return strObj != Value;
|
||||||
|
case Operator.StartsWith:
|
||||||
|
return strObj.StartsWith(Value);
|
||||||
|
case Operator.EndsWith:
|
||||||
|
return strObj.EndsWith(Value);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class MemberConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
//public string MemberPath { get; set; }
|
||||||
|
public ValueTypeConditionResolver<T>.Operator Op { get; set; }
|
||||||
|
public object? TargetObj { get; set; }
|
||||||
|
public T Value { get; set; }
|
||||||
|
|
||||||
|
public string ArithmeticExpression { get; set; }
|
||||||
|
|
||||||
|
public override bool Evaluate(object? obj)
|
||||||
|
{
|
||||||
|
//object? memberValue = GetMemberValue(obj, MemberPath);
|
||||||
|
if (TargetObj is T typedObj)
|
||||||
|
{
|
||||||
|
return new ValueTypeConditionResolver<T>
|
||||||
|
{
|
||||||
|
Op = Op,
|
||||||
|
Value = Value,
|
||||||
|
ArithmeticExpression = ArithmeticExpression,
|
||||||
|
}.Evaluate(typedObj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//private object? GetMemberValue(object? obj, string memberPath)
|
||||||
|
//{
|
||||||
|
// string[] members = memberPath[1..].Split('.');
|
||||||
|
// foreach (var member in members)
|
||||||
|
// {
|
||||||
|
// if (obj == null) return null;
|
||||||
|
// Type type = obj.GetType();
|
||||||
|
// PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
|
// FieldInfo? fieldInfo = type.GetField(member);
|
||||||
|
// if (propertyInfo != null)
|
||||||
|
// obj = propertyInfo.GetValue(obj);
|
||||||
|
// else if (fieldInfo != null)
|
||||||
|
// obj = fieldInfo.GetValue(obj);
|
||||||
|
// else
|
||||||
|
// throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
||||||
|
// }
|
||||||
|
// return obj;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MemberStringConditionResolver : ConditionResolver
|
||||||
|
{
|
||||||
|
|
||||||
|
public string MemberPath { get; set; }
|
||||||
|
|
||||||
|
public StringConditionResolver.Operator Op { get; set; }
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
object memberValue = GetMemberValue(obj, MemberPath);
|
||||||
|
if (memberValue is string strObj)
|
||||||
|
{
|
||||||
|
return new StringConditionResolver
|
||||||
|
{
|
||||||
|
Op = Op,
|
||||||
|
Value = Value
|
||||||
|
}.Evaluate(strObj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object GetMemberValue(object? obj, string memberPath)
|
||||||
|
{
|
||||||
|
string[] members = memberPath[1..].Split('.');
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
Type type = obj.GetType();
|
||||||
|
PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
|
FieldInfo? fieldInfo = type.GetField(member);
|
||||||
|
if (propertyInfo != null)
|
||||||
|
obj = propertyInfo.GetValue(obj);
|
||||||
|
else if (fieldInfo != null)
|
||||||
|
obj = fieldInfo.GetValue(obj);
|
||||||
|
else
|
||||||
|
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static string GetArithmeticExpression(string part)
|
||||||
|
{
|
||||||
|
int startIndex = part.IndexOf('[');
|
||||||
|
int endIndex = part.IndexOf(']');
|
||||||
|
if (startIndex >= 0 && endIndex > startIndex)
|
||||||
|
{
|
||||||
|
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
341
NodeFlow/Tool/SerinExpression/SerinConditionParser.cs
Normal file
341
NodeFlow/Tool/SerinExpression/SerinConditionParser.cs
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SerinExpression
|
||||||
|
{
|
||||||
|
|
||||||
|
public class SerinConditionParser
|
||||||
|
{
|
||||||
|
public static bool To<T>(T data, string expression)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
return ConditionParse(data, expression).Evaluate(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConditionResolver ConditionParse(object data, string expression)
|
||||||
|
{
|
||||||
|
if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
||||||
|
{
|
||||||
|
return ParseObjectExpression(data, expression);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ParseSimpleExpression(data, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//bool ContainsArithmeticOperators(string expression)
|
||||||
|
//{
|
||||||
|
// return expression.Contains('+') || expression.Contains('-') || expression.Contains('*') || expression.Contains('/');
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取计算表达式的部分
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="part"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string GetArithmeticExpression(string part)
|
||||||
|
{
|
||||||
|
int startIndex = part.IndexOf('[');
|
||||||
|
int endIndex = part.IndexOf(']');
|
||||||
|
if (startIndex >= 0 && endIndex > startIndex)
|
||||||
|
{
|
||||||
|
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取对象指定名称的成员
|
||||||
|
/// </summary>
|
||||||
|
private static object? GetMemberValue(object? obj, string memberPath)
|
||||||
|
{
|
||||||
|
string[] members = memberPath[1..].Split('.');
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
if (obj == null) return null;
|
||||||
|
Type type = obj.GetType();
|
||||||
|
PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
|
FieldInfo? fieldInfo = type.GetField(member);
|
||||||
|
if (propertyInfo != null)
|
||||||
|
obj = propertyInfo.GetValue(obj);
|
||||||
|
else if (fieldInfo != null)
|
||||||
|
obj = fieldInfo.GetValue(obj);
|
||||||
|
else
|
||||||
|
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 解析对象表达式
|
||||||
|
/// </summary>
|
||||||
|
private static ConditionResolver ParseObjectExpression(object data, string expression)
|
||||||
|
{
|
||||||
|
var parts = expression.Split(' ');
|
||||||
|
string operatorStr = parts[0];
|
||||||
|
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
||||||
|
|
||||||
|
int typeStartIndex = expression.IndexOf('<');
|
||||||
|
int typeEndIndex = expression.IndexOf('>');
|
||||||
|
|
||||||
|
string memberPath;
|
||||||
|
Type type;
|
||||||
|
object? targetObj;
|
||||||
|
if (typeStartIndex + typeStartIndex == -2)
|
||||||
|
{
|
||||||
|
memberPath = operatorStr;
|
||||||
|
targetObj = GetMemberValue(data, operatorStr);
|
||||||
|
|
||||||
|
type = targetObj.GetType();
|
||||||
|
|
||||||
|
operatorStr = parts[1].ToLower();
|
||||||
|
valueStr = string.Join(' ', parts.Skip(2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (typeStartIndex >= typeEndIndex)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("无效的表达式格式");
|
||||||
|
}
|
||||||
|
memberPath = expression.Substring(0, typeStartIndex).Trim();
|
||||||
|
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1).Trim().ToLower();
|
||||||
|
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
|
||||||
|
if (parts.Length == 3)
|
||||||
|
{
|
||||||
|
operatorStr = parts[1].ToLower();
|
||||||
|
valueStr = string.Join(' ', parts.Skip(2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
operatorStr = parts[0].ToLower();
|
||||||
|
valueStr = string.Join(' ', parts.Skip(1));
|
||||||
|
}
|
||||||
|
targetObj = GetMemberValue(data, memberPath);
|
||||||
|
|
||||||
|
Type? tempType = typeStr switch
|
||||||
|
{
|
||||||
|
"int" => typeof(int),
|
||||||
|
"double" => typeof(double),
|
||||||
|
"bool" => typeof(bool),
|
||||||
|
"string" => typeof(string),
|
||||||
|
_ => Type.GetType(typeStr)
|
||||||
|
};
|
||||||
|
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (type == typeof(int))
|
||||||
|
{
|
||||||
|
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
return new MemberConditionResolver<int>
|
||||||
|
{
|
||||||
|
TargetObj = targetObj,
|
||||||
|
//MemberPath = memberPath,
|
||||||
|
Op = ParseValueTypeOperator<int>(operatorStr),
|
||||||
|
Value = value,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (type == typeof(double))
|
||||||
|
{
|
||||||
|
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
return new MemberConditionResolver<double>
|
||||||
|
{
|
||||||
|
//MemberPath = memberPath,
|
||||||
|
TargetObj = targetObj,
|
||||||
|
Op = ParseValueTypeOperator<double>(operatorStr),
|
||||||
|
Value = value,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (type == typeof(bool))
|
||||||
|
{
|
||||||
|
return new MemberConditionResolver<bool>
|
||||||
|
{
|
||||||
|
//MemberPath = memberPath,
|
||||||
|
TargetObj = targetObj,
|
||||||
|
Op = (ValueTypeConditionResolver<bool>.Operator)ParseBoolOperator(operatorStr)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (type == typeof(string))
|
||||||
|
{
|
||||||
|
return new MemberStringConditionResolver
|
||||||
|
{
|
||||||
|
MemberPath = memberPath,
|
||||||
|
Op = ParseStringOperator(operatorStr),
|
||||||
|
Value = valueStr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException($"Type {type} is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConditionResolver ParseSimpleExpression(object data, string expression)
|
||||||
|
{
|
||||||
|
if ("pass".Equals(expression.ToLower()))
|
||||||
|
{
|
||||||
|
return new PassConditionResolver
|
||||||
|
{
|
||||||
|
Op = PassConditionResolver.Operator.Pass,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ("not pass".Equals(expression.ToLower()))
|
||||||
|
{
|
||||||
|
return new PassConditionResolver
|
||||||
|
{
|
||||||
|
Op = PassConditionResolver.Operator.NotPass,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ("!pass".Equals(expression.ToLower()))
|
||||||
|
{
|
||||||
|
return new PassConditionResolver
|
||||||
|
{
|
||||||
|
Op = PassConditionResolver.Operator.NotPass,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var parts = expression.Split(' ');
|
||||||
|
|
||||||
|
if (parts.Length < 2)
|
||||||
|
throw new ArgumentException("无效的表达式格式。");
|
||||||
|
|
||||||
|
//string typeStr = parts[0];
|
||||||
|
string operatorStr = parts[0];
|
||||||
|
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
||||||
|
|
||||||
|
Type type = data.GetType();//Type.GetType(typeStr);
|
||||||
|
if (type == typeof(int))
|
||||||
|
{
|
||||||
|
var op = ParseValueTypeOperator<int>(operatorStr);
|
||||||
|
if (op == ValueTypeConditionResolver<int>.Operator.InRange || op == ValueTypeConditionResolver<int>.Operator.OutOfRange)
|
||||||
|
{
|
||||||
|
var temp = valueStr.Split('-');
|
||||||
|
if (temp.Length < 2)
|
||||||
|
throw new ArgumentException($"范围无效:{valueStr}。");
|
||||||
|
int rangeStart = int.Parse(temp[0], CultureInfo.InvariantCulture);
|
||||||
|
int rangeEnd = int.Parse(temp[1], CultureInfo.InvariantCulture);
|
||||||
|
return new ValueTypeConditionResolver<int>
|
||||||
|
{
|
||||||
|
Op = op,
|
||||||
|
RangeStart = rangeStart,
|
||||||
|
RangeEnd = rangeEnd,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
return new ValueTypeConditionResolver<int>
|
||||||
|
{
|
||||||
|
Op = op,
|
||||||
|
Value = value,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (type == typeof(double))
|
||||||
|
{
|
||||||
|
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
return new ValueTypeConditionResolver<double>
|
||||||
|
{
|
||||||
|
Op = ParseValueTypeOperator<double>(operatorStr),
|
||||||
|
Value = value,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (type == typeof(bool))
|
||||||
|
{
|
||||||
|
bool value = bool.Parse(valueStr);
|
||||||
|
return new BoolConditionResolver
|
||||||
|
{
|
||||||
|
Op = ParseBoolOperator(operatorStr),
|
||||||
|
Value = value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (type == typeof(string))
|
||||||
|
{
|
||||||
|
return new StringConditionResolver
|
||||||
|
{
|
||||||
|
Op = ParseStringOperator(operatorStr),
|
||||||
|
Value = valueStr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException($"Type {type} is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static ValueTypeConditionResolver<T>.Operator ParseValueTypeOperator<T>(string operatorStr) where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
return operatorStr switch
|
||||||
|
{
|
||||||
|
">" => ValueTypeConditionResolver<T>.Operator.GreaterThan,
|
||||||
|
"<" => ValueTypeConditionResolver<T>.Operator.LessThan,
|
||||||
|
"=" => ValueTypeConditionResolver<T>.Operator.Equal,
|
||||||
|
"==" => ValueTypeConditionResolver<T>.Operator.Equal,
|
||||||
|
">=" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
||||||
|
"≥" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
||||||
|
"<=" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
||||||
|
"≤" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
||||||
|
"equals" => ValueTypeConditionResolver<T>.Operator.Equal,
|
||||||
|
"in" => ValueTypeConditionResolver<T>.Operator.InRange,
|
||||||
|
"!in" => ValueTypeConditionResolver<T>.Operator.OutOfRange,
|
||||||
|
_ => throw new ArgumentException($"Invalid operator {operatorStr} for value type.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr)
|
||||||
|
{
|
||||||
|
return operatorStr switch
|
||||||
|
{
|
||||||
|
"is" => BoolConditionResolver.Operator.Is,
|
||||||
|
"==" => BoolConditionResolver.Operator.Is,
|
||||||
|
"equals" => BoolConditionResolver.Operator.Is,
|
||||||
|
//"isFalse" => BoolConditionNode.Operator.IsFalse,
|
||||||
|
_ => throw new ArgumentException($"Invalid operator {operatorStr} for bool type.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StringConditionResolver.Operator ParseStringOperator(string operatorStr)
|
||||||
|
{
|
||||||
|
return operatorStr switch
|
||||||
|
{
|
||||||
|
"c" => StringConditionResolver.Operator.Contains,
|
||||||
|
"nc" => StringConditionResolver.Operator.DoesNotContain,
|
||||||
|
"sw" => StringConditionResolver.Operator.StartsWith,
|
||||||
|
"ew" => StringConditionResolver.Operator.EndsWith,
|
||||||
|
|
||||||
|
"contains" => StringConditionResolver.Operator.Contains,
|
||||||
|
"doesNotContain" => StringConditionResolver.Operator.DoesNotContain,
|
||||||
|
"equals" => StringConditionResolver.Operator.Equal,
|
||||||
|
"==" => StringConditionResolver.Operator.Equal,
|
||||||
|
"notEquals" => StringConditionResolver.Operator.NotEqual,
|
||||||
|
"!=" => StringConditionResolver.Operator.NotEqual,
|
||||||
|
"startsWith" => StringConditionResolver.Operator.StartsWith,
|
||||||
|
"endsWith" => StringConditionResolver.Operator.EndsWith,
|
||||||
|
_ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
216
NodeFlow/Tool/SerinExpression/SerinExpressionEvaluator.cs
Normal file
216
NodeFlow/Tool/SerinExpression/SerinExpressionEvaluator.cs
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SerinExpression
|
||||||
|
{
|
||||||
|
public class SerinArithmeticExpressionEvaluator
|
||||||
|
{
|
||||||
|
private static readonly DataTable table = new DataTable();
|
||||||
|
|
||||||
|
public static double Evaluate(string expression, double inputValue)
|
||||||
|
{
|
||||||
|
// 替换占位符@为输入值
|
||||||
|
expression = expression.Replace("@", inputValue.ToString());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 使用 DataTable.Compute 方法计算表达式
|
||||||
|
var result = table.Compute(expression, string.Empty);
|
||||||
|
return Convert.ToDouble(result);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid arithmetic expression.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SerinExpressionEvaluator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expression">表达式</param>
|
||||||
|
/// <param name="targetObJ">操作对象</param>
|
||||||
|
/// <param name="isChange">是否改变了对象(get语法)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
|
/// <exception cref="NotSupportedException"></exception>
|
||||||
|
public static object Evaluate(string expression, object targetObJ, out bool isChange)
|
||||||
|
{
|
||||||
|
var parts = expression.Split([' '], 2);
|
||||||
|
if (parts.Length != 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid expression format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var operation = parts[0].ToLower();
|
||||||
|
var operand = parts[1][0] == '.' ? parts[1][1..] : parts[1];
|
||||||
|
|
||||||
|
var result = operation switch
|
||||||
|
{
|
||||||
|
"@num" => ComputedNumber(targetObJ, operand),
|
||||||
|
"@call" => InvokeMethod(targetObJ, operand),
|
||||||
|
"@get" => GetMember(targetObJ, operand),
|
||||||
|
"@set" => SetMember(targetObJ, operand),
|
||||||
|
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
||||||
|
};
|
||||||
|
|
||||||
|
isChange = operation switch
|
||||||
|
{
|
||||||
|
"@num" => true,
|
||||||
|
"@call" => true,
|
||||||
|
"@get" => true,
|
||||||
|
"@set" => false,
|
||||||
|
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static readonly char[] separator = ['(', ')'];
|
||||||
|
private static readonly char[] separatorArray = [','];
|
||||||
|
|
||||||
|
private static object InvokeMethod(object target, string methodCall)
|
||||||
|
{
|
||||||
|
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (methodParts.Length != 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid method call format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodName = methodParts[0];
|
||||||
|
var parameterList = methodParts[1];
|
||||||
|
var parameters = parameterList.Split(separatorArray, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(p => p.Trim())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var method = target.GetType().GetMethod(methodName);
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Method {methodName} not found on target.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var parameterValues = method.GetParameters()
|
||||||
|
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
return method.Invoke(target, parameterValues);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GetMember(object target, string memberPath)
|
||||||
|
{
|
||||||
|
var members = memberPath.Split('.');
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (target == null) return null;
|
||||||
|
|
||||||
|
|
||||||
|
var property = target.GetType().GetProperty(member);
|
||||||
|
if (property != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
target = property.GetValue(target);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var field = target.GetType().GetField(member);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
target = field.GetValue(target);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Member {member} not found on target.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return target;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object SetMember(object target, string assignment)
|
||||||
|
{
|
||||||
|
var parts = assignment.Split(new[] { '=' }, 2);
|
||||||
|
if (parts.Length != 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid assignment format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var memberPath = parts[0].Trim();
|
||||||
|
var value = parts[1].Trim();
|
||||||
|
|
||||||
|
var members = memberPath.Split('.');
|
||||||
|
for (int i = 0; i < members.Length - 1; i++)
|
||||||
|
{
|
||||||
|
var member = members[i];
|
||||||
|
|
||||||
|
var property = target.GetType().GetProperty(member);
|
||||||
|
|
||||||
|
if (property != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
target = property.GetValue(target);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var field = target.GetType().GetField(member);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
target = field.GetValue(target);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Member {member} not found on target.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastMember = members.Last();
|
||||||
|
|
||||||
|
var lastProperty = target.GetType().GetProperty(lastMember);
|
||||||
|
|
||||||
|
if (lastProperty != null)
|
||||||
|
{
|
||||||
|
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
||||||
|
lastProperty.SetValue(target, convertedValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var lastField = target.GetType().GetField(lastMember);
|
||||||
|
if (lastField != null)
|
||||||
|
{
|
||||||
|
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
|
||||||
|
lastField.SetValue(target, convertedValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Member {lastMember} not found on target.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double ComputedNumber(object value, string expression)
|
||||||
|
{
|
||||||
|
double numericValue = Convert.ToDouble(value);
|
||||||
|
if (!string.IsNullOrEmpty(expression))
|
||||||
|
{
|
||||||
|
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return numericValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,8 @@ namespace Serein.NodeFlow.Tool
|
|||||||
{
|
{
|
||||||
//public ConcurrentDictionary<TSignal, Queue<TaskCompletionSource<object>>> TcsEvent { get; } = new();
|
//public ConcurrentDictionary<TSignal, Queue<TaskCompletionSource<object>>> TcsEvent { get; } = new();
|
||||||
public ConcurrentDictionary<TSignal, TaskCompletionSource<object>> TcsEvent { get; } = new();
|
public ConcurrentDictionary<TSignal, TaskCompletionSource<object>> TcsEvent { get; } = new();
|
||||||
public ConcurrentDictionary<TSignal, object> TcsValue { get; } = new();
|
|
||||||
|
public ConcurrentDictionary<TSignal, object> TcsLock { get; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 触发信号
|
/// 触发信号
|
||||||
@@ -28,30 +29,36 @@ namespace Serein.NodeFlow.Tool
|
|||||||
/// <returns>是否成功触发</returns>
|
/// <returns>是否成功触发</returns>
|
||||||
public bool TriggerSignal<T>(TSignal signal, T value)
|
public bool TriggerSignal<T>(TSignal signal, T value)
|
||||||
{
|
{
|
||||||
if (TcsEvent.TryRemove(signal, out var waitTcs))
|
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
||||||
|
lock (tcsLock)
|
||||||
{
|
{
|
||||||
waitTcs.SetResult(value);
|
if (TcsEvent.TryRemove(signal, out var waitTcs))
|
||||||
return true;
|
{
|
||||||
|
waitTcs.SetResult(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskCompletionSource<object> CreateTcs(TSignal signal)
|
public TaskCompletionSource<object> CreateTcs(TSignal signal)
|
||||||
{
|
{
|
||||||
var tcs = TcsEvent.GetOrAdd(signal,_ = new TaskCompletionSource<object>());
|
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
||||||
return tcs;
|
lock (tcsLock)
|
||||||
|
{
|
||||||
|
var tcs = TcsEvent.GetOrAdd(signal, new TaskCompletionSource<object>());
|
||||||
|
return tcs;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CancelTask()
|
public void CancelTask()
|
||||||
{
|
{
|
||||||
lock (TcsEvent)
|
foreach (var tcs in TcsEvent.Values)
|
||||||
{
|
{
|
||||||
foreach (var tcs in TcsEvent.Values)
|
tcs.SetException(new TcsSignalException("任务取消"));
|
||||||
{
|
|
||||||
tcs.SetException(new TcsSignalException("任务取消"));
|
|
||||||
}
|
|
||||||
TcsEvent.Clear();
|
|
||||||
}
|
}
|
||||||
|
TcsEvent.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyDll", "MyDll\MyDll.csproj
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.WorkBench", "WorkBench\Serein.WorkBench.csproj", "{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.WorkBench", "WorkBench\Serein.WorkBench.csproj", "{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library.Core", "Library.Core\Serein.Library.Core.csproj", "{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}"
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Module.WAT", "SereinWAT\Serein.Module.WAT.csproj", "{C2F68A15-5D07-4418-87C0-E7402DD9F83A}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.NodeFlow", "NodeFlow\Serein.NodeFlow.csproj", "{7B51A19A-88AB-471E-BCE3-3888C67C936D}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.NodeFlow", "NodeFlow\Serein.NodeFlow.csproj", "{7B51A19A-88AB-471E-BCE3-3888C67C936D}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -18,6 +16,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.Framework", "Library.Framework\Serein.Library.Framework.csproj", "{73B272E8-222D-4D08-A030-F1E1DB70B9D1}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -36,14 +38,18 @@ Global
|
|||||||
{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{7B51A19A-88AB-471E-BCE3-3888C67C936D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{7B51A19A-88AB-471E-BCE3-3888C67C936D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{7B51A19A-88AB-471E-BCE3-3888C67C936D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{7B51A19A-88AB-471E-BCE3-3888C67C936D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{7B51A19A-88AB-471E-BCE3-3888C67C936D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{7B51A19A-88AB-471E-BCE3-3888C67C936D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{7B51A19A-88AB-471E-BCE3-3888C67C936D}.Release|Any CPU.Build.0 = Release|Any CPU
|
{7B51A19A-88AB-471E-BCE3-3888C67C936D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{73B272E8-222D-4D08-A030-F1E1DB70B9D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{73B272E8-222D-4D08-A030-F1E1DB70B9D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{73B272E8-222D-4D08-A030-F1E1DB70B9D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{73B272E8-222D-4D08-A030-F1E1DB70B9D1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using OpenQA.Selenium.Edge;
|
|||||||
using OpenQA.Selenium.Firefox;
|
using OpenQA.Selenium.Firefox;
|
||||||
using OpenQA.Selenium.IE;
|
using OpenQA.Selenium.IE;
|
||||||
using Serein.NodeFlow;
|
using Serein.NodeFlow;
|
||||||
|
using Serein.NodeFlow.Tool;
|
||||||
using static System.Net.Mime.MediaTypeNames;
|
using static System.Net.Mime.MediaTypeNames;
|
||||||
|
|
||||||
namespace Serein.Module
|
namespace Serein.Module
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ namespace Serein.WorkBench
|
|||||||
Shutdown(); // 关闭应用程序
|
Shutdown(); // 关闭应用程序
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (1 == 1)
|
else if (1 == 11)
|
||||||
{
|
{
|
||||||
string filePath = @"F:\临时\project\U9 project.dnf";
|
string filePath = @"F:\临时\project\U9 project.dnf";
|
||||||
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
|
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
|
||||||
|
|||||||
@@ -93,6 +93,10 @@
|
|||||||
x:Name="FlowChartCanvas"
|
x:Name="FlowChartCanvas"
|
||||||
Background="#D9FFEA"
|
Background="#D9FFEA"
|
||||||
AllowDrop="True"
|
AllowDrop="True"
|
||||||
|
Width="1000"
|
||||||
|
Height="700"
|
||||||
|
MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown"
|
||||||
|
MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp"
|
||||||
MouseDown="FlowChartCanvas_MouseDown"
|
MouseDown="FlowChartCanvas_MouseDown"
|
||||||
MouseMove="FlowChartCanvas_MouseMove"
|
MouseMove="FlowChartCanvas_MouseMove"
|
||||||
MouseUp="FlowChartCanvas_MouseUp"
|
MouseUp="FlowChartCanvas_MouseUp"
|
||||||
@@ -100,6 +104,14 @@
|
|||||||
Drop="FlowChartCanvas_Drop"
|
Drop="FlowChartCanvas_Drop"
|
||||||
DragOver="FlowChartCanvas_DragOver">
|
DragOver="FlowChartCanvas_DragOver">
|
||||||
|
|
||||||
|
<Rectangle x:Name="SelectionRectangle"
|
||||||
|
Stroke="Blue"
|
||||||
|
StrokeThickness="2"
|
||||||
|
Fill="LightBlue"
|
||||||
|
Opacity="0.5"
|
||||||
|
Panel.ZIndex="999999"
|
||||||
|
Visibility="Collapsed"/>
|
||||||
|
|
||||||
<!-- Top-Left Thumb -->
|
<!-- Top-Left Thumb -->
|
||||||
<!--<Thumb x:Name="TopLeftThumb"
|
<!--<Thumb x:Name="TopLeftThumb"
|
||||||
Width="10" Height="10"
|
Width="10" Height="10"
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Serein.Library.IOC;
|
using Serein.Library.Attributes;
|
||||||
|
using Serein.Library.Utils;
|
||||||
using Serein.NodeFlow;
|
using Serein.NodeFlow;
|
||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
using Serein.NodeFlow.Tool;
|
using Serein.NodeFlow.Tool;
|
||||||
using Serein.WorkBench.Node.View;
|
using Serein.WorkBench.Node.View;
|
||||||
|
using Serein.WorkBench.Node.ViewModel;
|
||||||
using Serein.WorkBench.Themes;
|
using Serein.WorkBench.Themes;
|
||||||
using Serein.WorkBench.tool;
|
using Serein.WorkBench.tool;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Security.Cryptography.Xml;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Controls.Primitives;
|
using System.Windows.Controls.Primitives;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Animation;
|
using System.Windows.Media.Animation;
|
||||||
using System.Windows.Media.Media3D;
|
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using System.Xml.Linq;
|
|
||||||
using DataObject = System.Windows.DataObject;
|
using DataObject = System.Windows.DataObject;
|
||||||
|
|
||||||
namespace Serein.WorkBench
|
namespace Serein.WorkBench
|
||||||
@@ -126,7 +125,7 @@ namespace Serein.WorkBench
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 一种轻量的IOC容器
|
/// 一种轻量的IOC容器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ServiceContainer ServiceContainer { get; } = new ServiceContainer();
|
private SereinIoc ServiceContainer { get; } = new SereinIoc();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 全局捕获Console输出事件,打印在这个窗体里面
|
/// 全局捕获Console输出事件,打印在这个窗体里面
|
||||||
@@ -160,6 +159,20 @@ namespace Serein.WorkBench
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly List<SingleFlipflopNode> flipflopNodes = [];
|
private readonly List<SingleFlipflopNode> flipflopNodes = [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 流程启动器
|
||||||
|
/// </summary>
|
||||||
|
private NodeFlowStarter nodeFlowStarter;
|
||||||
|
|
||||||
|
#region 与画布相关的字段
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前选取的控件
|
||||||
|
/// </summary>
|
||||||
|
private readonly List<NodeControlBase> selectControls = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 记录拖动开始时的鼠标位置
|
/// 记录拖动开始时的鼠标位置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -185,6 +198,10 @@ namespace Serein.WorkBench
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool IsConnecting;
|
private bool IsConnecting;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// 标记是否正在尝试选取控件
|
||||||
|
/// </summary>
|
||||||
|
private bool IsSelectControl;
|
||||||
|
/// <summary>
|
||||||
/// 标记是否正在拖动控件
|
/// 标记是否正在拖动控件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool IsControlDragging;
|
private bool IsControlDragging;
|
||||||
@@ -203,12 +220,10 @@ namespace Serein.WorkBench
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 平移画布
|
/// 平移画布
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private TranslateTransform translateTransform;
|
private TranslateTransform translateTransform;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 流程起点
|
|
||||||
/// </summary>
|
|
||||||
private NodeFlowStarter nodeFlowStarter;
|
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
|
|
||||||
@@ -259,10 +274,10 @@ namespace Serein.WorkBench
|
|||||||
LoadDll(nf); // 加载DLL
|
LoadDll(nf); // 加载DLL
|
||||||
LoadNodeControls(nf); // 加载节点
|
LoadNodeControls(nf); // 加载节点
|
||||||
|
|
||||||
var startNode = nodeControls.FirstOrDefault(item => item.Node.Guid.Equals(nf.startNode));
|
var startNode = nodeControls.FirstOrDefault(control => control.ViewModel.Node.Guid.Equals(nf.startNode));
|
||||||
if (startNode != null)
|
if (startNode != null)
|
||||||
{
|
{
|
||||||
startNode.Node.IsStart = true;
|
startNode.ViewModel.Node.IsStart = true;
|
||||||
SetIsStartBlock(startNode);
|
SetIsStartBlock(startNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,7 +347,7 @@ namespace Serein.WorkBench
|
|||||||
}
|
}
|
||||||
else if (regionControl is ActionRegionControl actionRegionControl)
|
else if (regionControl is ActionRegionControl actionRegionControl)
|
||||||
{
|
{
|
||||||
actionRegionControl.AddAction(nodeControl);
|
//actionRegionControl.AddAction(nodeControl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -396,22 +411,22 @@ namespace Serein.WorkBench
|
|||||||
{
|
{
|
||||||
if (connectionType == ConnectionType.IsSucceed)
|
if (connectionType == ConnectionType.IsSucceed)
|
||||||
{
|
{
|
||||||
fromNode.Node.SucceedBranch.Add(toNode.Node);
|
fromNode.ViewModel.Node.SucceedBranch.Add(toNode.ViewModel.Node);
|
||||||
}
|
}
|
||||||
else if (connectionType == ConnectionType.IsFail)
|
else if (connectionType == ConnectionType.IsFail)
|
||||||
{
|
{
|
||||||
fromNode.Node.FailBranch.Add(toNode.Node);
|
fromNode.ViewModel.Node.FailBranch.Add(toNode.ViewModel.Node);
|
||||||
}
|
}
|
||||||
else if (connectionType == ConnectionType.IsError)
|
else if (connectionType == ConnectionType.IsError)
|
||||||
{
|
{
|
||||||
fromNode.Node.ErrorBranch.Add(toNode.Node);
|
fromNode.ViewModel.Node.ErrorBranch.Add(toNode.ViewModel.Node);
|
||||||
}
|
}
|
||||||
else if (connectionType == ConnectionType.Upstream)
|
else if (connectionType == ConnectionType.Upstream)
|
||||||
{
|
{
|
||||||
fromNode.Node.UpstreamBranch.Add(toNode.Node);
|
fromNode.ViewModel.Node.UpstreamBranch.Add(toNode.ViewModel.Node);
|
||||||
}
|
}
|
||||||
var connection = new Connection { Start = fromNode, End = toNode, Type = connectionType };
|
var connection = new Connection { Start = fromNode, End = toNode, Type = connectionType };
|
||||||
toNode.Node.PreviousNodes.Add(fromNode.Node);
|
toNode.ViewModel.Node.PreviousNodes.Add(fromNode.ViewModel.Node);
|
||||||
BsControl.Draw(FlowChartCanvas, connection);
|
BsControl.Draw(FlowChartCanvas, connection);
|
||||||
ConfigureLineContextMenu(connection);
|
ConfigureLineContextMenu(connection);
|
||||||
connections.Add(connection);
|
connections.Add(connection);
|
||||||
@@ -465,19 +480,19 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
NodeControlBase control = nodeInfo.type switch
|
NodeControlBase control = nodeInfo.type switch
|
||||||
{
|
{
|
||||||
$"{NodeSpaceName}.{nameof(SingleActionNode)}" => CreateNodeControl<SingleActionNode, ActionNodeControl>(md),
|
$"{NodeSpaceName}.{nameof(SingleActionNode)}" => CreateNodeControl<SingleActionNode, ActionNodeControl,ActionNodeControlViewModel>(md),
|
||||||
$"{NodeSpaceName}.{nameof(SingleFlipflopNode)}" => CreateNodeControl<SingleFlipflopNode, FlipflopNodeControl>(md),
|
$"{NodeSpaceName}.{nameof(SingleFlipflopNode)}" => CreateNodeControl<SingleFlipflopNode, FlipflopNodeControl,FlipflopNodeControlViewModel>(md),
|
||||||
|
|
||||||
$"{NodeSpaceName}.{nameof(SingleConditionNode)}" => CreateNodeControl<SingleConditionNode, ConditionNodeControl>(), // 条件表达式控件
|
$"{NodeSpaceName}.{nameof(SingleConditionNode)}" => CreateNodeControl<SingleConditionNode, ConditionNodeControl,ConditionNodeControlViewModel>(), // 条件表达式控件
|
||||||
$"{NodeSpaceName}.{nameof(SingleExpOpNode)}" => CreateNodeControl<SingleExpOpNode, ExpOpNodeControl>(), // 操作表达式控件
|
$"{NodeSpaceName}.{nameof(SingleExpOpNode)}" => CreateNodeControl<SingleExpOpNode, ExpOpNodeControl,ExpOpNodeViewModel>(), // 操作表达式控件
|
||||||
|
|
||||||
$"{NodeSpaceName}.{nameof(CompositeActionNode)}" => CreateNodeControl<CompositeActionNode, ActionRegionControl>(),
|
//$"{NodeSpaceName}.{nameof(CompositeActionNode)}" => CreateNodeControl<CompositeActionNode, ActionRegionControl>(),
|
||||||
$"{NodeSpaceName}.{nameof(CompositeConditionNode)}" => CreateNodeControl<CompositeConditionNode, ConditionRegionControl>(),
|
$"{NodeSpaceName}.{nameof(CompositeConditionNode)}" => CreateNodeControl<CompositeConditionNode, ConditionRegionControl, ConditionRegionNodeControlViewModel>(),
|
||||||
_ => throw new NotImplementedException($"非预期的节点类型{nodeInfo.type}"),
|
_ => throw new NotImplementedException($"非预期的节点类型{nodeInfo.type}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 如果是触发器,则需要添加到集合中
|
// 如果是触发器,则需要添加到集合中
|
||||||
if (control is FlipflopNodeControl flipflopNodeControl && flipflopNodeControl.Node is SingleFlipflopNode flipflopNode)
|
if (control is FlipflopNodeControl flipflopNodeControl && flipflopNodeControl.ViewModel.Node is SingleFlipflopNode flipflopNode)
|
||||||
{
|
{
|
||||||
var guid = flipflopNode.Guid;
|
var guid = flipflopNode.Guid;
|
||||||
if (!flipflopNodes.Exists(it => it.Guid.Equals(guid)))
|
if (!flipflopNodes.Exists(it => it.Guid.Equals(guid)))
|
||||||
@@ -485,22 +500,22 @@ namespace Serein.WorkBench
|
|||||||
flipflopNodes.Add(flipflopNode);
|
flipflopNodes.Add(flipflopNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var node = control.Node;
|
var node = control.ViewModel.Node;
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
node.Guid = nodeInfo.guid;
|
node.Guid = nodeInfo.guid;
|
||||||
for (int i = 0; i < nodeInfo.parameterData.Length; i++)
|
for (int i = 0; i < nodeInfo.parameterData.Length; i++)
|
||||||
{
|
{
|
||||||
Parameterdata? pd = nodeInfo.parameterData[i];
|
Parameterdata? pd = nodeInfo.parameterData[i];
|
||||||
if (control is ConditionNodeControl conditionNodeControl)
|
if (control is ConditionNodeControl conditionNodeControl && conditionNodeControl.ViewModel is ConditionNodeControlViewModel conditionNodeControlViewModel)
|
||||||
{
|
{
|
||||||
conditionNodeControl.ViewModel.IsCustomData = pd.state;
|
conditionNodeControlViewModel.IsCustomData = pd.state;
|
||||||
conditionNodeControl.ViewModel.CustomData = pd.value;
|
conditionNodeControlViewModel.CustomData = pd.value;
|
||||||
conditionNodeControl.ViewModel.Expression = pd.expression;
|
conditionNodeControlViewModel.Expression = pd.expression;
|
||||||
}
|
}
|
||||||
else if (control is ExpOpNodeControl expOpNodeControl)
|
else if (control is ExpOpNodeControl expOpNodeControl && expOpNodeControl.ViewModel is ExpOpNodeViewModel expOpNodeViewModel)
|
||||||
{
|
{
|
||||||
expOpNodeControl.ViewModel.Expression = pd.expression;
|
expOpNodeViewModel.Expression = pd.expression;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -529,9 +544,10 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
#region 节点控件的创建
|
#region 节点控件的创建
|
||||||
|
|
||||||
private static TControl CreateNodeControl<TNode, TControl>(MethodDetails? methodDetails = null)
|
private static TControl CreateNodeControl<TNode, TControl,TViewModel>(MethodDetails? methodDetails = null)
|
||||||
where TNode : NodeBase
|
where TNode : NodeBase
|
||||||
where TControl : NodeControlBase
|
where TControl : NodeControlBase
|
||||||
|
where TViewModel : NodeControlViewModelBase
|
||||||
{
|
{
|
||||||
var nodeObj = Activator.CreateInstance(typeof(TNode));
|
var nodeObj = Activator.CreateInstance(typeof(TNode));
|
||||||
var nodeBase = nodeObj as NodeBase;
|
var nodeBase = nodeObj as NodeBase;
|
||||||
@@ -551,7 +567,8 @@ namespace Serein.WorkBench
|
|||||||
nodeBase.MethodDetails = md;
|
nodeBase.MethodDetails = md;
|
||||||
}
|
}
|
||||||
|
|
||||||
var controlObj = Activator.CreateInstance(typeof(TControl), [nodeObj] );
|
var viewModel = Activator.CreateInstance(typeof(TViewModel), [nodeObj]);
|
||||||
|
var controlObj = Activator.CreateInstance(typeof(TControl), [viewModel] );
|
||||||
if(controlObj is TControl control)
|
if(controlObj is TControl control)
|
||||||
{
|
{
|
||||||
return control;
|
return control;
|
||||||
@@ -570,7 +587,7 @@ namespace Serein.WorkBench
|
|||||||
{
|
{
|
||||||
var contextMenu = new ContextMenu();
|
var contextMenu = new ContextMenu();
|
||||||
|
|
||||||
if (nodeControl.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void))
|
if (nodeControl.ViewModel.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void))
|
||||||
{
|
{
|
||||||
contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) =>
|
contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) =>
|
||||||
{
|
{
|
||||||
@@ -673,39 +690,37 @@ namespace Serein.WorkBench
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Assembly assembly = Assembly.LoadFrom(dllPath); // 加载DLL文件
|
Assembly assembly = Assembly.LoadFrom(dllPath); // 加载DLL文件
|
||||||
|
Type[] types = assembly.GetTypes(); // 获取程序集中的所有类型
|
||||||
|
|
||||||
|
List<Type> scanTypes = assembly.GetTypes().Where(t => t.GetCustomAttribute<DynamicFlowAttribute>()?.Scan == true ).ToList();
|
||||||
|
//List<(bool, Type)> scanTypes = scanTypesTemp.Select(t => (t.GetCustomAttribute<Serein.Library.Framework.NodeFlow.Tool.DynamicFlowAttribute>()?.Scan == true, t)).ToList();
|
||||||
|
|
||||||
|
//bool isNetFramework = false;
|
||||||
|
//if(scanTypes.Count == 0)
|
||||||
|
//{
|
||||||
|
// scanTypes = assembly.GetTypes().Where(t => t.GetCustomAttribute<Library.Framework.NodeFlow.Tool.DynamicFlowAttribute>()?.Scan == true).ToList();
|
||||||
|
// if(scanTypes.Count == 0)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// isNetFramework = true;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
loadedAssemblies.Add(assembly); // 将加载的程序集添加到列表中
|
loadedAssemblies.Add(assembly); // 将加载的程序集添加到列表中
|
||||||
loadedAssemblyPaths.Add(dllPath); // 记录加载的DLL路径
|
loadedAssemblyPaths.Add(dllPath); // 记录加载的DLL路径
|
||||||
|
|
||||||
Type[] types = assembly.GetTypes(); // 获取程序集中的所有类型
|
|
||||||
|
|
||||||
List<MethodDetails> conditionMethods = [];
|
List<MethodDetails> conditionMethods = [];
|
||||||
List<MethodDetails> actionMethods = [];
|
List<MethodDetails> actionMethods = [];
|
||||||
List<MethodDetails> flipflopMethods = [];
|
List<MethodDetails> flipflopMethods = [];
|
||||||
|
|
||||||
/* // 遍历类型,根据接口分类
|
|
||||||
foreach (Type type in types)
|
|
||||||
{
|
|
||||||
if (typeof(ICondition).IsAssignableFrom(type) && type.IsClass)
|
|
||||||
{
|
|
||||||
conditionTypes.Add(type); // 条件类型
|
|
||||||
}
|
|
||||||
if (typeof(IAction).IsAssignableFrom(type) && type.IsClass)
|
|
||||||
{
|
|
||||||
actionTypes.Add(type); // 动作类型
|
|
||||||
}
|
|
||||||
if (typeof(IState).IsAssignableFrom(type) && type.IsClass)
|
|
||||||
{
|
|
||||||
stateTypes.Add(type); // 状态类型
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
var scanTypes = assembly.GetTypes()
|
// foreach ((bool isNetFramework,Type type) item in scanTypes)
|
||||||
.Where(t => t.GetCustomAttribute<DynamicFlowAttribute>()?.Scan == true).ToList();
|
foreach (var item in scanTypes)
|
||||||
|
|
||||||
foreach (var type in scanTypes)
|
|
||||||
{
|
{
|
||||||
//加载DLL
|
//加载DLL
|
||||||
var dict = DelegateGenerator.GenerateMethodDetails(ServiceContainer, type);
|
var dict = DelegateGenerator.GenerateMethodDetails(ServiceContainer, item, false);
|
||||||
|
|
||||||
foreach (var detail in dict)
|
foreach (var detail in dict)
|
||||||
{
|
{
|
||||||
@@ -713,33 +728,22 @@ namespace Serein.WorkBench
|
|||||||
DllMethodDetails.TryAdd(detail.Key, detail.Value);
|
DllMethodDetails.TryAdd(detail.Key, detail.Value);
|
||||||
|
|
||||||
// 根据 DynamicType 分类
|
// 根据 DynamicType 分类
|
||||||
switch (detail.Value.MethodDynamicType)
|
switch (detail.Value.MethodDynamicType.ToString())
|
||||||
{
|
{
|
||||||
case DynamicNodeType.Condition:
|
case nameof(Serein.Library.Enums.NodeType.Condition):
|
||||||
conditionMethods.Add(detail.Value);
|
conditionMethods.Add(detail.Value);
|
||||||
break;
|
break;
|
||||||
case DynamicNodeType.Action:
|
case nameof(Serein.Library.Enums.NodeType.Action):
|
||||||
actionMethods.Add(detail.Value);
|
actionMethods.Add(detail.Value);
|
||||||
break;
|
break;
|
||||||
case DynamicNodeType.Flipflop:
|
case nameof(Serein.Library.Enums.NodeType.Flipflop):
|
||||||
flipflopMethods.Add(detail.Value);
|
flipflopMethods.Add(detail.Value);
|
||||||
break;
|
break;
|
||||||
//case DynamicNodeType.Init:
|
|
||||||
// initMethods.Add(detail.Value);
|
|
||||||
// break;
|
|
||||||
//case DynamicNodeType.Loading:
|
|
||||||
// loadingMethods.Add(detail.Value);
|
|
||||||
// break;
|
|
||||||
//case DynamicNodeType.Exit:
|
|
||||||
// exitMethods.Add(detail.Value);
|
|
||||||
// break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DictMethodDetail.TryAdd(detail.Key, detail.Value);
|
DictMethodDetail.TryAdd(detail.Key, detail.Value);
|
||||||
// 将委托缓存到全局字典
|
// 将委托缓存到全局字典
|
||||||
DelegateCache.GlobalDicDelegates.TryAdd(detail.Key, detail.Value.MethodDelegate);
|
DelegateCache.GlobalDicDelegates.TryAdd(detail.Key, detail.Value.MethodDelegate);
|
||||||
//globalDicDelegates.TryAdd(kvp.Key, kvp.Value.MethodDelegate);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -941,13 +945,16 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
if (nodeControl != null)
|
if (nodeControl != null)
|
||||||
{
|
{
|
||||||
// 尝试放置节点
|
// 首先判断是否为区域,如果是,则尝试将节点放置在区域中
|
||||||
if (TryPlaceNodeInRegion(nodeControl, dropPosition, e))
|
if (TryPlaceNodeInRegion(nodeControl, dropPosition, e))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
PlaceNodeOnCanvas(nodeControl, dropPosition);
|
{
|
||||||
|
// 放置在画布上
|
||||||
|
PlaceNodeOnCanvas(nodeControl, dropPosition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
@@ -963,7 +970,7 @@ namespace Serein.WorkBench
|
|||||||
return droppedType switch
|
return droppedType switch
|
||||||
{
|
{
|
||||||
Type when typeof(ConditionRegionControl).IsAssignableFrom(droppedType)
|
Type when typeof(ConditionRegionControl).IsAssignableFrom(droppedType)
|
||||||
=> CreateNodeControl<CompositeConditionNode, ConditionRegionControl>(), // 条件区域
|
=> CreateNodeControl<CompositeConditionNode, ConditionRegionControl, ConditionRegionNodeControlViewModel>(), // 条件区域
|
||||||
|
|
||||||
//Type when typeof(CompositeActionNode).IsAssignableFrom(droppedType)
|
//Type when typeof(CompositeActionNode).IsAssignableFrom(droppedType)
|
||||||
// => CreateNodeControl<CompositeActionNode,ActionRegionControl>(), // 动作区域
|
// => CreateNodeControl<CompositeActionNode,ActionRegionControl>(), // 动作区域
|
||||||
@@ -982,9 +989,9 @@ namespace Serein.WorkBench
|
|||||||
return droppedType switch
|
return droppedType switch
|
||||||
{
|
{
|
||||||
Type when typeof(ConditionNodeControl).IsAssignableFrom(droppedType)
|
Type when typeof(ConditionNodeControl).IsAssignableFrom(droppedType)
|
||||||
=> CreateNodeControl<SingleConditionNode, ConditionNodeControl>(), // 条件控件
|
=> CreateNodeControl<SingleConditionNode, ConditionNodeControl,ConditionNodeControlViewModel>(), // 条件控件
|
||||||
Type when typeof(ExpOpNodeControl).IsAssignableFrom(droppedType)
|
Type when typeof(ExpOpNodeControl).IsAssignableFrom(droppedType)
|
||||||
=> CreateNodeControl<SingleExpOpNode, ExpOpNodeControl>(), // 操作表达式控件
|
=> CreateNodeControl<SingleExpOpNode, ExpOpNodeControl,ExpOpNodeViewModel>(), // 操作表达式控件
|
||||||
_ => throw new NotImplementedException("非预期的基础节点类型"),
|
_ => throw new NotImplementedException("非预期的基础节点类型"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1000,13 +1007,13 @@ namespace Serein.WorkBench
|
|||||||
NodeControlBase control = methodDetails.MethodDynamicType switch
|
NodeControlBase control = methodDetails.MethodDynamicType switch
|
||||||
{
|
{
|
||||||
//DynamicNodeType.Condition => CreateNodeControl(typeof(SingleConditionNode), methodDetails), // 单个条件控件
|
//DynamicNodeType.Condition => CreateNodeControl(typeof(SingleConditionNode), methodDetails), // 单个条件控件
|
||||||
DynamicNodeType.Action => CreateNodeControl<SingleActionNode, ActionNodeControl>(methodDetails),// 单个动作控件
|
Serein.Library.Enums.NodeType.Action => CreateNodeControl<SingleActionNode, ActionNodeControl, ActionNodeControlViewModel>(methodDetails),// 单个动作控件
|
||||||
DynamicNodeType.Flipflop => CreateNodeControl<SingleFlipflopNode, FlipflopNodeControl>(methodDetails), // 单个动作控件
|
Serein.Library.Enums.NodeType.Flipflop => CreateNodeControl<SingleFlipflopNode, FlipflopNodeControl, FlipflopNodeControlViewModel>(methodDetails), // 单个动作控件
|
||||||
_ => throw new NotImplementedException("非预期的Dll节点类型"),
|
_ => throw new NotImplementedException("非预期的Dll节点类型"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 如果是触发器,则需要添加到集合中
|
// 如果是触发器,则需要添加到集合中
|
||||||
if (control is FlipflopNodeControl flipflopNodeControl && flipflopNodeControl.Node is SingleFlipflopNode flipflopNode)
|
if (control is FlipflopNodeControl flipflopNodeControl && flipflopNodeControl.ViewModel.Node is SingleFlipflopNode flipflopNode)
|
||||||
{
|
{
|
||||||
var guid = flipflopNode.Guid;
|
var guid = flipflopNode.Guid;
|
||||||
if (!flipflopNodes.Exists(it => it.Guid.Equals(guid)))
|
if (!flipflopNodes.Exists(it => it.Guid.Equals(guid)))
|
||||||
@@ -1032,45 +1039,19 @@ namespace Serein.WorkBench
|
|||||||
if (hitTestResult != null && hitTestResult.VisualHit is UIElement hitElement)
|
if (hitTestResult != null && hitTestResult.VisualHit is UIElement hitElement)
|
||||||
{
|
{
|
||||||
var data = e.Data.GetData(MouseNodeType.BaseNodeType);
|
var data = e.Data.GetData(MouseNodeType.BaseNodeType);
|
||||||
|
if(data is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (data == typeof(ConditionNodeControl))
|
if (data == typeof(ConditionNodeControl))
|
||||||
{
|
{
|
||||||
|
|
||||||
ConditionRegionControl conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement);
|
ConditionRegionControl conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement);
|
||||||
if (conditionRegion != null)
|
if (conditionRegion != null)
|
||||||
{
|
{
|
||||||
conditionRegion.AddCondition(nodeControl);
|
conditionRegion.AddCondition(nodeControl);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//if (e.Data.GetData(MouseNodeType.DllNodeType) is MethodDetails methodDetails)
|
|
||||||
//{
|
|
||||||
// if (methodDetails.MethodDynamicType == DynamicNodeType.Condition)
|
|
||||||
// {
|
|
||||||
// ConditionRegionControl conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement);
|
|
||||||
// if (conditionRegion != null)
|
|
||||||
// {
|
|
||||||
// conditionRegion.AddCondition(nodeControl);
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else if (methodDetails.MethodDynamicType == DynamicNodeType.Action)
|
|
||||||
// {
|
|
||||||
// ActionRegionControl actionRegion = GetParentOfType<ActionRegionControl>(hitElement);
|
|
||||||
// if (actionRegion != null)
|
|
||||||
// {
|
|
||||||
// actionRegion.AddAction(nodeControl);
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1098,7 +1079,7 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获得目标类型的父类
|
/// 判断当前元素是否是泛型类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="element"></param>
|
/// <param name="element"></param>
|
||||||
@@ -1120,19 +1101,7 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 鼠标左键按下事件,关闭所有连接的动画效果
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
/// <param name="e"></param>
|
|
||||||
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
//// 关闭所有连线的动画效果
|
|
||||||
//foreach (var connection in connections)
|
|
||||||
//{
|
|
||||||
// connection.StopAnimation();
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 拖动效果,根据拖放数据是否为指定类型设置拖放效果
|
/// 拖动效果,根据拖放数据是否为指定类型设置拖放效果
|
||||||
@@ -1284,13 +1253,102 @@ namespace Serein.WorkBench
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在画布中按下鼠标左键
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
|
||||||
|
{
|
||||||
|
IsSelectControl = true;
|
||||||
|
|
||||||
|
// 开始选取时,记录鼠标起始点
|
||||||
|
startPoint = e.GetPosition(FlowChartCanvas);
|
||||||
|
|
||||||
|
// 初始化选取矩形的位置和大小
|
||||||
|
Canvas.SetLeft(SelectionRectangle, startPoint.X);
|
||||||
|
Canvas.SetTop(SelectionRectangle, startPoint.Y);
|
||||||
|
SelectionRectangle.Width = 0;
|
||||||
|
SelectionRectangle.Height = 0;
|
||||||
|
|
||||||
|
// 显示选取矩形
|
||||||
|
SelectionRectangle.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
// 捕获鼠标,以便在鼠标移动到Canvas外部时仍能处理事件
|
||||||
|
FlowChartCanvas.CaptureMouse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 在画布中释放鼠标按下,结束选取状态
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (IsSelectControl)
|
||||||
|
{
|
||||||
|
|
||||||
|
IsSelectControl = false;
|
||||||
|
// 释放鼠标捕获
|
||||||
|
FlowChartCanvas.ReleaseMouseCapture();
|
||||||
|
|
||||||
|
// 隐藏选取矩形(如果需要保持选取状态,可以删除此行)
|
||||||
|
SelectionRectangle.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
// 处理选取区域内的元素(例如,获取选取范围内的控件)
|
||||||
|
Rect selectionArea = new Rect(Canvas.GetLeft(SelectionRectangle),
|
||||||
|
Canvas.GetTop(SelectionRectangle),
|
||||||
|
SelectionRectangle.Width,
|
||||||
|
SelectionRectangle.Height);
|
||||||
|
|
||||||
|
|
||||||
|
selectControls.Clear();
|
||||||
|
// 在此处处理选取的逻辑
|
||||||
|
foreach (UIElement element in FlowChartCanvas.Children)
|
||||||
|
{
|
||||||
|
Rect elementBounds = new Rect(Canvas.GetLeft(element), Canvas.GetTop(element),
|
||||||
|
element.RenderSize.Width, element.RenderSize.Height);
|
||||||
|
|
||||||
|
if (selectionArea.Contains(elementBounds))
|
||||||
|
{
|
||||||
|
// 选中元素,执行相应操作
|
||||||
|
if (element is NodeControlBase control)
|
||||||
|
{
|
||||||
|
selectControls.Add(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine($"一共选取了{selectControls.Count}个控件");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 鼠标在画布移动。
|
/// 鼠标在画布移动。
|
||||||
|
/// 选择控件状态下,调整选择框大小
|
||||||
/// 连接状态下,实时更新连接线的终点位置。
|
/// 连接状态下,实时更新连接线的终点位置。
|
||||||
/// 移动画布状态下,移动画布。
|
/// 移动画布状态下,移动画布。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
|
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (IsSelectControl && e.LeftButton == MouseButtonState.Pressed)
|
||||||
|
{
|
||||||
|
// 获取当前鼠标位置
|
||||||
|
Point currentPoint = e.GetPosition(FlowChartCanvas);
|
||||||
|
|
||||||
|
// 更新选取矩形的位置和大小
|
||||||
|
double x = Math.Min(currentPoint.X, startPoint.X);
|
||||||
|
double y = Math.Min(currentPoint.Y, startPoint.Y);
|
||||||
|
double width = Math.Abs(currentPoint.X - startPoint.X);
|
||||||
|
double height = Math.Abs(currentPoint.Y - startPoint.Y);
|
||||||
|
|
||||||
|
Canvas.SetLeft(SelectionRectangle, x);
|
||||||
|
Canvas.SetTop(SelectionRectangle, y);
|
||||||
|
SelectionRectangle.Width = width;
|
||||||
|
SelectionRectangle.Height = height;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsConnecting)
|
if (IsConnecting)
|
||||||
{
|
{
|
||||||
Point position = e.GetPosition(FlowChartCanvas);
|
Point position = e.GetPosition(FlowChartCanvas);
|
||||||
@@ -1384,26 +1442,26 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
if (currentConnectionType == ConnectionType.IsSucceed)
|
if (currentConnectionType == ConnectionType.IsSucceed)
|
||||||
{
|
{
|
||||||
startConnectBlock.Node.SucceedBranch.Add(targetBlock.Node);
|
startConnectBlock.ViewModel.Node.SucceedBranch.Add(targetBlock.ViewModel.Node);
|
||||||
}
|
}
|
||||||
else if (currentConnectionType == ConnectionType.IsFail)
|
else if (currentConnectionType == ConnectionType.IsFail)
|
||||||
{
|
{
|
||||||
startConnectBlock.Node.FailBranch.Add(targetBlock.Node);
|
startConnectBlock.ViewModel.Node.FailBranch.Add(targetBlock.ViewModel.Node);
|
||||||
}
|
}
|
||||||
else if (currentConnectionType == ConnectionType.IsError)
|
else if (currentConnectionType == ConnectionType.IsError)
|
||||||
{
|
{
|
||||||
startConnectBlock.Node.ErrorBranch.Add(targetBlock.Node);
|
startConnectBlock.ViewModel.Node.ErrorBranch.Add(targetBlock.ViewModel.Node);
|
||||||
}
|
}
|
||||||
else if (currentConnectionType == ConnectionType.Upstream)
|
else if (currentConnectionType == ConnectionType.Upstream)
|
||||||
{
|
{
|
||||||
startConnectBlock.Node.UpstreamBranch.Add(targetBlock.Node);
|
startConnectBlock.ViewModel.Node.UpstreamBranch.Add(targetBlock.ViewModel.Node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存连接关系
|
// 保存连接关系
|
||||||
BsControl.Draw(FlowChartCanvas, connection);
|
BsControl.Draw(FlowChartCanvas, connection);
|
||||||
ConfigureLineContextMenu(connection);
|
ConfigureLineContextMenu(connection);
|
||||||
|
|
||||||
targetBlock.Node.PreviousNodes.Add(startConnectBlock.Node); // 将当前发起连接的节点,添加到被连接的节点的上一节点队列。(用于回溯)
|
targetBlock.ViewModel.Node.PreviousNodes.Add(startConnectBlock.ViewModel.Node); // 将当前发起连接的节点,添加到被连接的节点的上一节点队列。(用于回溯)
|
||||||
connections.Add(connection);
|
connections.Add(connection);
|
||||||
}
|
}
|
||||||
EndConnection();
|
EndConnection();
|
||||||
@@ -1461,7 +1519,7 @@ namespace Serein.WorkBench
|
|||||||
/// <param name="nodeControl"></param>
|
/// <param name="nodeControl"></param>
|
||||||
private void DeleteBlock(NodeControlBase nodeControl)
|
private void DeleteBlock(NodeControlBase nodeControl)
|
||||||
{
|
{
|
||||||
if (nodeControl.Node.IsStart)
|
if (nodeControl.ViewModel.Node.IsStart)
|
||||||
{
|
{
|
||||||
if (nodeControls.Count > 1)
|
if (nodeControls.Count > 1)
|
||||||
{
|
{
|
||||||
@@ -1470,10 +1528,10 @@ namespace Serein.WorkBench
|
|||||||
}
|
}
|
||||||
flowStartBlock = null;
|
flowStartBlock = null;
|
||||||
}
|
}
|
||||||
var RemoveEonnections = connections.Where(c => c.Start.Node.Guid.Equals(nodeControl.Node.Guid)
|
var RemoveEonnections = connections.Where(c => c.Start.ViewModel.Node.Guid.Equals(nodeControl.ViewModel.Node.Guid)
|
||||||
|| c.End.Node.Guid.Equals(nodeControl.Node.Guid)).ToList();
|
|| c.End.ViewModel.Node.Guid.Equals(nodeControl.ViewModel.Node.Guid)).ToList();
|
||||||
|
|
||||||
Remove(RemoveEonnections, nodeControl.Node);
|
Remove(RemoveEonnections, nodeControl.ViewModel.Node);
|
||||||
// 删除控件
|
// 删除控件
|
||||||
FlowChartCanvas.Children.Remove(nodeControl);
|
FlowChartCanvas.Children.Remove(nodeControl);
|
||||||
nodeControls.Remove(nodeControl);
|
nodeControls.Remove(nodeControl);
|
||||||
@@ -1494,8 +1552,8 @@ namespace Serein.WorkBench
|
|||||||
var tempArr = connections.ToArray();
|
var tempArr = connections.ToArray();
|
||||||
foreach (var connection in tempArr)
|
foreach (var connection in tempArr)
|
||||||
{
|
{
|
||||||
var startNode = connection.Start.Node;
|
var startNode = connection.Start.ViewModel.Node;
|
||||||
var endNode = connection.End.Node;
|
var endNode = connection.End.ViewModel.Node;
|
||||||
bool IsStartInThisConnection = false;
|
bool IsStartInThisConnection = false;
|
||||||
// 要删除的节点(targetNode),在连接关系中是否为起点
|
// 要删除的节点(targetNode),在连接关系中是否为起点
|
||||||
// 如果是,则需要从 targetNode 中删除子节点。
|
// 如果是,则需要从 targetNode 中删除子节点。
|
||||||
@@ -1570,19 +1628,21 @@ namespace Serein.WorkBench
|
|||||||
if (nodeControl == null) { return; }
|
if (nodeControl == null) { return; }
|
||||||
if (flowStartBlock != null)
|
if (flowStartBlock != null)
|
||||||
{
|
{
|
||||||
flowStartBlock.Node.IsStart = false;
|
flowStartBlock.ViewModel.Node.IsStart = false;
|
||||||
flowStartBlock.BorderBrush = Brushes.Black;
|
flowStartBlock.BorderBrush = Brushes.Black;
|
||||||
flowStartBlock.BorderThickness = new Thickness(0);
|
flowStartBlock.BorderThickness = new Thickness(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeControl.Node.IsStart = true;
|
nodeControl.ViewModel.Node.IsStart = true;
|
||||||
nodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"));
|
nodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"));
|
||||||
nodeControl.BorderThickness = new Thickness(2);
|
nodeControl.BorderThickness = new Thickness(2);
|
||||||
|
|
||||||
flowStartBlock = nodeControl;
|
flowStartBlock = nodeControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 树形结构展开类型的成员
|
/// 查看返回类型(树形结构展开类型的成员)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type"></param>
|
/// <param name="type"></param>
|
||||||
private void DisplayReturnTypeTreeViewer(Type type)
|
private void DisplayReturnTypeTreeViewer(Type type)
|
||||||
@@ -1614,8 +1674,8 @@ namespace Serein.WorkBench
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 获取起始节点与终止节点,消除映射关系
|
// 获取起始节点与终止节点,消除映射关系
|
||||||
var StartNode = connectionToRemove.Start.Node;
|
var StartNode = connectionToRemove.Start.ViewModel.Node;
|
||||||
var EndNode = connectionToRemove.End.Node;
|
var EndNode = connectionToRemove.End.ViewModel.Node;
|
||||||
|
|
||||||
if (connectionToRemove.Type == ConnectionType.IsSucceed)
|
if (connectionToRemove.Type == ConnectionType.IsSucceed)
|
||||||
{
|
{
|
||||||
@@ -1687,95 +1747,6 @@ namespace Serein.WorkBench
|
|||||||
FlowChartCanvas.Height = height;
|
FlowChartCanvas.Height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 准备弃用
|
|
||||||
// 画布缩放时调整画布大小(已弃用)
|
|
||||||
private void AdjustCanvasSizeToContainer()
|
|
||||||
{
|
|
||||||
// 获取当前缩放后的画布大小
|
|
||||||
double newWidth = FlowChartCanvas.Width * scaleTransform.ScaleX;
|
|
||||||
double newHeight = FlowChartCanvas.Height * scaleTransform.ScaleY;
|
|
||||||
|
|
||||||
// 设置画布大小,使其至少与ScrollViewer的可视区域大小相同
|
|
||||||
FlowChartCanvas.Width = Math.Max(FlowChartStackPanel.Width, newWidth);
|
|
||||||
FlowChartCanvas.Height = Math.Max(FlowChartStackPanel.Height, newHeight);
|
|
||||||
}
|
|
||||||
// 窗体尺寸发生变化时调整画布大小(已弃用)
|
|
||||||
private void AdjustCanvasSize()
|
|
||||||
{
|
|
||||||
// 获取 ScrollViewer 可视区域的宽度和高度
|
|
||||||
double scrollViewerWidth = FlowChartStackPanel.Width * scaleTransform.ScaleX;
|
|
||||||
double scrollViewerHeight = FlowChartStackPanel.Height * scaleTransform.ScaleY;
|
|
||||||
|
|
||||||
// 调整 Canvas 大小
|
|
||||||
if (scrollViewerWidth > 0 && scrollViewerHeight > 0)
|
|
||||||
{
|
|
||||||
FlowChartCanvas.Width = scrollViewerWidth;
|
|
||||||
FlowChartCanvas.Height = scrollViewerHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 随着平移拖动动态调整画布大小(弃用)
|
|
||||||
private void AdjustCanvasSizeAndContent(double deltaX, double deltaY)
|
|
||||||
{
|
|
||||||
|
|
||||||
// 获取画布的边界框
|
|
||||||
Rect transformedBounds = FlowChartCanvas.RenderTransform.TransformBounds(new Rect(FlowChartCanvas.RenderSize));
|
|
||||||
|
|
||||||
|
|
||||||
Debug.WriteLine($"deltaX : {deltaX},{deltaY}");
|
|
||||||
Debug.WriteLine($" LTRP : {transformedBounds.Left},{transformedBounds.Top},{transformedBounds.Right},{transformedBounds.Bottom}");
|
|
||||||
|
|
||||||
|
|
||||||
// 检查画布的左边缘是否超出视图
|
|
||||||
if (deltaX > 0 && transformedBounds.Left > 0)
|
|
||||||
{
|
|
||||||
double offsetX = transformedBounds.Left;
|
|
||||||
FlowChartCanvas.Width += offsetX;
|
|
||||||
translateTransform.X -= offsetX;
|
|
||||||
|
|
||||||
Debug.Print($" offsetX : {offsetX}");
|
|
||||||
// 移动所有控件的位置
|
|
||||||
foreach (UIElement child in FlowChartCanvas.Children)
|
|
||||||
{
|
|
||||||
Canvas.SetLeft(child, Canvas.GetLeft(child) + offsetX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查画布的上边缘是否超出视图
|
|
||||||
//if (transformedBounds.Top > 0)
|
|
||||||
if (deltaY > 0 & transformedBounds.Top > 0)
|
|
||||||
{
|
|
||||||
double offsetY = transformedBounds.Top;
|
|
||||||
FlowChartCanvas.Height += offsetY;
|
|
||||||
translateTransform.Y -= offsetY;
|
|
||||||
Debug.Print($" offsetY : {offsetY}");
|
|
||||||
|
|
||||||
// 移动所有控件的位置
|
|
||||||
foreach (UIElement child in FlowChartCanvas.Children)
|
|
||||||
{
|
|
||||||
Canvas.SetTop(child, Canvas.GetTop(child) + offsetY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var size = 50;
|
|
||||||
// 检查画布的右边缘是否超出当前宽度
|
|
||||||
if (transformedBounds.Right + size < FlowChartStackPanel.ActualWidth)
|
|
||||||
{
|
|
||||||
|
|
||||||
double extraWidth = FlowChartStackPanel.ActualWidth - transformedBounds.Right;
|
|
||||||
this.FlowChartCanvas.Width += extraWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查画布的下边缘是否超出当前高度
|
|
||||||
if (transformedBounds.Bottom + size < FlowChartStackPanel.ActualHeight)
|
|
||||||
{
|
|
||||||
double extraHeight = FlowChartStackPanel.ActualHeight - transformedBounds.Bottom;
|
|
||||||
this.FlowChartCanvas.Height += extraHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region 动态调整区域大小
|
#region 动态调整区域大小
|
||||||
//private void Thumb_DragDelta_TopLeft(object sender, DragDeltaEventArgs e)
|
//private void Thumb_DragDelta_TopLeft(object sender, DragDeltaEventArgs e)
|
||||||
@@ -1913,7 +1884,7 @@ namespace Serein.WorkBench
|
|||||||
private async void ButtonDebugRun_Click(object sender, RoutedEventArgs e)
|
private async void ButtonDebugRun_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
logWindow?.Show();
|
logWindow?.Show();
|
||||||
var nodes = nodeControls.Select(it => it.Node).ToList();
|
var nodes = nodeControls.Select(control => control.ViewModel.Node).ToList();
|
||||||
var methodDetails = DictMethodDetail.Values.ToList();
|
var methodDetails = DictMethodDetail.Values.ToList();
|
||||||
nodeFlowStarter ??= new NodeFlowStarter(ServiceContainer, methodDetails);
|
nodeFlowStarter ??= new NodeFlowStarter(ServiceContainer, methodDetails);
|
||||||
await nodeFlowStarter.RunAsync(nodes);
|
await nodeFlowStarter.RunAsync(nodes);
|
||||||
@@ -1942,19 +1913,19 @@ namespace Serein.WorkBench
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 生成节点信息
|
// 生成节点信息
|
||||||
var nodeInfos = nodeControls.Select(item =>
|
var nodeInfos = nodeControls.Select(control =>
|
||||||
{
|
{
|
||||||
var node = item.Node;
|
var node = control.ViewModel.Node;
|
||||||
Point positionRelativeToParent = item.TranslatePoint(new Point(0, 0), FlowChartCanvas);
|
Point positionRelativeToParent = control.TranslatePoint(new Point(0, 0), FlowChartCanvas);
|
||||||
var trueNodes = item.Node.SucceedBranch.Select(item => item.Guid); // 真分支
|
var trueNodes = control.ViewModel.Node.SucceedBranch.Select(item => item.Guid); // 真分支
|
||||||
var falseNodes = item.Node.FailBranch.Select(item => item.Guid);// 假分支
|
var falseNodes = control.ViewModel.Node.FailBranch.Select(item => item.Guid);// 假分支
|
||||||
var upstreamNodes = item.Node.UpstreamBranch.Select(item => item.Guid);// 上游分支
|
var upstreamNodes = control.ViewModel.Node.UpstreamBranch.Select(item => item.Guid);// 上游分支
|
||||||
|
|
||||||
// 常规节点的参数信息
|
// 常规节点的参数信息
|
||||||
List<Parameterdata> parameterData = [];
|
List<Parameterdata> parameterData = [];
|
||||||
if (node?.MethodDetails?.ExplicitDatas is not null
|
if (node?.MethodDetails?.ExplicitDatas is not null
|
||||||
&& (node.MethodDetails.MethodDynamicType == DynamicNodeType.Action
|
&& (node.MethodDetails.MethodDynamicType == Serein.Library.Enums.NodeType.Action
|
||||||
|| node.MethodDetails.MethodDynamicType == DynamicNodeType.Flipflop))
|
|| node.MethodDetails.MethodDynamicType == Serein.Library.Enums.NodeType.Flipflop))
|
||||||
{
|
{
|
||||||
parameterData = node.MethodDetails
|
parameterData = node.MethodDetails
|
||||||
.ExplicitDatas
|
.ExplicitDatas
|
||||||
@@ -2022,7 +1993,7 @@ namespace Serein.WorkBench
|
|||||||
.Select(region =>
|
.Select(region =>
|
||||||
{
|
{
|
||||||
WriteLog(region.GetType().ToString() + "\r\n");
|
WriteLog(region.GetType().ToString() + "\r\n");
|
||||||
if (region is ConditionRegionControl && region.Node is CompositeConditionNode conditionRegion) // 条件区域控件
|
if (region is ConditionRegionControl && region.ViewModel.Node is CompositeConditionNode conditionRegion) // 条件区域控件
|
||||||
{
|
{
|
||||||
List<object> childNodes = [];
|
List<object> childNodes = [];
|
||||||
var tmpChildNodes = conditionRegion.ConditionNodes;
|
var tmpChildNodes = conditionRegion.ConditionNodes;
|
||||||
@@ -2046,11 +2017,11 @@ namespace Serein.WorkBench
|
|||||||
}
|
}
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
guid = region.Node.Guid,
|
guid = region.ViewModel.Node.Guid,
|
||||||
childNodes = childNodes
|
childNodes = childNodes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (region is ActionRegionControl && region.Node is CompositeActionNode actionRegion) // 动作区域控件
|
else if (region is ActionRegionControl && region.ViewModel.Node is CompositeActionNode actionRegion) // 动作区域控件
|
||||||
{
|
{
|
||||||
//WriteLog(region.Node.GetType().ToString() + "\r\n");
|
//WriteLog(region.Node.GetType().ToString() + "\r\n");
|
||||||
|
|
||||||
@@ -2076,7 +2047,7 @@ namespace Serein.WorkBench
|
|||||||
}
|
}
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
guid = region.Node.Guid,
|
guid = region.ViewModel.Node.Guid,
|
||||||
childNodes = childNodes
|
childNodes = childNodes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -2129,7 +2100,7 @@ namespace Serein.WorkBench
|
|||||||
["versions"] = "1",
|
["versions"] = "1",
|
||||||
},
|
},
|
||||||
["library"] = JArray.FromObject(dlls),
|
["library"] = JArray.FromObject(dlls),
|
||||||
["startNode"] = flowStartBlock == null ? "" : flowStartBlock.Node.Guid,
|
["startNode"] = flowStartBlock == null ? "" : flowStartBlock.ViewModel.Node.Guid,
|
||||||
["nodes"] = JArray.FromObject(nodeInfos),
|
["nodes"] = JArray.FromObject(nodeInfos),
|
||||||
["regions"] = JArray.FromObject(regionObjs),
|
["regions"] = JArray.FromObject(regionObjs),
|
||||||
};
|
};
|
||||||
@@ -2245,6 +2216,8 @@ namespace Serein.WorkBench
|
|||||||
{
|
{
|
||||||
// AdjustCanvasSize();
|
// AdjustCanvasSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
#region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线
|
#region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线
|
||||||
|
|
||||||
|
|||||||
@@ -10,16 +10,10 @@ namespace Serein.WorkBench.Node.View
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ActionNodeControl : NodeControlBase
|
public partial class ActionNodeControl : NodeControlBase
|
||||||
{
|
{
|
||||||
private readonly ActionNodeControlViewModel actionNodeControlViewModel;
|
public ActionNodeControl(ActionNodeControlViewModel viewModel):base(viewModel)
|
||||||
public ActionNodeControl(SingleActionNode node) : base(node)
|
|
||||||
{
|
{
|
||||||
Node = node;
|
DataContext = viewModel;
|
||||||
actionNodeControlViewModel = new ActionNodeControlViewModel(node);
|
|
||||||
DataContext = actionNodeControlViewModel;
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,18 +13,21 @@ namespace Serein.WorkBench.Node.View
|
|||||||
{
|
{
|
||||||
private Point _dragStartPoint;
|
private Point _dragStartPoint;
|
||||||
|
|
||||||
private new readonly CompositeActionNode Node;
|
//private new readonly CompositeActionNode Node;
|
||||||
|
|
||||||
public ActionRegionControl() : base()
|
//public override NodeControlViewModel ViewModel { get ; set ; }
|
||||||
|
|
||||||
|
public ActionRegionControl() : base(null)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
public ActionRegionControl(CompositeActionNode node) : base(node)
|
//public ActionRegionControl(CompositeActionNode node)
|
||||||
{
|
//{
|
||||||
InitializeComponent();
|
// InitializeComponent();
|
||||||
Node = node;
|
// //ViewModel = new NodeControlViewModel(node);
|
||||||
base.Name = "动作组合节点";
|
// DataContext = ViewModel;
|
||||||
}
|
// base.Name = "动作组合节点";
|
||||||
|
//}
|
||||||
|
|
||||||
public void AddAction(NodeControlBase node, bool isTask = false)
|
public void AddAction(NodeControlBase node, bool isTask = false)
|
||||||
{
|
{
|
||||||
@@ -34,8 +37,8 @@ namespace Serein.WorkBench.Node.View
|
|||||||
Margin = new Thickness(10, 2, 0, 0),
|
Margin = new Thickness(10, 2, 0, 0),
|
||||||
Tag = node.MethodDetails,
|
Tag = node.MethodDetails,
|
||||||
};*/
|
};*/
|
||||||
Node?.AddNode((SingleActionNode)node.Node);
|
/// Node?.AddNode((SingleActionNode)node.ViewModel.Node);
|
||||||
ActionsListBox.Items.Add(node);
|
// ActionsListBox.Items.Add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* public async Task ExecuteActions(DynamicContext context)
|
/* public async Task ExecuteActions(DynamicContext context)
|
||||||
|
|||||||
@@ -8,24 +8,19 @@ namespace Serein.WorkBench.Node.View
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ConditionNodeControl : NodeControlBase
|
public partial class ConditionNodeControl : NodeControlBase
|
||||||
{
|
{
|
||||||
public ConditionNodeControlViewModel ViewModel { get; }
|
|
||||||
|
|
||||||
public ConditionNodeControl() : base()
|
public ConditionNodeControl() : base()
|
||||||
{
|
{
|
||||||
|
// 窗体初始化需要
|
||||||
ViewModel = new (new ());
|
ViewModel = new ConditionNodeControlViewModel (new SingleConditionNode());
|
||||||
DataContext = ViewModel;
|
DataContext = ViewModel;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConditionNodeControl(SingleConditionNode node) : base(node)
|
public ConditionNodeControl(ConditionNodeControlViewModel viewModel):base(viewModel)
|
||||||
{
|
{
|
||||||
Node = node;
|
DataContext = viewModel;
|
||||||
ViewModel = new ConditionNodeControlViewModel(node);
|
|
||||||
DataContext = ViewModel;
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,9 @@
|
|||||||
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
||||||
MaxWidth="300">
|
MaxWidth="300">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|
||||||
<Border BorderBrush="Black" BorderThickness="1" Padding="10">
|
<Border BorderBrush="Black" BorderThickness="1" Padding="10">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<DockPanel Margin="2,2,2,5">
|
<DockPanel Margin="2,2,2,5">
|
||||||
<!--<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="40"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
</Grid.ColumnDefinitions>-->
|
|
||||||
<TextBlock Text="条件区域" FontWeight="Bold" HorizontalAlignment="Left" FontSize="14" Margin="0,1,0,0"/>
|
<TextBlock Text="条件区域" FontWeight="Bold" HorizontalAlignment="Left" FontSize="14" Margin="0,1,0,0"/>
|
||||||
<Button Content="编辑" FontWeight="Bold" HorizontalAlignment="Right"/>
|
<Button Content="编辑" FontWeight="Bold" HorizontalAlignment="Right"/>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
|
using Serein.WorkBench.Node.ViewModel;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Controls.Primitives;
|
using System.Windows.Controls.Primitives;
|
||||||
@@ -12,26 +13,29 @@ namespace Serein.WorkBench.Node.View
|
|||||||
public partial class ConditionRegionControl : NodeControlBase
|
public partial class ConditionRegionControl : NodeControlBase
|
||||||
{
|
{
|
||||||
private Point _dragStartPoint;
|
private Point _dragStartPoint;
|
||||||
|
|
||||||
public ConditionRegionControl() : base()
|
public ConditionRegionControl() : base()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConditionRegionControl(CompositeConditionNode node) : base(node)
|
public ConditionRegionControl(ConditionRegionNodeControlViewModel viewModel) : base(viewModel)
|
||||||
{
|
{
|
||||||
Node = node;
|
DataContext = viewModel;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加条件控件
|
/// 添加条件控件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="condition"></param>
|
/// <param name="condition"></param>
|
||||||
public void AddCondition(NodeControlBase node)
|
public void AddCondition(NodeControlBase node)
|
||||||
{
|
{
|
||||||
((CompositeConditionNode)Node).AddNode((SingleConditionNode)node.Node);
|
((CompositeConditionNode)ViewModel.Node).AddNode((SingleConditionNode)node.ViewModel.Node);
|
||||||
|
|
||||||
this.Width += node.Width;
|
this.Width += node.Width;
|
||||||
this.Height += node.Height;
|
this.Height += node.Height;
|
||||||
@@ -46,27 +50,27 @@ namespace Serein.WorkBench.Node.View
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mouse event handlers for dragging
|
// Mouse event handlers for dragging
|
||||||
private void TypeText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
//private void TypeText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
//{
|
||||||
_dragStartPoint = e.GetPosition(null);
|
// _dragStartPoint = e.GetPosition(null);
|
||||||
}
|
//}
|
||||||
|
|
||||||
private void TypeText_MouseMove(object sender, MouseEventArgs e)
|
//private void TypeText_MouseMove(object sender, MouseEventArgs e)
|
||||||
{
|
//{
|
||||||
Point mousePos = e.GetPosition(null);
|
// Point mousePos = e.GetPosition(null);
|
||||||
Vector diff = _dragStartPoint - mousePos;
|
// Vector diff = _dragStartPoint - mousePos;
|
||||||
|
|
||||||
if (e.LeftButton == MouseButtonState.Pressed &&
|
// if (e.LeftButton == MouseButtonState.Pressed &&
|
||||||
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
// (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
||||||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
// Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
||||||
{
|
// {
|
||||||
if (sender is TextBlock typeText)
|
// if (sender is TextBlock typeText)
|
||||||
{
|
// {
|
||||||
var dragData = new DataObject(MouseNodeType.RegionType, typeText.Tag);
|
// var dragData = new DataObject(MouseNodeType.RegionType, typeText.Tag);
|
||||||
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
|
// DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -86,8 +90,7 @@ namespace Serein.WorkBench.Node.View
|
|||||||
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
|
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,21 +8,17 @@ namespace Serein.WorkBench.Node.View
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ExpOpNodeControl : NodeControlBase
|
public partial class ExpOpNodeControl : NodeControlBase
|
||||||
{
|
{
|
||||||
public ExpOpNodeViewModel ViewModel { get; }
|
public ExpOpNodeControl() : base()
|
||||||
|
|
||||||
public ExpOpNodeControl()
|
|
||||||
{
|
{
|
||||||
ViewModel = new (new());
|
// 窗体初始化需要
|
||||||
|
ViewModel = new ExpOpNodeViewModel(new SingleExpOpNode());
|
||||||
DataContext = ViewModel;
|
DataContext = ViewModel;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
public ExpOpNodeControl(SingleExpOpNode node):base(node)
|
public ExpOpNodeControl(ExpOpNodeViewModel viewModel) :base(viewModel)
|
||||||
{
|
{
|
||||||
Node = node;
|
DataContext = viewModel;
|
||||||
ViewModel = new(node);
|
|
||||||
DataContext = ViewModel;
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,31 +8,10 @@ namespace Serein.WorkBench.Node.View
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class FlipflopNodeControl : NodeControlBase
|
public partial class FlipflopNodeControl : NodeControlBase
|
||||||
{
|
{
|
||||||
private readonly FlipflopNodeControlViewModel viewModel;
|
public FlipflopNodeControl(FlipflopNodeControlViewModel viewModel) : base(viewModel)
|
||||||
|
|
||||||
public FlipflopNodeControl(SingleFlipflopNode node) : base(node)
|
|
||||||
{
|
{
|
||||||
Node = node;
|
|
||||||
viewModel = new FlipflopNodeControlViewModel(node);
|
|
||||||
DataContext = viewModel;
|
DataContext = viewModel;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
//{
|
|
||||||
// var comboBox = sender as ComboBox;
|
|
||||||
// if (comboBox == null)
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// var selectedExplicitData = comboBox.DataContext as ExplicitData;
|
|
||||||
// if (selectedExplicitData == null)
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Console.WriteLine (selectedExplicitData.DataValue, "Selected Value");
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user