mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-20 00:06:45 +08:00
重写了触发器底层逻辑
This commit is contained in:
@@ -38,6 +38,11 @@ namespace Serein.Library.Core.NodeFlow
|
||||
{
|
||||
// 获取 Task<> 的泛型参数类型
|
||||
var innerType = type.GetGenericArguments()[0];
|
||||
if (innerType.IsGenericType && type.GetGenericTypeDefinition() == typeof(IFlipflopContext<>))
|
||||
{
|
||||
var flipflop = type.GetGenericArguments()[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断 innerType 是否继承 IFlipflopContext
|
||||
//if (typeof(IFlipflopContext).IsAssignableFrom(innerType))
|
||||
@@ -50,11 +55,11 @@ namespace Serein.Library.Core.NodeFlow
|
||||
//}
|
||||
|
||||
// 检查泛型参数是否为 Flipflop<>
|
||||
if (innerType == typeof(IFlipflopContext))
|
||||
//if (innerType == typeof(IFlipflopContext))
|
||||
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//{
|
||||
//return true;
|
||||
//}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -64,20 +69,21 @@ namespace Serein.Library.Core.NodeFlow
|
||||
/// <summary>
|
||||
/// 触发器上下文
|
||||
/// </summary>
|
||||
public class FlipflopContext : IFlipflopContext
|
||||
public class FlipflopContext<TResult> : IFlipflopContext<TResult>
|
||||
{
|
||||
public FlipflopStateType State { get; set; }
|
||||
|
||||
public TriggerData TriggerData { get; set; }
|
||||
public TriggerType Type { get; set; }
|
||||
public TResult Value { get; set; }
|
||||
|
||||
public FlipflopContext(FlipflopStateType ffState)
|
||||
{
|
||||
State = ffState;
|
||||
}
|
||||
public FlipflopContext(FlipflopStateType ffState, TriggerData data)
|
||||
public FlipflopContext(FlipflopStateType ffState, TResult value)
|
||||
{
|
||||
State = ffState;
|
||||
TriggerData = data;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -40,13 +40,18 @@ namespace Serein.Library.Framework.NodeFlow
|
||||
{
|
||||
// 获取 Task<> 的泛型参数类型
|
||||
var innerType = type.GetGenericArguments()[0];
|
||||
|
||||
// 检查泛型参数是否为 Flipflop<>
|
||||
if (innerType == typeof(FlipflopContext))
|
||||
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||
if (innerType.IsGenericType && type.GetGenericTypeDefinition() == typeof(IFlipflopContext<>))
|
||||
{
|
||||
var flipflop = type.GetGenericArguments()[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
//// 检查泛型参数是否为 Flipflop<>
|
||||
//if (innerType == typeof(FlipflopContext))
|
||||
////if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -56,24 +61,22 @@ namespace Serein.Library.Framework.NodeFlow
|
||||
/// <summary>
|
||||
/// 触发器上下文
|
||||
/// </summary>
|
||||
public class FlipflopContext : IFlipflopContext
|
||||
public class FlipflopContext<TResult> : IFlipflopContext<TResult>
|
||||
{
|
||||
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)
|
||||
{
|
||||
State = ffState;
|
||||
}
|
||||
public FlipflopContext(FlipflopStateType ffState, object data)
|
||||
|
||||
public FlipflopContext(FlipflopStateType ffState, TResult value)
|
||||
{
|
||||
State = ffState;
|
||||
TriggerData = new TriggerData
|
||||
{
|
||||
Type = TriggerType.External,
|
||||
Value = data
|
||||
};
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,24 @@ using Serein.Library.NodeFlow.Tool;
|
||||
|
||||
namespace Serein.Library.Api
|
||||
{
|
||||
public interface IFlipflopContext
|
||||
/// <summary>
|
||||
/// 触发器必须使用该接口作为返回值,同时必须用Task泛型表示,否则将不会进行等待触发。
|
||||
/// </summary>
|
||||
public interface IFlipflopContext<out TResult>
|
||||
{
|
||||
/// <summary>
|
||||
/// 触发器完成的状态(根据业务场景手动设置)
|
||||
/// </summary>
|
||||
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,
|
||||
|
||||
/// <summary>
|
||||
/// 触发器(建议手动设置返回类型,方便视图直观)
|
||||
/// 触发器(返回值必须为Task<IFlipflopContext<TResult>>)
|
||||
/// </summary>
|
||||
Flipflop,
|
||||
/// <summary>
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Serein.Library.Web
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 自动注册控制器
|
||||
/// 标记该类为 Web Api 处理类
|
||||
/// </summary>
|
||||
public class AutoHostingAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -13,14 +13,14 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
/// <para>使用方式:</para>
|
||||
/// <para>[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]</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> "data": { // 【DataKey】 </para>
|
||||
/// <para> "name":"张三", </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>
|
||||
///
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class AutoSocketModuleAttribute : Attribute
|
||||
|
||||
@@ -24,7 +24,16 @@ namespace Serein.Library.Network.WebSocketCommunication
|
||||
{
|
||||
listener = new HttpListener();
|
||||
listener.Prefixes.Add(url);
|
||||
listener.Start();
|
||||
try
|
||||
{
|
||||
listener.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Out.WriteLineAsync(ex.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
@@ -104,10 +104,6 @@ namespace Serein.Library.Attributes
|
||||
/// </summary>
|
||||
public NodeType MethodDynamicType;
|
||||
/// <summary>
|
||||
/// 显示标注方法返回类型,不影响运行逻辑(用于表示触发器触发后返回的数据)
|
||||
/// </summary>
|
||||
public Type ReturnType;
|
||||
/// <summary>
|
||||
/// 暂无意义
|
||||
/// </summary>
|
||||
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 表达式
|
||||
var lambda = Expression.Lambda<Func<object, object[], Task<IFlipflopContext>>>(
|
||||
Expression.Convert(methodCall, typeof(Task<IFlipflopContext>)),
|
||||
var lambda = Expression.Lambda<Func<object, object[], Task<IFlipflopContext<object>>>>(
|
||||
Expression.Convert(methodCall, typeof(Task<IFlipflopContext<object>>)),
|
||||
instanceParam,
|
||||
argsParam
|
||||
);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
@@ -22,28 +24,29 @@ namespace Serein.Library.NodeFlow.Tool
|
||||
Overtime
|
||||
}
|
||||
|
||||
public class TriggerData
|
||||
public class TriggerResult<T>
|
||||
{
|
||||
public TriggerType Type { get; set; }
|
||||
public object Value { get; set; }
|
||||
public T Value { get; set; }
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// 获取或创建指定信号的 Subject(消息广播者)
|
||||
/// </summary>
|
||||
/// <param name="signal">枚举信号标识符</param>
|
||||
/// <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>
|
||||
@@ -52,9 +55,10 @@ namespace Serein.Library.NodeFlow.Tool
|
||||
/// <param name="signal">枚举信号标识符</param>
|
||||
/// <param name="observer">订阅者</param>
|
||||
/// <returns>取消订阅的句柄</returns>
|
||||
public IDisposable Subscribe(TSignal signal, IObserver<TriggerData> observer)
|
||||
public IDisposable Subscribe(TSignal signal, IObserver<(TriggerType, object)> observer)
|
||||
{
|
||||
var subject = GetOrCreateSubject(signal);
|
||||
// (IObserver<(TriggerType, object)>)
|
||||
return subject.Subscribe(observer); // 返回取消订阅的句柄
|
||||
}
|
||||
|
||||
@@ -63,8 +67,8 @@ namespace Serein.Library.NodeFlow.Tool
|
||||
/// </summary>
|
||||
/// <param name="signal">枚举信号标识符</param>
|
||||
/// <param name="outTime">超时时间</param>
|
||||
/// <returns>等待任务</returns>
|
||||
public async Task<TriggerData> CreateChannelWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime, TResult outValue)
|
||||
/// <returns>等待任务,返回值为:状态(超时触发,手动触发),数据(超时触发时会使用设置好的数据)</returns>
|
||||
public async Task<(TriggerType, TResult)> CreateTaskWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime, TResult outValue)
|
||||
{
|
||||
var subject = GetOrCreateSubject(signal);
|
||||
var cts = new CancellationTokenSource();
|
||||
@@ -77,12 +81,7 @@ namespace Serein.Library.NodeFlow.Tool
|
||||
await Task.Delay(outTime, cts.Token);
|
||||
if (!cts.IsCancellationRequested) // 如果还没有被取消
|
||||
{
|
||||
TriggerData triggerData = new TriggerData()
|
||||
{
|
||||
Value = outValue,
|
||||
Type = TriggerType.Overtime,
|
||||
};
|
||||
subject.OnNext(triggerData); // 广播给所有订阅者
|
||||
subject.OnNext((TriggerType.Overtime, outValue)); // 广播给所有订阅者
|
||||
subject.OnCompleted(); // 通知订阅结束
|
||||
}
|
||||
}
|
||||
@@ -96,39 +95,54 @@ namespace Serein.Library.NodeFlow.Tool
|
||||
}
|
||||
}, cts.Token);
|
||||
|
||||
// 返回一个等待的任务
|
||||
return await WaitForSignalAsync(signal);
|
||||
var result = await WaitSignalAsync<TResult>(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>
|
||||
/// <param name="signal">枚举信号标识符</param>
|
||||
/// <returns>等待任务</returns>
|
||||
public async Task<TriggerData> WaitForSignalAsync(TSignal signal)
|
||||
public async Task<(TriggerType, TResult)> WaitSignalAsync<TResult>(TSignal signal)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<TriggerData>();
|
||||
var subscription = Subscribe(signal, new Observer<TriggerData>(taskCompletionSource.SetResult));
|
||||
var result = await taskCompletionSource.Task;
|
||||
var taskCompletionSource = new TaskCompletionSource<(TriggerType, object)>();
|
||||
var subscription = Subscribe(signal, new Observer<(TriggerType, object)>(taskCompletionSource.SetResult));
|
||||
(var type,var result) = await taskCompletionSource.Task;
|
||||
subscription.Dispose(); // 取消订阅
|
||||
return result;
|
||||
|
||||
return (type, result.ToConvert<TResult>());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 手动触发信号,并广播给所有订阅者
|
||||
/// </summary>
|
||||
/// <param name="signal">枚举信号标识符</param>
|
||||
/// <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))
|
||||
{
|
||||
TriggerData triggerData = new TriggerData()
|
||||
{
|
||||
Value = value,
|
||||
Type = TriggerType.External,
|
||||
};
|
||||
subject.OnNext(triggerData); // 广播给所有订阅者
|
||||
subject.OnNext((TriggerType.External, value)); // 广播给所有订阅者
|
||||
//subject.OnCompleted(); // 通知订阅结束
|
||||
return true;
|
||||
}
|
||||
@@ -28,42 +28,19 @@ namespace Net462DllTest.LogicControl
|
||||
}
|
||||
|
||||
|
||||
[NodeAction(NodeType.Flipflop, "等待车位调取命令",ReturnType=typeof(string))]
|
||||
public async Task<IFlipflopContext> GetPparkingSpace(ParkingCommand parkingCommand = ParkingCommand.GetPparkingSpace)
|
||||
[NodeAction(NodeType.Flipflop, "等待车位调取命令")]
|
||||
public async Task<IFlipflopContext<string>> GetPparkingSpace(ParkingCommand parkingCommand = ParkingCommand.GetPparkingSpace)
|
||||
{
|
||||
try
|
||||
{
|
||||
TriggerData triggerData = await PrakingDevice.CreateChannelWithTimeoutAsync(parkingCommand, TimeSpan.FromMinutes(120), 0);
|
||||
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);
|
||||
}
|
||||
var spaceNum = await PrakingDevice.CreateTaskAsync<string>(parkingCommand);
|
||||
await Console.Out.WriteLineAsync("收到命令:调取车位,车位号" + spaceNum);
|
||||
return new FlipflopContext<string>(FlipflopStateType.Succeed, spaceNum);
|
||||
}
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "调取指定车位")]
|
||||
public void Storage(string spaceNum = "101")
|
||||
{
|
||||
if (PrakingDevice.TriggerSignal(ParkingCommand.GetPparkingSpace, spaceNum))
|
||||
if (PrakingDevice.Trigger(ParkingCommand.GetPparkingSpace, spaceNum))
|
||||
{
|
||||
Console.WriteLine("发送命令成功:调取车位" + spaceNum);
|
||||
|
||||
|
||||
@@ -50,27 +50,20 @@ namespace Net462DllTest.LogicControl
|
||||
|
||||
#region 触发器节点
|
||||
|
||||
[NodeAction(NodeType.Flipflop, "等待变量更新", ReturnType = typeof(int))]
|
||||
public async Task<IFlipflopContext> WaitTask(PlcVarName varName = PlcVarName.ErrorCode)
|
||||
[NodeAction(NodeType.Flipflop, "等待变量更新")]
|
||||
public async Task<IFlipflopContext<dynamic>> WaitTask(PlcVarName varName = PlcVarName.ErrorCode)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
TriggerData triggerData = await MyPlc.CreateChannelWithTimeoutAsync(varName, TimeSpan.FromMinutes(120), 0);
|
||||
if (triggerData.Type == TriggerType.Overtime)
|
||||
{
|
||||
throw new FlipflopException("超时取消");
|
||||
}
|
||||
await Console.Out.WriteLineAsync($"PLC变量触发器[{varName}]传递数据:{triggerData.Value}");
|
||||
return new FlipflopContext(FlipflopStateType.Succeed, triggerData.Value);
|
||||
}
|
||||
catch (FlipflopException)
|
||||
{
|
||||
throw;
|
||||
var triggerData = await MyPlc.CreateTaskAsync<dynamic>(varName);
|
||||
|
||||
await Console.Out.WriteLineAsync($"PLC变量触发器[{varName}]传递数据:{triggerData}");
|
||||
return new FlipflopContext<dynamic>(FlipflopStateType.Succeed, triggerData);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new FlipflopContext(FlipflopStateType.Error);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -94,8 +87,8 @@ namespace Net462DllTest.LogicControl
|
||||
string ip = "192.168.10.100",
|
||||
int port = 102)
|
||||
{
|
||||
MyPlc.Model.Set(PlcVarName.DoorVar,(Int16)1);
|
||||
MyPlc.Model.Get(PlcVarName.DoorVar);
|
||||
//MyPlc.Model.Set(PlcVarName.DoorVar,(Int16)1);
|
||||
//MyPlc.Model.Get(PlcVarName.DoorVar);
|
||||
if (MyPlc.Client is null)
|
||||
{
|
||||
try
|
||||
@@ -144,7 +137,7 @@ namespace Net462DllTest.LogicControl
|
||||
[NodeAction(NodeType.Action, "批量读取")]
|
||||
public PlcVarModelDataProxy BatchReadVar()
|
||||
{
|
||||
MyPlc.BatchRefresh();
|
||||
//MyPlc.BatchRefresh();
|
||||
return plcVarModelDataProxy;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,25 +34,18 @@ namespace Net462DllTest.LogicControl
|
||||
|
||||
#region 触发器节点
|
||||
|
||||
[NodeAction(NodeType.Flipflop, "等待视图命令", ReturnType = typeof(int))]
|
||||
public async Task<IFlipflopContext> WaitTask(CommandSignal command)
|
||||
[NodeAction(NodeType.Flipflop, "等待视图命令")]
|
||||
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);
|
||||
if (triggerData.Type == TriggerType.Overtime)
|
||||
{
|
||||
return new FlipflopContext(FlipflopStateType.Cancel, triggerData.Value);
|
||||
}
|
||||
return new FlipflopContext(FlipflopStateType.Succeed, triggerData.Value);
|
||||
return new FlipflopContext<int>(FlipflopStateType.Cancel, result);
|
||||
}
|
||||
catch (FlipflopException)
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new FlipflopContext(FlipflopStateType.Error);
|
||||
|
||||
return new FlipflopContext<int>(FlipflopStateType.Succeed, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using System.Threading.Tasks;
|
||||
namespace Net462DllTest.Trigger
|
||||
{
|
||||
[AutoRegister]
|
||||
public class PrakingDevice : ChannelFlowTrigger<ParkingCommand>
|
||||
public class PrakingDevice : FlowTrigger<ParkingCommand>
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Net462DllTest.Trigger
|
||||
|
||||
|
||||
[AutoRegister]
|
||||
public class SiemensPlcDevice : ChannelFlowTrigger<PlcVarName>
|
||||
public class SiemensPlcDevice : FlowTrigger<PlcVarName>
|
||||
{
|
||||
public SiemensClient Client { get; set; }
|
||||
public SiemensVersion Version { get; set; }
|
||||
@@ -202,7 +202,7 @@ namespace Net462DllTest.Trigger
|
||||
if (isNotification)
|
||||
{
|
||||
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>
|
||||
[AutoRegister]
|
||||
public class ViewManagement : ChannelFlowTrigger<CommandSignal>
|
||||
public class ViewManagement : FlowTrigger<CommandSignal>
|
||||
{
|
||||
public ViewManagement(IFlowEnvironment environment)
|
||||
{
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace Net462DllTest.ViewModel
|
||||
});
|
||||
CommandGetParkingSpace = new RelayCommand((p) =>
|
||||
{
|
||||
viewManagement.TriggerSignal(SelectedSignal, SpcaeNumber);
|
||||
viewManagement.Trigger(SelectedSignal, SpcaeNumber);
|
||||
});
|
||||
CommandCloseForm = new RelayCommand((p) =>
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Net462DllTest.Web
|
||||
if (EnumHelper.TryConvertEnum<PlcVarName>(var,out var signal))
|
||||
{
|
||||
Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} ");
|
||||
plcDevice.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号
|
||||
plcDevice.Trigger(signal, value);// 通过 Web Api 模拟外部输入信号
|
||||
return new { state = "succeed" };
|
||||
}
|
||||
else
|
||||
@@ -67,7 +67,7 @@ namespace Net462DllTest.Web
|
||||
if (EnumHelper.TryConvertEnum<CommandSignal>(command, out var signal))
|
||||
{
|
||||
Console.WriteLine($"外部触发 {signal} 信号,信号内容 : {value} ");
|
||||
viewManagement.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号
|
||||
viewManagement.Trigger(signal, value);// 通过 Web Api 模拟外部输入信号
|
||||
return new { state = "succeed" };
|
||||
}
|
||||
else
|
||||
|
||||
@@ -16,8 +16,8 @@ using System.Threading.Tasks;
|
||||
namespace Net462DllTest.Web
|
||||
{
|
||||
|
||||
[DynamicFlow("[PlcSocketService]")]
|
||||
[AutoRegister]
|
||||
[DynamicFlow("[PlcSocketService]")]
|
||||
[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]
|
||||
public class PlcSocketService : ISocketHandleModule
|
||||
{
|
||||
@@ -42,8 +42,6 @@ namespace Net462DllTest.Web
|
||||
|
||||
context.Env.IOC.Register<IRouter, Router>();
|
||||
context.Env.IOC.Register<WebApiServer>();
|
||||
|
||||
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作
|
||||
@@ -90,17 +88,18 @@ namespace Net462DllTest.Web
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "等待")]
|
||||
public async Task Delay(int ms = 5000)
|
||||
[AutoSocketHandle]
|
||||
public async Task BatchReadVar(Func<string, Task> SendMsg, Func<object, Task> SendObj)
|
||||
{
|
||||
await Console.Out.WriteLineAsync("开始等待");
|
||||
await Task.Delay(ms);
|
||||
await Console.Out.WriteLineAsync("不再等待");
|
||||
|
||||
await SendMsg("开始刷新数据");
|
||||
//MyPlc.BatchRefresh();
|
||||
await Task.Delay(1000);
|
||||
await SendMsg("刷新完成");
|
||||
await SendObj(plcVarModelDataProxy);
|
||||
await SendMsg("发送完成");
|
||||
}
|
||||
|
||||
|
||||
[AutoSocketHandle]
|
||||
public object ReadVar(PlcVarName varName)
|
||||
{
|
||||
@@ -109,6 +108,14 @@ namespace Net462DllTest.Web
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[AutoSocketHandle(IsReturnValue = false)]
|
||||
public SiemensPlcDevice WriteVar(object value, PlcVarName varName)
|
||||
{
|
||||
MyPlc.Write(varName, value); // 新数据
|
||||
return MyPlc;
|
||||
}
|
||||
[AutoSocketHandle(IsReturnValue = false)]
|
||||
public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None,
|
||||
string ip = "192.168.10.100",
|
||||
@@ -142,24 +149,8 @@ namespace Net462DllTest.Web
|
||||
return MyPlc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[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()
|
||||
{
|
||||
Task.Run(async () => await MyPlc.OpenTimedRefreshAsync());
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Serein.NodeFlow.Base
|
||||
internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
@@ -86,7 +86,7 @@ namespace Serein.NodeFlow.Base
|
||||
this.MethodDetails.ExplicitDatas[i].DataValue = pd.Value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -104,9 +104,9 @@ namespace Serein.NodeFlow.Base
|
||||
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
||||
stack.Push(this);
|
||||
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)
|
||||
break;
|
||||
@@ -118,7 +118,7 @@ namespace Serein.NodeFlow.Base
|
||||
|
||||
// 筛选出上游分支
|
||||
var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].Where(
|
||||
node => node.DebugSetting.IsEnable
|
||||
node => node.DebugSetting.IsEnable
|
||||
).ToArray();
|
||||
// 执行上游分支
|
||||
foreach (var upstreamNode in upstreamNodes)
|
||||
@@ -141,10 +141,10 @@ namespace Serein.NodeFlow.Base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 上游分支执行完成,才执行当前节点
|
||||
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;
|
||||
@@ -204,14 +204,11 @@ namespace Serein.NodeFlow.Base
|
||||
md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
|
||||
object instance = md.ActingInstance;
|
||||
|
||||
|
||||
|
||||
object? result = null;
|
||||
|
||||
//Console.WriteLine($"(isTask, isTaskHaveResult):{(isTask, isTaskHaveResult)}");
|
||||
try
|
||||
{
|
||||
// Action/Func([方法作用的实例],[可能的参数值],[可能的返回值])
|
||||
|
||||
object?[]? args = GetParameters(context, this, md);
|
||||
result = await dd.Invoke(md.ActingInstance, args);
|
||||
NextOrientation = ConnectionType.IsSucceed;
|
||||
@@ -219,7 +216,7 @@ namespace Serein.NodeFlow.Base
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex.Message);
|
||||
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
||||
NextOrientation = ConnectionType.IsError;
|
||||
RuningException = ex;
|
||||
return null;
|
||||
@@ -268,8 +265,8 @@ namespace Serein.NodeFlow.Base
|
||||
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))
|
||||
{
|
||||
@@ -286,96 +283,57 @@ 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);
|
||||
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);
|
||||
if(value is not null)
|
||||
if (value is not null)
|
||||
{
|
||||
parameters[i] = value;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if (ed.DataType.IsValueType)
|
||||
{
|
||||
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) // 节点参数类型转换异常
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (ed.DataType.IsValueType)
|
||||
{
|
||||
parameters[i] = new object();
|
||||
Console.WriteLine(ex);
|
||||
var valueStr = inputParameter?.ToString();
|
||||
parameters[i] = valueStr.ToValueData(ed.DataType);
|
||||
}
|
||||
else
|
||||
{
|
||||
var valueStr = inputParameter?.ToString();
|
||||
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;
|
||||
}
|
||||
@@ -384,10 +342,10 @@ namespace Serein.NodeFlow.Base
|
||||
/// 更新节点数据,并检查监视表达式是否生效
|
||||
/// </summary>
|
||||
/// <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;
|
||||
if(newData is not null)
|
||||
if (newData is not null)
|
||||
{
|
||||
await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象
|
||||
await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点
|
||||
@@ -399,7 +357,7 @@ namespace Serein.NodeFlow.Base
|
||||
{
|
||||
MonitorObjectEventArgs.ObjSourceType sourceType;
|
||||
string? key;
|
||||
if(monitorType == 0)
|
||||
if (monitorType == 0)
|
||||
{
|
||||
key = data?.GetType()?.FullName;
|
||||
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
|
||||
@@ -444,7 +402,7 @@ namespace Serein.NodeFlow.Base
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 释放对象
|
||||
|
||||
@@ -43,6 +43,23 @@ namespace Serein.NodeFlow.Model
|
||||
}
|
||||
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);
|
||||
NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.Library.Ex;
|
||||
using Serein.Library.NodeFlow.Tool;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Base;
|
||||
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
||||
@@ -42,44 +43,15 @@ namespace Serein.NodeFlow.Model
|
||||
{
|
||||
var args = GetParameters(context, this, md);
|
||||
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();
|
||||
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;
|
||||
throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
//}
|
||||
|
||||
return flipflopContext.Value;
|
||||
|
||||
}
|
||||
catch (FlipflopException ex)
|
||||
{
|
||||
@@ -87,14 +59,14 @@ namespace Serein.NodeFlow.Model
|
||||
{
|
||||
throw;
|
||||
}
|
||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex.Message);
|
||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
||||
NextOrientation = ConnectionType.None;
|
||||
RuningException = ex;
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex.Message);
|
||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
||||
NextOrientation = ConnectionType.IsError;
|
||||
RuningException = ex;
|
||||
return null;
|
||||
@@ -104,7 +76,10 @@ namespace Serein.NodeFlow.Model
|
||||
// flipflopTask?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static object GetContextValueDynamic(dynamic context)
|
||||
{
|
||||
return context.Value; // dynamic 会在运行时处理类型
|
||||
}
|
||||
internal override Parameterdata[] GetParameterdatas()
|
||||
{
|
||||
if (base.MethodDetails.ExplicitDatas.Length > 0)
|
||||
|
||||
@@ -79,11 +79,31 @@ public static class NodeMethodDetailsHelper
|
||||
|
||||
if (attribute.MethodDynamicType == Library.Enums.NodeType.Flipflop)
|
||||
{
|
||||
returnType = attribute.ReturnType;
|
||||
if (!isTask || taskResult != typeof(IFlipflopContext))
|
||||
if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -54,46 +55,73 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
||||
|
||||
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);
|
||||
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
||||
{
|
||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
|
||||
}
|
||||
evaluatedValue = SerinArithmeticExpressionEvaluator<T>.Evaluate(ArithmeticExpression, evaluatedValue);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
|
||||
//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 Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Tool.SereinExpression.Resolver;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Globalization;
|
||||
@@ -31,7 +32,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
|
||||
public static SereinConditionResolver ConditionParse(object? data, string expression)
|
||||
{
|
||||
if (expression.StartsWith('.') || expression.StartsWith('<')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
||||
if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
||||
{
|
||||
return ParseObjectExpression(data, expression);
|
||||
}
|
||||
@@ -138,11 +139,22 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
}
|
||||
Type? tempType = typeStr switch
|
||||
{
|
||||
"int" => typeof(int),
|
||||
"double" => typeof(double),
|
||||
"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),
|
||||
_ => Type.GetType(typeStr)
|
||||
_ => Type.GetType(typeStr),
|
||||
};
|
||||
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
||||
if (string.IsNullOrWhiteSpace(memberPath))
|
||||
@@ -157,6 +169,10 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
}
|
||||
|
||||
#region 解析类型 int
|
||||
if (type.IsValueType)
|
||||
{
|
||||
//return GetValueResolver(type, valueStr, operatorStr, parts);
|
||||
}
|
||||
if (type == typeof(int))
|
||||
{
|
||||
var op = ParseValueTypeOperator<int>(operatorStr);
|
||||
@@ -278,52 +294,76 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
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))
|
||||
string operatorStr;
|
||||
string valueStr;
|
||||
Type type;
|
||||
// 尝试获取指定类型
|
||||
int typeStartIndex = expression.IndexOf('<');
|
||||
int typeEndIndex = expression.IndexOf('>');
|
||||
if (typeStartIndex + typeStartIndex == -2)
|
||||
{
|
||||
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])
|
||||
};
|
||||
|
||||
}
|
||||
// 如果不需要转为指定类型
|
||||
operatorStr = parts[0];
|
||||
valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
||||
type = data.GetType();
|
||||
}
|
||||
else if (type == typeof(double))
|
||||
{
|
||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||
return new ValueTypeConditionResolver<double>
|
||||
else
|
||||
{//string typeStr = parts[0];
|
||||
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1)
|
||||
.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),
|
||||
Value = value,
|
||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||
"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),
|
||||
_ => typeof(string),
|
||||
};
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (type == typeof(bool))
|
||||
{
|
||||
bool value = bool.Parse(valueStr);
|
||||
return new BoolConditionResolver
|
||||
@@ -332,6 +372,10 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
Value = value,
|
||||
};
|
||||
}
|
||||
else if (type.IsValueType)
|
||||
{
|
||||
return GetValueResolver(type, valueStr, operatorStr, parts);
|
||||
}
|
||||
else if (type == typeof(string))
|
||||
{
|
||||
return new StringConditionResolver
|
||||
@@ -344,6 +388,133 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
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>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Data;
|
||||
using Serein.Library.Utils;
|
||||
using System.Data;
|
||||
|
||||
namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
{
|
||||
@@ -9,19 +10,20 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
/// </summary>
|
||||
/// <param name="obj">操作的对象</param>
|
||||
/// <returns></returns>
|
||||
public class SerinArithmeticExpressionEvaluator
|
||||
public class SerinArithmeticExpressionEvaluator<T> where T : struct, IComparable<T>
|
||||
{
|
||||
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());
|
||||
try
|
||||
{
|
||||
// 使用 DataTable.Compute 方法计算表达式
|
||||
var result = table.Compute(expression, string.Empty);
|
||||
return Convert.ToDouble(result);
|
||||
return (T)result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -84,8 +86,9 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
/// <param name="methodCall">方法名称</param>
|
||||
/// <returns></returns>
|
||||
/// <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);
|
||||
if (methodParts.Length != 2)
|
||||
{
|
||||
@@ -344,15 +347,15 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
/// <param name="value"></param>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
private static double ComputedNumber(object value, string expression)
|
||||
private static decimal ComputedNumber(object value, string expression)
|
||||
{
|
||||
double numericValue = Convert.ToDouble(value);
|
||||
if (!string.IsNullOrEmpty(expression))
|
||||
{
|
||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
|
||||
}
|
||||
return ComputedNumber<decimal>(value, expression);
|
||||
}
|
||||
|
||||
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