重写了触发器底层逻辑

This commit is contained in:
fengjiayi
2024-10-11 16:46:16 +08:00
parent 2d0f354895
commit aa85c907a3
27 changed files with 770 additions and 392 deletions

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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; }
}
}

View File

@@ -22,7 +22,7 @@ namespace Serein.Library.Enums
Exit,
/// <summary>
/// 触发器(建议手动设置返回类型,方便视图直观
/// 触发器(返回值必须为Task&lt;IFlipflopContext&lt;TResult&gt;&gt;
/// </summary>
Flipflop,
/// <summary>

View File

@@ -34,7 +34,7 @@ namespace Serein.Library.Web
}
/// <summary>
/// 自动注册控制器
/// 标记该类为 Web Api 处理类
/// </summary>
public class AutoHostingAttribute : Attribute
{

View File

@@ -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

View File

@@ -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)
{

View File

@@ -104,10 +104,6 @@ namespace Serein.Library.Attributes
/// </summary>
public NodeType MethodDynamicType;
/// <summary>
/// 显示标注方法返回类型,不影响运行逻辑(用于表示触发器触发后返回的数据)
/// </summary>
public Type ReturnType;
/// <summary>
/// 暂无意义
/// </summary>
public string LockName;

View 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;
}
}
}

View File

@@ -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
);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -10,7 +10,7 @@ using System.Threading.Tasks;
namespace Net462DllTest.Trigger
{
[AutoRegister]
public class PrakingDevice : ChannelFlowTrigger<ParkingCommand>
public class PrakingDevice : FlowTrigger<ParkingCommand>
{
}

View File

@@ -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);
}

View File

@@ -15,7 +15,7 @@ namespace Net462DllTest.Trigger
/// 视图管理
/// </summary>
[AutoRegister]
public class ViewManagement : ChannelFlowTrigger<CommandSignal>
public class ViewManagement : FlowTrigger<CommandSignal>
{
public ViewManagement(IFlowEnvironment environment)
{

View File

@@ -131,7 +131,7 @@ namespace Net462DllTest.ViewModel
});
CommandGetParkingSpace = new RelayCommand((p) =>
{
viewManagement.TriggerSignal(SelectedSignal, SpcaeNumber);
viewManagement.Trigger(SelectedSignal, SpcaeNumber);
});
CommandCloseForm = new RelayCommand((p) =>
{

View File

@@ -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

View File

@@ -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());

View File

@@ -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>
/// 释放对象

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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);
}
}
}