mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-05-01 03:53:22 +08:00
重写了触发器底层逻辑
This commit is contained in:
@@ -38,6 +38,11 @@ namespace Serein.Library.Core.NodeFlow
|
|||||||
{
|
{
|
||||||
// 获取 Task<> 的泛型参数类型
|
// 获取 Task<> 的泛型参数类型
|
||||||
var innerType = type.GetGenericArguments()[0];
|
var innerType = type.GetGenericArguments()[0];
|
||||||
|
if (innerType.IsGenericType && type.GetGenericTypeDefinition() == typeof(IFlipflopContext<>))
|
||||||
|
{
|
||||||
|
var flipflop = type.GetGenericArguments()[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// 判断 innerType 是否继承 IFlipflopContext
|
// 判断 innerType 是否继承 IFlipflopContext
|
||||||
//if (typeof(IFlipflopContext).IsAssignableFrom(innerType))
|
//if (typeof(IFlipflopContext).IsAssignableFrom(innerType))
|
||||||
@@ -50,11 +55,11 @@ namespace Serein.Library.Core.NodeFlow
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
// 检查泛型参数是否为 Flipflop<>
|
// 检查泛型参数是否为 Flipflop<>
|
||||||
if (innerType == typeof(IFlipflopContext))
|
//if (innerType == typeof(IFlipflopContext))
|
||||||
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||||
{
|
//{
|
||||||
return true;
|
//return true;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -64,20 +69,21 @@ namespace Serein.Library.Core.NodeFlow
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 触发器上下文
|
/// 触发器上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FlipflopContext : IFlipflopContext
|
public class FlipflopContext<TResult> : IFlipflopContext<TResult>
|
||||||
{
|
{
|
||||||
public FlipflopStateType State { get; set; }
|
public FlipflopStateType State { get; set; }
|
||||||
|
|
||||||
public TriggerData TriggerData { get; set; }
|
public TriggerType Type { get; set; }
|
||||||
|
public TResult Value { get; set; }
|
||||||
|
|
||||||
public FlipflopContext(FlipflopStateType ffState)
|
public FlipflopContext(FlipflopStateType ffState)
|
||||||
{
|
{
|
||||||
State = ffState;
|
State = ffState;
|
||||||
}
|
}
|
||||||
public FlipflopContext(FlipflopStateType ffState, TriggerData data)
|
public FlipflopContext(FlipflopStateType ffState, TResult value)
|
||||||
{
|
{
|
||||||
State = ffState;
|
State = ffState;
|
||||||
TriggerData = data;
|
Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,13 +40,18 @@ namespace Serein.Library.Framework.NodeFlow
|
|||||||
{
|
{
|
||||||
// 获取 Task<> 的泛型参数类型
|
// 获取 Task<> 的泛型参数类型
|
||||||
var innerType = type.GetGenericArguments()[0];
|
var innerType = type.GetGenericArguments()[0];
|
||||||
|
if (innerType.IsGenericType && type.GetGenericTypeDefinition() == typeof(IFlipflopContext<>))
|
||||||
// 检查泛型参数是否为 Flipflop<>
|
|
||||||
if (innerType == typeof(FlipflopContext))
|
|
||||||
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
|
||||||
{
|
{
|
||||||
|
var flipflop = type.GetGenericArguments()[0];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//// 检查泛型参数是否为 Flipflop<>
|
||||||
|
//if (innerType == typeof(FlipflopContext))
|
||||||
|
////if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||||
|
//{
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -56,24 +61,22 @@ namespace Serein.Library.Framework.NodeFlow
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 触发器上下文
|
/// 触发器上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FlipflopContext : IFlipflopContext
|
public class FlipflopContext<TResult> : IFlipflopContext<TResult>
|
||||||
{
|
{
|
||||||
public FlipflopStateType State { get; set; }
|
public FlipflopStateType State { get; set; }
|
||||||
//public TResult? Data { get; set; }
|
|
||||||
public TriggerData TriggerData { get; set; }
|
public TriggerType Type { get; set; }
|
||||||
|
public TResult Value { get; set; }
|
||||||
|
|
||||||
public FlipflopContext(FlipflopStateType ffState)
|
public FlipflopContext(FlipflopStateType ffState)
|
||||||
{
|
{
|
||||||
State = ffState;
|
State = ffState;
|
||||||
}
|
}
|
||||||
public FlipflopContext(FlipflopStateType ffState, object data)
|
|
||||||
|
public FlipflopContext(FlipflopStateType ffState, TResult value)
|
||||||
{
|
{
|
||||||
State = ffState;
|
State = ffState;
|
||||||
TriggerData = new TriggerData
|
Value = value;
|
||||||
{
|
|
||||||
Type = TriggerType.External,
|
|
||||||
Value = data
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,24 @@ using Serein.Library.NodeFlow.Tool;
|
|||||||
|
|
||||||
namespace Serein.Library.Api
|
namespace Serein.Library.Api
|
||||||
{
|
{
|
||||||
public interface IFlipflopContext
|
/// <summary>
|
||||||
|
/// 触发器必须使用该接口作为返回值,同时必须用Task泛型表示,否则将不会进行等待触发。
|
||||||
|
/// </summary>
|
||||||
|
public interface IFlipflopContext<out TResult>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 触发器完成的状态(根据业务场景手动设置)
|
||||||
|
/// </summary>
|
||||||
FlipflopStateType State { get; set; }
|
FlipflopStateType State { get; set; }
|
||||||
TriggerData TriggerData { get; set; }
|
/// <summary>
|
||||||
|
/// 触发传递的数据
|
||||||
|
/// </summary>
|
||||||
|
//TriggerData TriggerData { get; set; }
|
||||||
|
|
||||||
|
TriggerType Type { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 触发传递的数据
|
||||||
|
/// </summary>
|
||||||
|
TResult Value { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Serein.Library.Enums
|
|||||||
Exit,
|
Exit,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 触发器(建议手动设置返回类型,方便视图直观)
|
/// 触发器(返回值必须为Task<IFlipflopContext<TResult>>)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Flipflop,
|
Flipflop,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Serein.Library.Web
|
|||||||
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 自动注册控制器
|
/// 标记该类为 Web Api 处理类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AutoHostingAttribute : Attribute
|
public class AutoHostingAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ namespace Serein.Library.Network.WebSocketCommunication
|
|||||||
/// <para>使用方式:</para>
|
/// <para>使用方式:</para>
|
||||||
/// <para>[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]</para>
|
/// <para>[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]</para>
|
||||||
/// <para>public class PlcSocketService : ISocketHandleModule</para>
|
/// <para>public class PlcSocketService : ISocketHandleModule</para>
|
||||||
/// <para>类中方法示例:void AddUser(string name,age 35)</para>
|
/// <para>类中方法示例:void AddUser(string name,int age)</para>
|
||||||
/// <para>Json示例:{ "theme":"AddUser", //【ThemeKey】 </para>
|
/// <para>Json示例:{ "theme":"AddUser", //【ThemeKey】 </para>
|
||||||
/// <para> "data": { // 【DataKey】 </para>
|
/// <para> "data": { // 【DataKey】 </para>
|
||||||
/// <para> "name":"张三", </para>
|
/// <para> "name":"张三", </para>
|
||||||
/// <para> "age":35, } } </para>
|
/// <para> "age":35, } } </para>
|
||||||
/// <para>WebSocket中收到以上该Json时,通过ThemeKey获取到"AddUser",然后找到AddUser()方法,并将"data"作为数据对象,取出相应数据作为入参(args:"张三",35)进行调用(如果有)</para>
|
/// <para>WebSocket中收到以上该Json时,通过ThemeKey获取到"AddUser",然后找到AddUser()方法</para>
|
||||||
|
/// <para>然后根据方法入参名称,从data对应的json数据中取出"name""age"对应的数据作为入参进行调用。AddUser("张三",35)</para>
|
||||||
/// <para></para>
|
/// <para></para>
|
||||||
///
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public sealed class AutoSocketModuleAttribute : Attribute
|
public sealed class AutoSocketModuleAttribute : Attribute
|
||||||
|
|||||||
@@ -24,7 +24,16 @@ namespace Serein.Library.Network.WebSocketCommunication
|
|||||||
{
|
{
|
||||||
listener = new HttpListener();
|
listener = new HttpListener();
|
||||||
listener.Prefixes.Add(url);
|
listener.Prefixes.Add(url);
|
||||||
listener.Start();
|
try
|
||||||
|
{
|
||||||
|
listener.Start();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await Console.Out.WriteLineAsync(ex.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -104,10 +104,6 @@ namespace Serein.Library.Attributes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public NodeType MethodDynamicType;
|
public NodeType MethodDynamicType;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示标注方法返回类型,不影响运行逻辑(用于表示触发器触发后返回的数据)
|
|
||||||
/// </summary>
|
|
||||||
public Type ReturnType;
|
|
||||||
/// <summary>
|
|
||||||
/// 暂无意义
|
/// 暂无意义
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string LockName;
|
public string LockName;
|
||||||
|
|||||||
209
Library/Utils/ConvertHelper.cs
Normal file
209
Library/Utils/ConvertHelper.cs
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Utils
|
||||||
|
{
|
||||||
|
public static class ConvertHelper
|
||||||
|
{
|
||||||
|
public static TResult ToConvert<TResult>(this object data)
|
||||||
|
{
|
||||||
|
var type = typeof(TResult);
|
||||||
|
if (data is null && type.IsValueType)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
return (TResult)data.ToConvert(type);
|
||||||
|
|
||||||
|
}
|
||||||
|
public static object ToConvert(this object data,Type type)
|
||||||
|
{
|
||||||
|
if (type.IsValueType)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance(type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ConvertHelper.ValueParse(type, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static T ValueParse<T>(object value) where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
string valueStr = value.ToString();
|
||||||
|
return valueStr.ToValueData<T>() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object ValueParse(Type type, object value)
|
||||||
|
{
|
||||||
|
string valueStr = value.ToString();
|
||||||
|
return valueStr.ToValueData(type);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static T ToValueData<T>(this string valueStr) where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(valueStr))
|
||||||
|
{
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
var type = typeof(T);
|
||||||
|
object result;
|
||||||
|
if (type.IsEnum)
|
||||||
|
{
|
||||||
|
result = Enum.Parse(type, valueStr);
|
||||||
|
}
|
||||||
|
else if (type == typeof(bool))
|
||||||
|
{
|
||||||
|
result = bool.Parse(valueStr);
|
||||||
|
}
|
||||||
|
else if (type == typeof(float))
|
||||||
|
{
|
||||||
|
result = float.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(decimal))
|
||||||
|
{
|
||||||
|
result = decimal.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(double))
|
||||||
|
{
|
||||||
|
result = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(sbyte))
|
||||||
|
{
|
||||||
|
result = sbyte.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(byte))
|
||||||
|
{
|
||||||
|
result = byte.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(short))
|
||||||
|
{
|
||||||
|
result = short.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(ushort))
|
||||||
|
{
|
||||||
|
result = ushort.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(int))
|
||||||
|
{
|
||||||
|
result = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(uint))
|
||||||
|
{
|
||||||
|
result = uint.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(long))
|
||||||
|
{
|
||||||
|
result = long.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(ulong))
|
||||||
|
{
|
||||||
|
result = ulong.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
#if NET6_0 || NET7_0 || NET8_0
|
||||||
|
else if (type == typeof(nint))
|
||||||
|
{
|
||||||
|
result = nint.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(nuint))
|
||||||
|
{
|
||||||
|
result = nuint.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("非预期值类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)result;
|
||||||
|
}
|
||||||
|
public static object ToValueData(this string valueStr, Type type)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(valueStr))
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance(type);
|
||||||
|
}
|
||||||
|
object result;
|
||||||
|
if (type.IsEnum)
|
||||||
|
{
|
||||||
|
result = Enum.Parse(type, valueStr);
|
||||||
|
}
|
||||||
|
else if (type == typeof(bool))
|
||||||
|
{
|
||||||
|
result = bool.Parse(valueStr);
|
||||||
|
}
|
||||||
|
else if (type == typeof(float))
|
||||||
|
{
|
||||||
|
result = float.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(decimal))
|
||||||
|
{
|
||||||
|
result = decimal.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(double))
|
||||||
|
{
|
||||||
|
result = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(sbyte))
|
||||||
|
{
|
||||||
|
result = sbyte.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(byte))
|
||||||
|
{
|
||||||
|
result = byte.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(short))
|
||||||
|
{
|
||||||
|
result = short.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(ushort))
|
||||||
|
{
|
||||||
|
result = ushort.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(int))
|
||||||
|
{
|
||||||
|
result = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(uint))
|
||||||
|
{
|
||||||
|
result = uint.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(long))
|
||||||
|
{
|
||||||
|
result = long.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(ulong))
|
||||||
|
{
|
||||||
|
result = ulong.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
#if NET6_0 || NET7_0 || NET8_0
|
||||||
|
else if (type == typeof(nint))
|
||||||
|
{
|
||||||
|
result = nint.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (type == typeof(nuint))
|
||||||
|
{
|
||||||
|
result = nuint.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("非预期值类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -358,8 +358,8 @@ namespace Serein.Library.Utils
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 创建 lambda 表达式
|
// 创建 lambda 表达式
|
||||||
var lambda = Expression.Lambda<Func<object, object[], Task<IFlipflopContext>>>(
|
var lambda = Expression.Lambda<Func<object, object[], Task<IFlipflopContext<object>>>>(
|
||||||
Expression.Convert(methodCall, typeof(Task<IFlipflopContext>)),
|
Expression.Convert(methodCall, typeof(Task<IFlipflopContext<object>>)),
|
||||||
instanceParam,
|
instanceParam,
|
||||||
argsParam
|
argsParam
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using Serein.Library.Utils;
|
||||||
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
@@ -22,28 +24,29 @@ namespace Serein.Library.NodeFlow.Tool
|
|||||||
Overtime
|
Overtime
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TriggerData
|
public class TriggerResult<T>
|
||||||
{
|
{
|
||||||
public TriggerType Type { get; set; }
|
public TriggerType Type { get; set; }
|
||||||
public object Value { get; set; }
|
public T Value { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 信号触发器类,带有消息广播功能
|
/// 信号触发器类,带有消息广播功能。
|
||||||
|
/// 使用枚举作为标记,创建
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChannelFlowTrigger<TSignal> where TSignal : struct, Enum
|
public class FlowTrigger<TSignal> where TSignal : struct, Enum
|
||||||
{
|
{
|
||||||
// 使用并发字典管理每个信号对应的广播列表
|
// 使用并发字典管理每个信号对应的广播列表
|
||||||
private readonly ConcurrentDictionary<TSignal, Subject<TriggerData>> _subscribers = new ConcurrentDictionary<TSignal, Subject<TriggerData>>();
|
private readonly ConcurrentDictionary<TSignal, Subject<(TriggerType,object)>> _subscribers = new ConcurrentDictionary<TSignal, Subject<(TriggerType, object)>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或创建指定信号的 Subject(消息广播者)
|
/// 获取或创建指定信号的 Subject(消息广播者)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="signal">枚举信号标识符</param>
|
/// <param name="signal">枚举信号标识符</param>
|
||||||
/// <returns>对应的 Subject</returns>
|
/// <returns>对应的 Subject</returns>
|
||||||
private Subject<TriggerData> GetOrCreateSubject(TSignal signal)
|
private Subject<(TriggerType, object)> GetOrCreateSubject(TSignal signal)
|
||||||
{
|
{
|
||||||
return _subscribers.GetOrAdd(signal, _ => new Subject<TriggerData>());
|
return _subscribers.GetOrAdd(signal, _ => new Subject<(TriggerType, object)>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -52,9 +55,10 @@ namespace Serein.Library.NodeFlow.Tool
|
|||||||
/// <param name="signal">枚举信号标识符</param>
|
/// <param name="signal">枚举信号标识符</param>
|
||||||
/// <param name="observer">订阅者</param>
|
/// <param name="observer">订阅者</param>
|
||||||
/// <returns>取消订阅的句柄</returns>
|
/// <returns>取消订阅的句柄</returns>
|
||||||
public IDisposable Subscribe(TSignal signal, IObserver<TriggerData> observer)
|
public IDisposable Subscribe(TSignal signal, IObserver<(TriggerType, object)> observer)
|
||||||
{
|
{
|
||||||
var subject = GetOrCreateSubject(signal);
|
var subject = GetOrCreateSubject(signal);
|
||||||
|
// (IObserver<(TriggerType, object)>)
|
||||||
return subject.Subscribe(observer); // 返回取消订阅的句柄
|
return subject.Subscribe(observer); // 返回取消订阅的句柄
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,8 +67,8 @@ namespace Serein.Library.NodeFlow.Tool
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="signal">枚举信号标识符</param>
|
/// <param name="signal">枚举信号标识符</param>
|
||||||
/// <param name="outTime">超时时间</param>
|
/// <param name="outTime">超时时间</param>
|
||||||
/// <returns>等待任务</returns>
|
/// <returns>等待任务,返回值为:状态(超时触发,手动触发),数据(超时触发时会使用设置好的数据)</returns>
|
||||||
public async Task<TriggerData> CreateChannelWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime, TResult outValue)
|
public async Task<(TriggerType, TResult)> CreateTaskWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime, TResult outValue)
|
||||||
{
|
{
|
||||||
var subject = GetOrCreateSubject(signal);
|
var subject = GetOrCreateSubject(signal);
|
||||||
var cts = new CancellationTokenSource();
|
var cts = new CancellationTokenSource();
|
||||||
@@ -77,12 +81,7 @@ namespace Serein.Library.NodeFlow.Tool
|
|||||||
await Task.Delay(outTime, cts.Token);
|
await Task.Delay(outTime, cts.Token);
|
||||||
if (!cts.IsCancellationRequested) // 如果还没有被取消
|
if (!cts.IsCancellationRequested) // 如果还没有被取消
|
||||||
{
|
{
|
||||||
TriggerData triggerData = new TriggerData()
|
subject.OnNext((TriggerType.Overtime, outValue)); // 广播给所有订阅者
|
||||||
{
|
|
||||||
Value = outValue,
|
|
||||||
Type = TriggerType.Overtime,
|
|
||||||
};
|
|
||||||
subject.OnNext(triggerData); // 广播给所有订阅者
|
|
||||||
subject.OnCompleted(); // 通知订阅结束
|
subject.OnCompleted(); // 通知订阅结束
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,39 +95,54 @@ namespace Serein.Library.NodeFlow.Tool
|
|||||||
}
|
}
|
||||||
}, cts.Token);
|
}, cts.Token);
|
||||||
|
|
||||||
// 返回一个等待的任务
|
var result = await WaitSignalAsync<TResult>(signal);// 返回一个可以超时触发的等待任务
|
||||||
return await WaitForSignalAsync(signal);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建等待任务,触发时通知所有订阅者
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="signal">枚举信号标识符</param>
|
||||||
|
/// <param name="outTime">超时时间</param>
|
||||||
|
/// <returns>等待任务</returns>
|
||||||
|
public async Task<TResult> CreateTaskAsync<TResult>(TSignal signal)
|
||||||
|
{
|
||||||
|
var subject = GetOrCreateSubject(signal);
|
||||||
|
(_,var result) = await WaitSignalAsync<TResult>(signal);
|
||||||
|
|
||||||
|
return result;// 返回一个等待的任务
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待指定信号的触发
|
/// 等待指定信号的触发
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="signal">枚举信号标识符</param>
|
/// <param name="signal">枚举信号标识符</param>
|
||||||
/// <returns>等待任务</returns>
|
/// <returns>等待任务</returns>
|
||||||
public async Task<TriggerData> WaitForSignalAsync(TSignal signal)
|
public async Task<(TriggerType, TResult)> WaitSignalAsync<TResult>(TSignal signal)
|
||||||
{
|
{
|
||||||
var taskCompletionSource = new TaskCompletionSource<TriggerData>();
|
var taskCompletionSource = new TaskCompletionSource<(TriggerType, object)>();
|
||||||
var subscription = Subscribe(signal, new Observer<TriggerData>(taskCompletionSource.SetResult));
|
var subscription = Subscribe(signal, new Observer<(TriggerType, object)>(taskCompletionSource.SetResult));
|
||||||
var result = await taskCompletionSource.Task;
|
(var type,var result) = await taskCompletionSource.Task;
|
||||||
subscription.Dispose(); // 取消订阅
|
subscription.Dispose(); // 取消订阅
|
||||||
return result;
|
|
||||||
|
return (type, result.ToConvert<TResult>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 手动触发信号,并广播给所有订阅者
|
/// 手动触发信号,并广播给所有订阅者
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="signal">枚举信号标识符</param>
|
/// <param name="signal">枚举信号标识符</param>
|
||||||
/// <returns>是否成功触发</returns>
|
/// <returns>是否成功触发</returns>
|
||||||
public bool TriggerSignal<TResult>(TSignal signal, TResult value)
|
public bool Trigger<TResult>(TSignal signal, TResult value)
|
||||||
{
|
{
|
||||||
if (_subscribers.TryGetValue(signal, out var subject))
|
if (_subscribers.TryGetValue(signal, out var subject))
|
||||||
{
|
{
|
||||||
TriggerData triggerData = new TriggerData()
|
subject.OnNext((TriggerType.External, value)); // 广播给所有订阅者
|
||||||
{
|
|
||||||
Value = value,
|
|
||||||
Type = TriggerType.External,
|
|
||||||
};
|
|
||||||
subject.OnNext(triggerData); // 广播给所有订阅者
|
|
||||||
//subject.OnCompleted(); // 通知订阅结束
|
//subject.OnCompleted(); // 通知订阅结束
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -28,42 +28,19 @@ namespace Net462DllTest.LogicControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[NodeAction(NodeType.Flipflop, "等待车位调取命令",ReturnType=typeof(string))]
|
[NodeAction(NodeType.Flipflop, "等待车位调取命令")]
|
||||||
public async Task<IFlipflopContext> GetPparkingSpace(ParkingCommand parkingCommand = ParkingCommand.GetPparkingSpace)
|
public async Task<IFlipflopContext<string>> GetPparkingSpace(ParkingCommand parkingCommand = ParkingCommand.GetPparkingSpace)
|
||||||
{
|
{
|
||||||
try
|
var spaceNum = await PrakingDevice.CreateTaskAsync<string>(parkingCommand);
|
||||||
{
|
await Console.Out.WriteLineAsync("收到命令:调取车位,车位号" + spaceNum);
|
||||||
TriggerData triggerData = await PrakingDevice.CreateChannelWithTimeoutAsync(parkingCommand, TimeSpan.FromMinutes(120), 0);
|
return new FlipflopContext<string>(FlipflopStateType.Succeed, spaceNum);
|
||||||
if (triggerData.Type == TriggerType.Overtime)
|
|
||||||
{
|
|
||||||
throw new FlipflopException("超时取消");
|
|
||||||
}
|
|
||||||
if(triggerData.Value is string spaceNum)
|
|
||||||
{
|
|
||||||
await Console.Out.WriteLineAsync("收到命令:调取车位,车位号"+ spaceNum);
|
|
||||||
return new FlipflopContext(FlipflopStateType.Succeed, spaceNum);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new FlipflopException("并非车位号");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (FlipflopException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return new FlipflopContext(FlipflopStateType.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[NodeAction(NodeType.Action, "调取指定车位")]
|
[NodeAction(NodeType.Action, "调取指定车位")]
|
||||||
public void Storage(string spaceNum = "101")
|
public void Storage(string spaceNum = "101")
|
||||||
{
|
{
|
||||||
if (PrakingDevice.TriggerSignal(ParkingCommand.GetPparkingSpace, spaceNum))
|
if (PrakingDevice.Trigger(ParkingCommand.GetPparkingSpace, spaceNum))
|
||||||
{
|
{
|
||||||
Console.WriteLine("发送命令成功:调取车位" + spaceNum);
|
Console.WriteLine("发送命令成功:调取车位" + spaceNum);
|
||||||
|
|
||||||
|
|||||||
@@ -50,27 +50,20 @@ namespace Net462DllTest.LogicControl
|
|||||||
|
|
||||||
#region 触发器节点
|
#region 触发器节点
|
||||||
|
|
||||||
[NodeAction(NodeType.Flipflop, "等待变量更新", ReturnType = typeof(int))]
|
[NodeAction(NodeType.Flipflop, "等待变量更新")]
|
||||||
public async Task<IFlipflopContext> WaitTask(PlcVarName varName = PlcVarName.ErrorCode)
|
public async Task<IFlipflopContext<dynamic>> WaitTask(PlcVarName varName = PlcVarName.ErrorCode)
|
||||||
{
|
{
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TriggerData triggerData = await MyPlc.CreateChannelWithTimeoutAsync(varName, TimeSpan.FromMinutes(120), 0);
|
var triggerData = await MyPlc.CreateTaskAsync<dynamic>(varName);
|
||||||
if (triggerData.Type == TriggerType.Overtime)
|
|
||||||
{
|
await Console.Out.WriteLineAsync($"PLC变量触发器[{varName}]传递数据:{triggerData}");
|
||||||
throw new FlipflopException("超时取消");
|
return new FlipflopContext<dynamic>(FlipflopStateType.Succeed, triggerData);
|
||||||
}
|
|
||||||
await Console.Out.WriteLineAsync($"PLC变量触发器[{varName}]传递数据:{triggerData.Value}");
|
|
||||||
return new FlipflopContext(FlipflopStateType.Succeed, triggerData.Value);
|
|
||||||
}
|
|
||||||
catch (FlipflopException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return new FlipflopContext(FlipflopStateType.Error);
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -94,8 +87,8 @@ namespace Net462DllTest.LogicControl
|
|||||||
string ip = "192.168.10.100",
|
string ip = "192.168.10.100",
|
||||||
int port = 102)
|
int port = 102)
|
||||||
{
|
{
|
||||||
MyPlc.Model.Set(PlcVarName.DoorVar,(Int16)1);
|
//MyPlc.Model.Set(PlcVarName.DoorVar,(Int16)1);
|
||||||
MyPlc.Model.Get(PlcVarName.DoorVar);
|
//MyPlc.Model.Get(PlcVarName.DoorVar);
|
||||||
if (MyPlc.Client is null)
|
if (MyPlc.Client is null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -144,7 +137,7 @@ namespace Net462DllTest.LogicControl
|
|||||||
[NodeAction(NodeType.Action, "批量读取")]
|
[NodeAction(NodeType.Action, "批量读取")]
|
||||||
public PlcVarModelDataProxy BatchReadVar()
|
public PlcVarModelDataProxy BatchReadVar()
|
||||||
{
|
{
|
||||||
MyPlc.BatchRefresh();
|
//MyPlc.BatchRefresh();
|
||||||
return plcVarModelDataProxy;
|
return plcVarModelDataProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,25 +34,18 @@ namespace Net462DllTest.LogicControl
|
|||||||
|
|
||||||
#region 触发器节点
|
#region 触发器节点
|
||||||
|
|
||||||
[NodeAction(NodeType.Flipflop, "等待视图命令", ReturnType = typeof(int))]
|
[NodeAction(NodeType.Flipflop, "等待视图命令")]
|
||||||
public async Task<IFlipflopContext> WaitTask(CommandSignal command)
|
public async Task<IFlipflopContext<int>> WaitTask(CommandSignal command)
|
||||||
{
|
{
|
||||||
try
|
(var type, var result) = await ViewManagement.CreateTaskWithTimeoutAsync(command, TimeSpan.FromHours(10), 0);
|
||||||
|
if (type == TriggerType.Overtime)
|
||||||
{
|
{
|
||||||
TriggerData triggerData = await ViewManagement.CreateChannelWithTimeoutAsync(command, TimeSpan.FromHours(10), 0);
|
return new FlipflopContext<int>(FlipflopStateType.Cancel, result);
|
||||||
if (triggerData.Type == TriggerType.Overtime)
|
|
||||||
{
|
|
||||||
return new FlipflopContext(FlipflopStateType.Cancel, triggerData.Value);
|
|
||||||
}
|
|
||||||
return new FlipflopContext(FlipflopStateType.Succeed, triggerData.Value);
|
|
||||||
}
|
}
|
||||||
catch (FlipflopException)
|
else
|
||||||
{
|
{
|
||||||
throw;
|
|
||||||
}
|
return new FlipflopContext<int>(FlipflopStateType.Succeed, result);
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return new FlipflopContext(FlipflopStateType.Error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using System.Threading.Tasks;
|
|||||||
namespace Net462DllTest.Trigger
|
namespace Net462DllTest.Trigger
|
||||||
{
|
{
|
||||||
[AutoRegister]
|
[AutoRegister]
|
||||||
public class PrakingDevice : ChannelFlowTrigger<ParkingCommand>
|
public class PrakingDevice : FlowTrigger<ParkingCommand>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Net462DllTest.Trigger
|
|||||||
|
|
||||||
|
|
||||||
[AutoRegister]
|
[AutoRegister]
|
||||||
public class SiemensPlcDevice : ChannelFlowTrigger<PlcVarName>
|
public class SiemensPlcDevice : FlowTrigger<PlcVarName>
|
||||||
{
|
{
|
||||||
public SiemensClient Client { get; set; }
|
public SiemensClient Client { get; set; }
|
||||||
public SiemensVersion Version { get; set; }
|
public SiemensVersion Version { get; set; }
|
||||||
@@ -202,7 +202,7 @@ namespace Net462DllTest.Trigger
|
|||||||
if (isNotification)
|
if (isNotification)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"VarName: {signal}\t\tOld Data: {oldData}\tNew Data: {newData}");
|
Console.WriteLine($"VarName: {signal}\t\tOld Data: {oldData}\tNew Data: {newData}");
|
||||||
TriggerSignal(signal, newData);
|
Trigger(signal, newData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Net462DllTest.Trigger
|
|||||||
/// 视图管理
|
/// 视图管理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AutoRegister]
|
[AutoRegister]
|
||||||
public class ViewManagement : ChannelFlowTrigger<CommandSignal>
|
public class ViewManagement : FlowTrigger<CommandSignal>
|
||||||
{
|
{
|
||||||
public ViewManagement(IFlowEnvironment environment)
|
public ViewManagement(IFlowEnvironment environment)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ namespace Net462DllTest.ViewModel
|
|||||||
});
|
});
|
||||||
CommandGetParkingSpace = new RelayCommand((p) =>
|
CommandGetParkingSpace = new RelayCommand((p) =>
|
||||||
{
|
{
|
||||||
viewManagement.TriggerSignal(SelectedSignal, SpcaeNumber);
|
viewManagement.Trigger(SelectedSignal, SpcaeNumber);
|
||||||
});
|
});
|
||||||
CommandCloseForm = new RelayCommand((p) =>
|
CommandCloseForm = new RelayCommand((p) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace Net462DllTest.Web
|
|||||||
if (EnumHelper.TryConvertEnum<PlcVarName>(var,out var signal))
|
if (EnumHelper.TryConvertEnum<PlcVarName>(var,out var signal))
|
||||||
{
|
{
|
||||||
Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} ");
|
Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} ");
|
||||||
plcDevice.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号
|
plcDevice.Trigger(signal, value);// 通过 Web Api 模拟外部输入信号
|
||||||
return new { state = "succeed" };
|
return new { state = "succeed" };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -67,7 +67,7 @@ namespace Net462DllTest.Web
|
|||||||
if (EnumHelper.TryConvertEnum<CommandSignal>(command, out var signal))
|
if (EnumHelper.TryConvertEnum<CommandSignal>(command, out var signal))
|
||||||
{
|
{
|
||||||
Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} ");
|
Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} ");
|
||||||
viewManagement.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号
|
viewManagement.Trigger(signal, value);// 通过 Web Api 模拟外部输入信号
|
||||||
return new { state = "succeed" };
|
return new { state = "succeed" };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ using System.Threading.Tasks;
|
|||||||
namespace Net462DllTest.Web
|
namespace Net462DllTest.Web
|
||||||
{
|
{
|
||||||
|
|
||||||
[DynamicFlow("[PlcSocketService]")]
|
|
||||||
[AutoRegister]
|
[AutoRegister]
|
||||||
|
[DynamicFlow("[PlcSocketService]")]
|
||||||
[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]
|
[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]
|
||||||
public class PlcSocketService : ISocketHandleModule
|
public class PlcSocketService : ISocketHandleModule
|
||||||
{
|
{
|
||||||
@@ -42,8 +42,6 @@ namespace Net462DllTest.Web
|
|||||||
|
|
||||||
context.Env.IOC.Register<IRouter, Router>();
|
context.Env.IOC.Register<IRouter, Router>();
|
||||||
context.Env.IOC.Register<WebApiServer>();
|
context.Env.IOC.Register<WebApiServer>();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作
|
[NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作
|
||||||
@@ -90,17 +88,18 @@ namespace Net462DllTest.Web
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
[AutoSocketHandle]
|
||||||
|
public async Task BatchReadVar(Func<string, Task> SendMsg, Func<object, Task> SendObj)
|
||||||
[NodeAction(NodeType.Action, "等待")]
|
|
||||||
public async Task Delay(int ms = 5000)
|
|
||||||
{
|
{
|
||||||
await Console.Out.WriteLineAsync("开始等待");
|
await SendMsg("开始刷新数据");
|
||||||
await Task.Delay(ms);
|
//MyPlc.BatchRefresh();
|
||||||
await Console.Out.WriteLineAsync("不再等待");
|
await Task.Delay(1000);
|
||||||
|
await SendMsg("刷新完成");
|
||||||
|
await SendObj(plcVarModelDataProxy);
|
||||||
|
await SendMsg("发送完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[AutoSocketHandle]
|
[AutoSocketHandle]
|
||||||
public object ReadVar(PlcVarName varName)
|
public object ReadVar(PlcVarName varName)
|
||||||
{
|
{
|
||||||
@@ -109,6 +108,14 @@ namespace Net462DllTest.Web
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[AutoSocketHandle(IsReturnValue = false)]
|
||||||
|
public SiemensPlcDevice WriteVar(object value, PlcVarName varName)
|
||||||
|
{
|
||||||
|
MyPlc.Write(varName, value); // 新数据
|
||||||
|
return MyPlc;
|
||||||
|
}
|
||||||
[AutoSocketHandle(IsReturnValue = false)]
|
[AutoSocketHandle(IsReturnValue = false)]
|
||||||
public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None,
|
public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None,
|
||||||
string ip = "192.168.10.100",
|
string ip = "192.168.10.100",
|
||||||
@@ -144,22 +151,6 @@ namespace Net462DllTest.Web
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[AutoSocketHandle(IsReturnValue = false)]
|
|
||||||
public SiemensPlcDevice WriteVar(object value, PlcVarName varName)
|
|
||||||
{
|
|
||||||
MyPlc.Write(varName, value); // 新数据
|
|
||||||
return MyPlc;
|
|
||||||
}
|
|
||||||
|
|
||||||
[AutoSocketHandle]
|
|
||||||
public PlcVarModelDataProxy BatchReadVar(Func<string,Task> send)
|
|
||||||
{
|
|
||||||
send("开始读取");
|
|
||||||
MyPlc.BatchRefresh();
|
|
||||||
send("读取完成");
|
|
||||||
return plcVarModelDataProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OpenTimedRefresh()
|
public void OpenTimedRefresh()
|
||||||
{
|
{
|
||||||
Task.Run(async () => await MyPlc.OpenTimedRefreshAsync());
|
Task.Run(async () => await MyPlc.OpenTimedRefreshAsync());
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ namespace Serein.NodeFlow.Base
|
|||||||
internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
|
internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
|
||||||
{
|
{
|
||||||
this.Guid = nodeInfo.Guid;
|
this.Guid = nodeInfo.Guid;
|
||||||
if(this.MethodDetails is not null)
|
if (this.MethodDetails is not null)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
|
for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -104,9 +104,9 @@ namespace Serein.NodeFlow.Base
|
|||||||
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
||||||
stack.Push(this);
|
stack.Push(this);
|
||||||
var flowCts = context.Env.IOC.Get<CancellationTokenSource>(FlowStarter.FlipFlopCtsName);
|
var flowCts = context.Env.IOC.Get<CancellationTokenSource>(FlowStarter.FlipFlopCtsName);
|
||||||
while (stack.Count > 0 ) // 循环中直到栈为空才会退出循环
|
while (stack.Count > 0) // 循环中直到栈为空才会退出循环
|
||||||
{
|
{
|
||||||
if(flowCts is not null)
|
if (flowCts is not null)
|
||||||
{
|
{
|
||||||
if (flowCts.IsCancellationRequested)
|
if (flowCts.IsCancellationRequested)
|
||||||
break;
|
break;
|
||||||
@@ -118,7 +118,7 @@ namespace Serein.NodeFlow.Base
|
|||||||
|
|
||||||
// 筛选出上游分支
|
// 筛选出上游分支
|
||||||
var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].Where(
|
var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].Where(
|
||||||
node => node.DebugSetting.IsEnable
|
node => node.DebugSetting.IsEnable
|
||||||
).ToArray();
|
).ToArray();
|
||||||
// 执行上游分支
|
// 执行上游分支
|
||||||
foreach (var upstreamNode in upstreamNodes)
|
foreach (var upstreamNode in upstreamNodes)
|
||||||
@@ -144,7 +144,7 @@ namespace Serein.NodeFlow.Base
|
|||||||
|
|
||||||
// 上游分支执行完成,才执行当前节点
|
// 上游分支执行完成,才执行当前节点
|
||||||
object? newFlowData = await currentNode.ExecutingAsync(context);
|
object? newFlowData = await currentNode.ExecutingAsync(context);
|
||||||
if (flowCts is null || flowCts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None)
|
if (flowCts is null || flowCts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None)
|
||||||
{
|
{
|
||||||
// 不再执行
|
// 不再执行
|
||||||
break;
|
break;
|
||||||
@@ -207,11 +207,8 @@ namespace Serein.NodeFlow.Base
|
|||||||
|
|
||||||
object? result = null;
|
object? result = null;
|
||||||
|
|
||||||
//Console.WriteLine($"(isTask, isTaskHaveResult):{(isTask, isTaskHaveResult)}");
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Action/Func([方法作用的实例],[可能的参数值],[可能的返回值])
|
|
||||||
|
|
||||||
object?[]? args = GetParameters(context, this, md);
|
object?[]? args = GetParameters(context, this, md);
|
||||||
result = await dd.Invoke(md.ActingInstance, args);
|
result = await dd.Invoke(md.ActingInstance, args);
|
||||||
NextOrientation = ConnectionType.IsSucceed;
|
NextOrientation = ConnectionType.IsSucceed;
|
||||||
@@ -219,7 +216,7 @@ namespace Serein.NodeFlow.Base
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex.Message);
|
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
||||||
NextOrientation = ConnectionType.IsError;
|
NextOrientation = ConnectionType.IsError;
|
||||||
RuningException = ex;
|
RuningException = ex;
|
||||||
return null;
|
return null;
|
||||||
@@ -268,8 +265,8 @@ namespace Serein.NodeFlow.Base
|
|||||||
inputParameter = flowData; // 使用上一节点的对象
|
inputParameter = flowData; // 使用上一节点的对象
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存在转换器
|
// 入参存在取值转换器
|
||||||
if (ed.Convertor is not null)
|
if (ed.ExplicitType.IsEnum && ed.Convertor is not null)
|
||||||
{
|
{
|
||||||
if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum))
|
if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum))
|
||||||
{
|
{
|
||||||
@@ -286,15 +283,17 @@ namespace Serein.NodeFlow.Base
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ed.DataType != ed.ExplicitType) // 获取枚举转换器中记录的枚举
|
// 入参存在类型转换器,获取枚举转换器中记录的枚举
|
||||||
|
if (ed.ExplicitType.IsEnum && ed.DataType != ed.ExplicitType)
|
||||||
{
|
{
|
||||||
if (ed.ExplicitType.IsEnum && Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum)) // 获取对应的枚举项
|
if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum)) // 获取对应的枚举项
|
||||||
{
|
{
|
||||||
|
// 获取绑定的类型
|
||||||
var type = EnumHelper.GetBoundValue(ed.ExplicitType, resultEnum, attr => attr.Value);
|
var type = EnumHelper.GetBoundValue(ed.ExplicitType, resultEnum, attr => attr.Value);
|
||||||
if(type is Type enumBindType && enumBindType is not null)
|
if (type is Type enumBindType && enumBindType is not null)
|
||||||
{
|
{
|
||||||
var value = context.Env.IOC.Instantiate(enumBindType);
|
var value = context.Env.IOC.Instantiate(enumBindType);
|
||||||
if(value is not null)
|
if (value is not null)
|
||||||
{
|
{
|
||||||
parameters[i] = value;
|
parameters[i] = value;
|
||||||
continue;
|
continue;
|
||||||
@@ -306,76 +305,35 @@ namespace Serein.NodeFlow.Base
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (ed.DataType.IsValueType)
|
||||||
{
|
{
|
||||||
|
var valueStr = inputParameter?.ToString();
|
||||||
if (ed.DataType.IsValueType)
|
parameters[i] = valueStr.ToValueData(ed.DataType);
|
||||||
{
|
|
||||||
if (inputParameter is null)
|
|
||||||
{
|
|
||||||
parameters[i] = Activator.CreateInstance(ed.DataType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string? valueStr = inputParameter?.ToString();
|
|
||||||
if (string.IsNullOrEmpty(valueStr))
|
|
||||||
{
|
|
||||||
parameters[i] = Activator.CreateInstance(ed.DataType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parameters[i] = ed.DataType switch
|
|
||||||
{
|
|
||||||
Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举
|
|
||||||
Type t when t == typeof(char) => char.Parse(valueStr),
|
|
||||||
Type t when t == typeof(bool) => bool.Parse(valueStr),
|
|
||||||
Type t when t == typeof(float) => float.Parse(valueStr),
|
|
||||||
Type t when t == typeof(decimal) => decimal.Parse(valueStr),
|
|
||||||
Type t when t == typeof(double) => double.Parse(valueStr),
|
|
||||||
Type t when t == typeof(sbyte) => sbyte.Parse(valueStr),
|
|
||||||
Type t when t == typeof(byte) => byte.Parse(valueStr),
|
|
||||||
Type t when t == typeof(short) => short.Parse(valueStr),
|
|
||||||
Type t when t == typeof(ushort) => ushort.Parse(valueStr),
|
|
||||||
Type t when t == typeof(int) => int.Parse(valueStr),
|
|
||||||
Type t when t == typeof(uint) => uint.Parse(valueStr),
|
|
||||||
Type t when t == typeof(long) => long.Parse(valueStr),
|
|
||||||
Type t when t == typeof(ulong) => ulong.Parse(valueStr),
|
|
||||||
Type t when t == typeof(nint) => nint.Parse(valueStr),
|
|
||||||
Type t when t == typeof(nuint) => nuint.Parse(valueStr),
|
|
||||||
_ => throw new Exception($"调用节点对应方法[{nodeModel.MethodDetails.MethodName}]时,遇到了未在预期内的值类型入参:{ed.DataType.FullName}"),
|
|
||||||
// Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parameters[i] = ed.DataType switch
|
|
||||||
{
|
|
||||||
Type t when t == typeof(IDynamicContext) => context, // 上下文
|
|
||||||
Type t when t == typeof(string) => inputParameter?.ToString(),
|
|
||||||
|
|
||||||
//Type t when t == typeof(DateTime) => string.IsNullOrEmpty(valueStr) ? 0 : DateTime.Parse(valueStr),
|
|
||||||
|
|
||||||
Type t when t == typeof(MethodDetails) => md, // 节点方法描述
|
|
||||||
Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类
|
|
||||||
|
|
||||||
Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
|
|
||||||
Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
|
|
||||||
_ => inputParameter,
|
|
||||||
// Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) // 节点参数类型转换异常
|
else
|
||||||
{
|
{
|
||||||
parameters[i] = new object();
|
var valueStr = inputParameter?.ToString();
|
||||||
Console.WriteLine(ex);
|
parameters[i] = ed.DataType switch
|
||||||
|
{
|
||||||
|
Type t when t == typeof(string) => valueStr,
|
||||||
|
Type t when t == typeof(IDynamicContext) => context, // 上下文
|
||||||
|
Type t when t == typeof(DateTime) => string.IsNullOrEmpty(valueStr) ? 0 : DateTime.Parse(valueStr),
|
||||||
|
|
||||||
|
Type t when t == typeof(MethodDetails) => md, // 节点方法描述
|
||||||
|
Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类
|
||||||
|
|
||||||
|
Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
|
||||||
|
Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
|
||||||
|
_ => inputParameter,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
@@ -384,10 +342,10 @@ namespace Serein.NodeFlow.Base
|
|||||||
/// 更新节点数据,并检查监视表达式是否生效
|
/// 更新节点数据,并检查监视表达式是否生效
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="newData"></param>
|
/// <param name="newData"></param>
|
||||||
public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context,NodeModelBase nodeModel, object? newData = null)
|
public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object? newData = null)
|
||||||
{
|
{
|
||||||
string guid = nodeModel.Guid;
|
string guid = nodeModel.Guid;
|
||||||
if(newData is not null)
|
if (newData is not null)
|
||||||
{
|
{
|
||||||
await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象
|
await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象
|
||||||
await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点
|
await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点
|
||||||
@@ -399,7 +357,7 @@ namespace Serein.NodeFlow.Base
|
|||||||
{
|
{
|
||||||
MonitorObjectEventArgs.ObjSourceType sourceType;
|
MonitorObjectEventArgs.ObjSourceType sourceType;
|
||||||
string? key;
|
string? key;
|
||||||
if(monitorType == 0)
|
if (monitorType == 0)
|
||||||
{
|
{
|
||||||
key = data?.GetType()?.FullName;
|
key = data?.GetType()?.FullName;
|
||||||
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
|
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
|
||||||
|
|||||||
@@ -43,6 +43,23 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var getObjExp = CustomData?.ToString();
|
||||||
|
|
||||||
|
if (IsCustomData && !string.IsNullOrEmpty(getObjExp) && getObjExp.Length >= 4)
|
||||||
|
{
|
||||||
|
|
||||||
|
var ExpOpOption = getObjExp[..4];
|
||||||
|
if(ExpOpOption.ToLower() == "@get")
|
||||||
|
{
|
||||||
|
result = PreviousNode?.GetFlowData();
|
||||||
|
if (result is not null)
|
||||||
|
{
|
||||||
|
result = SerinExpressionEvaluator.Evaluate(getObjExp, result, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
var isPass = SereinConditionParser.To(result, Expression);
|
var isPass = SereinConditionParser.To(result, Expression);
|
||||||
NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail;
|
NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Serein.Library.Entity;
|
using Serein.Library.Entity;
|
||||||
using Serein.Library.Enums;
|
using Serein.Library.Enums;
|
||||||
using Serein.Library.Ex;
|
using Serein.Library.Ex;
|
||||||
|
using Serein.Library.NodeFlow.Tool;
|
||||||
using Serein.Library.Utils;
|
using Serein.Library.Utils;
|
||||||
using Serein.NodeFlow.Base;
|
using Serein.NodeFlow.Base;
|
||||||
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
||||||
@@ -42,43 +43,14 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
var args = GetParameters(context, this, md);
|
var args = GetParameters(context, this, md);
|
||||||
var result = await dd.Invoke(md.ActingInstance, args);
|
var result = await dd.Invoke(md.ActingInstance, args);
|
||||||
if (result is IFlipflopContext flipflopContext)
|
dynamic flipflopContext = result;
|
||||||
|
FlipflopStateType flipflopStateType = flipflopContext.State;
|
||||||
|
NextOrientation = flipflopStateType.ToContentType();
|
||||||
|
if (flipflopContext.Type == TriggerType.Overtime)
|
||||||
{
|
{
|
||||||
NextOrientation = flipflopContext.State.ToContentType();
|
throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
||||||
if (flipflopContext.TriggerData is null || flipflopContext.TriggerData.Type == Library.NodeFlow.Tool.TriggerType.Overtime)
|
|
||||||
{
|
|
||||||
throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
|
||||||
}
|
|
||||||
return flipflopContext.TriggerData.Value;
|
|
||||||
}
|
}
|
||||||
else
|
return flipflopContext.Value;
|
||||||
{
|
|
||||||
throw new FlipflopException("触发器节点返回了非预期的类型", true, FlipflopException.CancelClass.Flow);
|
|
||||||
}
|
|
||||||
// Task<IFlipflopContext> flipflopTask;
|
|
||||||
//var delType = dd.EmitMethodType;
|
|
||||||
//var del = dd.EmitDelegate;
|
|
||||||
//if (delType == EmitHelper.EmitMethodType.HasResultTask && del is Func<object, object?[]?, Task<object>> hasResultTask)
|
|
||||||
//{
|
|
||||||
// var flipflopTaskObj = await hasResultTask(instance, args);
|
|
||||||
// if(flipflopTaskObj is IFlipflopContext flipflopContext)
|
|
||||||
// {
|
|
||||||
// NextOrientation = flipflopContext.State.ToContentType();
|
|
||||||
// if (flipflopContext.TriggerData is null || flipflopContext.TriggerData.Type == Library.NodeFlow.Tool.TriggerType.Overtime)
|
|
||||||
// {
|
|
||||||
// throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
|
||||||
// }
|
|
||||||
// return flipflopContext.TriggerData.Value;
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// throw new FlipflopException("触发器节点返回了非预期的类型", true, FlipflopException.CancelClass.Flow);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// throw new FlipflopException("触发器节点构造了非预期的委托", true, FlipflopException.CancelClass.Flow);
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (FlipflopException ex)
|
catch (FlipflopException ex)
|
||||||
@@ -87,14 +59,14 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex.Message);
|
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
||||||
NextOrientation = ConnectionType.None;
|
NextOrientation = ConnectionType.None;
|
||||||
RuningException = ex;
|
RuningException = ex;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex.Message);
|
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
||||||
NextOrientation = ConnectionType.IsError;
|
NextOrientation = ConnectionType.IsError;
|
||||||
RuningException = ex;
|
RuningException = ex;
|
||||||
return null;
|
return null;
|
||||||
@@ -104,7 +76,10 @@ namespace Serein.NodeFlow.Model
|
|||||||
// flipflopTask?.Dispose();
|
// flipflopTask?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static object GetContextValueDynamic(dynamic context)
|
||||||
|
{
|
||||||
|
return context.Value; // dynamic 会在运行时处理类型
|
||||||
|
}
|
||||||
internal override Parameterdata[] GetParameterdatas()
|
internal override Parameterdata[] GetParameterdatas()
|
||||||
{
|
{
|
||||||
if (base.MethodDetails.ExplicitDatas.Length > 0)
|
if (base.MethodDetails.ExplicitDatas.Length > 0)
|
||||||
|
|||||||
@@ -79,11 +79,31 @@ public static class NodeMethodDetailsHelper
|
|||||||
|
|
||||||
if (attribute.MethodDynamicType == Library.Enums.NodeType.Flipflop)
|
if (attribute.MethodDynamicType == Library.Enums.NodeType.Flipflop)
|
||||||
{
|
{
|
||||||
returnType = attribute.ReturnType;
|
if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||||
if (!isTask || taskResult != typeof(IFlipflopContext))
|
|
||||||
{
|
{
|
||||||
Console.WriteLine($"触发器节点的返回类型非预期类型,可能会导致流程异常。[{dllTypeMethodName}]当前返回类型为[{method.ReturnType}],而预期的返回类型应为[Task<IFlipflopContext>]");
|
// 获取 Task<> 的泛型参数类型
|
||||||
|
var innerType = method.ReturnType.GetGenericArguments()[0];
|
||||||
|
if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(IFlipflopContext<>))
|
||||||
|
{
|
||||||
|
var flipflopType = innerType.GetGenericArguments()[0];
|
||||||
|
returnType = flipflopType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[{dllTypeMethodName}]跳过创建,返回类型非预期的Task<IFlipflopContext<TResult>>。");
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[{dllTypeMethodName}]跳过创建,因为触发器方法的返回值并非Task<>,将无法等待。");
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (!isTask || taskResult != typeof(IFlipflopContext<object>))
|
||||||
|
//{
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if(isTask)
|
else if(isTask)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Serein.Library.Utils;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -54,46 +55,73 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
|||||||
|
|
||||||
public override bool Evaluate(object obj)
|
public override bool Evaluate(object obj)
|
||||||
{
|
{
|
||||||
if (obj is T typedObj)
|
|
||||||
|
var evaluatedValue = obj.ToConvert<T>();
|
||||||
|
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
||||||
{
|
{
|
||||||
double numericValue = Convert.ToDouble(typedObj);
|
evaluatedValue = SerinArithmeticExpressionEvaluator<T>.Evaluate(ArithmeticExpression, evaluatedValue);
|
||||||
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
}
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T));
|
switch (Op)
|
||||||
|
{
|
||||||
/*return Op switch
|
case Operator.GreaterThan:
|
||||||
{
|
return evaluatedValue.CompareTo(Value) > 0;
|
||||||
Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0,
|
case Operator.LessThan:
|
||||||
Operator.LessThan => evaluatedValue.CompareTo(Value) < 0,
|
return evaluatedValue.CompareTo(Value) < 0;
|
||||||
Operator.Equal => evaluatedValue.CompareTo(Value) == 0,
|
case Operator.Equal:
|
||||||
Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0,
|
return evaluatedValue.CompareTo(Value) == 0;
|
||||||
Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0,
|
case Operator.GreaterThanOrEqual:
|
||||||
Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0,
|
return evaluatedValue.CompareTo(Value) >= 0;
|
||||||
Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0,
|
case Operator.LessThanOrEqual:
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
return evaluatedValue.CompareTo(Value) <= 0;
|
||||||
};*/
|
case Operator.InRange:
|
||||||
switch (Op)
|
return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
|
||||||
{
|
case Operator.OutOfRange:
|
||||||
case Operator.GreaterThan:
|
return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
|
||||||
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;
|
return false;
|
||||||
|
|
||||||
|
//if (obj is T typedObj)
|
||||||
|
//{
|
||||||
|
// numericValue = Convert.ToDouble(typedObj);
|
||||||
|
// numericValue = Convert.ToDouble(obj);
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Serein.Library.Utils;
|
||||||
using Serein.NodeFlow.Tool.SereinExpression.Resolver;
|
using Serein.NodeFlow.Tool.SereinExpression.Resolver;
|
||||||
using System.ComponentModel.Design;
|
using System.ComponentModel.Design;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -31,7 +32,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
|
|
||||||
public static SereinConditionResolver ConditionParse(object? data, string expression)
|
public static SereinConditionResolver ConditionParse(object? data, string expression)
|
||||||
{
|
{
|
||||||
if (expression.StartsWith('.') || expression.StartsWith('<')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
||||||
{
|
{
|
||||||
return ParseObjectExpression(data, expression);
|
return ParseObjectExpression(data, expression);
|
||||||
}
|
}
|
||||||
@@ -138,11 +139,22 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
}
|
}
|
||||||
Type? tempType = typeStr switch
|
Type? tempType = typeStr switch
|
||||||
{
|
{
|
||||||
"int" => typeof(int),
|
|
||||||
"double" => typeof(double),
|
|
||||||
"bool" => typeof(bool),
|
"bool" => typeof(bool),
|
||||||
|
"float" => typeof(float),
|
||||||
|
"decimal" => typeof(decimal),
|
||||||
|
"double" => typeof(double),
|
||||||
|
"sbyte" => typeof(sbyte),
|
||||||
|
"byte" => typeof(byte),
|
||||||
|
"short" => typeof(short),
|
||||||
|
"ushort" => typeof(ushort),
|
||||||
|
"int" => typeof(int),
|
||||||
|
"uint" => typeof(uint),
|
||||||
|
"long" => typeof(long),
|
||||||
|
"ulong" => typeof(ulong),
|
||||||
|
"nint" => typeof(nint),
|
||||||
|
"nuint" => typeof(nuint),
|
||||||
"string" => typeof(string),
|
"string" => typeof(string),
|
||||||
_ => Type.GetType(typeStr)
|
_ => Type.GetType(typeStr),
|
||||||
};
|
};
|
||||||
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
||||||
if (string.IsNullOrWhiteSpace(memberPath))
|
if (string.IsNullOrWhiteSpace(memberPath))
|
||||||
@@ -157,6 +169,10 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region 解析类型 int
|
#region 解析类型 int
|
||||||
|
if (type.IsValueType)
|
||||||
|
{
|
||||||
|
//return GetValueResolver(type, valueStr, operatorStr, parts);
|
||||||
|
}
|
||||||
if (type == typeof(int))
|
if (type == typeof(int))
|
||||||
{
|
{
|
||||||
var op = ParseValueTypeOperator<int>(operatorStr);
|
var op = ParseValueTypeOperator<int>(operatorStr);
|
||||||
@@ -278,52 +294,76 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
if (parts.Length < 2)
|
if (parts.Length < 2)
|
||||||
throw new ArgumentException("无效的表达式格式。");
|
throw new ArgumentException("无效的表达式格式。");
|
||||||
|
|
||||||
//string typeStr = parts[0];
|
string operatorStr;
|
||||||
string operatorStr = parts[0];
|
string valueStr;
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
Type type;
|
||||||
|
// 尝试获取指定类型
|
||||||
Type type = data.GetType();//Type.GetType(typeStr);
|
int typeStartIndex = expression.IndexOf('<');
|
||||||
if (type == typeof(int))
|
int typeEndIndex = expression.IndexOf('>');
|
||||||
|
if (typeStartIndex + typeStartIndex == -2)
|
||||||
{
|
{
|
||||||
var op = ParseValueTypeOperator<int>(operatorStr);
|
// 如果不需要转为指定类型
|
||||||
if (op == ValueTypeConditionResolver<int>.Operator.InRange || op == ValueTypeConditionResolver<int>.Operator.OutOfRange)
|
operatorStr = parts[0];
|
||||||
{
|
valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
||||||
var temp = valueStr.Split('-');
|
type = data.GetType();
|
||||||
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))
|
else
|
||||||
{
|
{//string typeStr = parts[0];
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1)
|
||||||
return new ValueTypeConditionResolver<double>
|
.Trim().ToLower(); // 手动置顶的类型
|
||||||
|
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
|
||||||
|
operatorStr = parts[0].ToLower(); // 操作类型
|
||||||
|
valueStr = string.Join(' ', parts.Skip(1)); // 表达式值
|
||||||
|
|
||||||
|
|
||||||
|
type = typeStr switch
|
||||||
{
|
{
|
||||||
Op = ParseValueTypeOperator<double>(operatorStr),
|
"bool" => typeof(bool),
|
||||||
Value = value,
|
"float" => typeof(float),
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
"decimal" => typeof(decimal),
|
||||||
|
"double" => typeof(double),
|
||||||
|
"sbyte" => typeof(sbyte),
|
||||||
|
"byte" => typeof(byte),
|
||||||
|
"short" => typeof(short),
|
||||||
|
"ushort" => typeof(ushort),
|
||||||
|
"int" => typeof(int),
|
||||||
|
"uint" => typeof(uint),
|
||||||
|
"long" => typeof(long),
|
||||||
|
"ulong" => typeof(ulong),
|
||||||
|
"nint" => typeof(nint),
|
||||||
|
"nuint" => typeof(nuint),
|
||||||
|
_ => typeof(string),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (type == typeof(bool))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (type == typeof(bool))
|
||||||
{
|
{
|
||||||
bool value = bool.Parse(valueStr);
|
bool value = bool.Parse(valueStr);
|
||||||
return new BoolConditionResolver
|
return new BoolConditionResolver
|
||||||
@@ -332,6 +372,10 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
Value = value,
|
Value = value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else if (type.IsValueType)
|
||||||
|
{
|
||||||
|
return GetValueResolver(type, valueStr, operatorStr, parts);
|
||||||
|
}
|
||||||
else if (type == typeof(string))
|
else if (type == typeof(string))
|
||||||
{
|
{
|
||||||
return new StringConditionResolver
|
return new StringConditionResolver
|
||||||
@@ -344,6 +388,133 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
throw new NotSupportedException($"Type {type} is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SereinConditionResolver GetValueResolver(Type valueType, string valueStr, string operatorStr, string[] parts)// where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
SereinConditionResolver resolver = valueType switch
|
||||||
|
{
|
||||||
|
Type t when t == typeof(float) => GetValueResolver<float>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(decimal) => GetValueResolver<decimal>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(double) => GetValueResolver<double>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(sbyte) => GetValueResolver<sbyte>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(byte) => GetValueResolver<byte>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(short) => GetValueResolver<short>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(ushort) => GetValueResolver<ushort>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(int) => GetValueResolver<int>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(uint) => GetValueResolver<uint>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(long) => GetValueResolver<long>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(ulong) => GetValueResolver<ulong>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(nint) => GetValueResolver<nint>(valueStr, operatorStr, parts),
|
||||||
|
Type t when t == typeof(nuint) => GetValueResolver<nuint>(valueStr, operatorStr, parts),
|
||||||
|
_ => throw new ArgumentException("非预期值类型")
|
||||||
|
};
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static ValueTypeConditionResolver<T> GetValueResolver<T>(string valueStr, string operatorStr, string[] parts)
|
||||||
|
where T :struct, IComparable<T>
|
||||||
|
{
|
||||||
|
var op = ParseValueTypeOperator<T>(operatorStr);
|
||||||
|
if (op == ValueTypeConditionResolver<T>.Operator.InRange || op == ValueTypeConditionResolver<T>.Operator.OutOfRange)
|
||||||
|
{
|
||||||
|
var temp = valueStr.Split('-');
|
||||||
|
var leftNum = string.Empty;
|
||||||
|
var rightNum = string.Empty;
|
||||||
|
if (temp.Length < 2 || temp.Length > 4)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"范围无效:{valueStr}。");
|
||||||
|
}
|
||||||
|
else if (temp.Length == 2)
|
||||||
|
{
|
||||||
|
leftNum = temp[0];
|
||||||
|
rightNum = temp[1];
|
||||||
|
}
|
||||||
|
else if (temp.Length == 3)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(temp[0])
|
||||||
|
&& !string.IsNullOrEmpty(temp[1])
|
||||||
|
&& !string.IsNullOrEmpty(temp[2]))
|
||||||
|
{
|
||||||
|
leftNum = "-" + temp[1];
|
||||||
|
rightNum = temp[2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"范围无效:{valueStr}。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (temp.Length == 4)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(temp[0])
|
||||||
|
&& !string.IsNullOrEmpty(temp[1])
|
||||||
|
&& string.IsNullOrEmpty(temp[2])
|
||||||
|
&& !string.IsNullOrEmpty(temp[3]))
|
||||||
|
{
|
||||||
|
leftNum = "-" + temp[1];
|
||||||
|
rightNum = temp[3];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"范围无效:{valueStr}。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return new ValueTypeConditionResolver<T>
|
||||||
|
{
|
||||||
|
Op = op,
|
||||||
|
RangeStart = leftNum.ToValueData<T>(),
|
||||||
|
RangeEnd = rightNum.ToValueData<T>(),
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new ValueTypeConditionResolver<T>
|
||||||
|
{
|
||||||
|
Op = op,
|
||||||
|
Value = valueStr.ToValueData<T>(),
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//public static T ValueParse<T>(object value) where T : struct, IComparable<T>
|
||||||
|
//{
|
||||||
|
// return (T)ValueParse(typeof(T), value);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public static object ValueParse(Type type, object value)
|
||||||
|
//{
|
||||||
|
|
||||||
|
// string? valueStr = value.ToString();
|
||||||
|
// if (string.IsNullOrEmpty(valueStr))
|
||||||
|
// {
|
||||||
|
// throw new ArgumentException("value is null");
|
||||||
|
// }
|
||||||
|
// object result = type switch
|
||||||
|
// {
|
||||||
|
// Type t when t.IsEnum => Enum.Parse(type, valueStr),
|
||||||
|
// Type t when t == typeof(bool) => bool.Parse(valueStr),
|
||||||
|
// Type t when t == typeof(float) => float.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(decimal) => decimal.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(double) => double.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(sbyte) => sbyte.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(byte) => byte.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(short) => short.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(ushort) => ushort.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(int) => int.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(uint) => uint.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(long) => long.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(ulong) => ulong.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(nint) => nint.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// Type t when t == typeof(nuint) => nuint.Parse(valueStr, CultureInfo.InvariantCulture),
|
||||||
|
// _ => throw new ArgumentException("非预期值类型")
|
||||||
|
// };
|
||||||
|
// return result;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Data;
|
using Serein.Library.Utils;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool.SereinExpression
|
namespace Serein.NodeFlow.Tool.SereinExpression
|
||||||
{
|
{
|
||||||
@@ -9,19 +10,20 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">操作的对象</param>
|
/// <param name="obj">操作的对象</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public class SerinArithmeticExpressionEvaluator
|
public class SerinArithmeticExpressionEvaluator<T> where T : struct, IComparable<T>
|
||||||
{
|
{
|
||||||
private static readonly DataTable table = new DataTable();
|
private static readonly DataTable table = new DataTable();
|
||||||
|
|
||||||
public static double Evaluate(string expression, double inputValue)
|
public static T Evaluate(string expression, T inputValue)
|
||||||
{
|
{
|
||||||
|
|
||||||
// 替换占位符@为输入值
|
// 替换占位符@为输入值
|
||||||
expression = expression.Replace("@", inputValue.ToString());
|
expression = expression.Replace("@", inputValue.ToString());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 使用 DataTable.Compute 方法计算表达式
|
// 使用 DataTable.Compute 方法计算表达式
|
||||||
var result = table.Compute(expression, string.Empty);
|
var result = table.Compute(expression, string.Empty);
|
||||||
return Convert.ToDouble(result);
|
return (T)result;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -84,8 +86,9 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
/// <param name="methodCall">方法名称</param>
|
/// <param name="methodCall">方法名称</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="ArgumentException"></exception>
|
/// <exception cref="ArgumentException"></exception>
|
||||||
private static object? InvokeMethod(object target, string methodCall)
|
private static object? InvokeMethod(object? target, string methodCall)
|
||||||
{
|
{
|
||||||
|
if (target is null) return null;
|
||||||
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (methodParts.Length != 2)
|
if (methodParts.Length != 2)
|
||||||
{
|
{
|
||||||
@@ -344,15 +347,15 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
/// <param name="expression"></param>
|
/// <param name="expression"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static double ComputedNumber(object value, string expression)
|
private static decimal ComputedNumber(object value, string expression)
|
||||||
{
|
{
|
||||||
double numericValue = Convert.ToDouble(value);
|
return ComputedNumber<decimal>(value, expression);
|
||||||
if (!string.IsNullOrEmpty(expression))
|
}
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return numericValue;
|
private static T ComputedNumber<T>(object value, string expression) where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
T result = value.ToConvert<T>();
|
||||||
|
return SerinArithmeticExpressionEvaluator<T>.Evaluate(expression, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user