diff --git a/Library.Core/NodeFlow/FlipflopContext.cs b/Library.Core/NodeFlow/FlipflopContext.cs index f59edf6..3ef05f4 100644 --- a/Library.Core/NodeFlow/FlipflopContext.cs +++ b/Library.Core/NodeFlow/FlipflopContext.cs @@ -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 /// /// 触发器上下文 /// - public class FlipflopContext : IFlipflopContext + public class FlipflopContext : IFlipflopContext { 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; } diff --git a/Library.Framework/NodeFlow/FlipflopContext.cs b/Library.Framework/NodeFlow/FlipflopContext.cs index 93092db..61136c3 100644 --- a/Library.Framework/NodeFlow/FlipflopContext.cs +++ b/Library.Framework/NodeFlow/FlipflopContext.cs @@ -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 /// /// 触发器上下文 /// - public class FlipflopContext : IFlipflopContext + public class FlipflopContext : IFlipflopContext { 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; } } diff --git a/Library/Api/IFlipflopContext.cs b/Library/Api/IFlipflopContext.cs index 88e8947..36134f3 100644 --- a/Library/Api/IFlipflopContext.cs +++ b/Library/Api/IFlipflopContext.cs @@ -3,9 +3,24 @@ using Serein.Library.NodeFlow.Tool; namespace Serein.Library.Api { - public interface IFlipflopContext + /// + /// 触发器必须使用该接口作为返回值,同时必须用Task泛型表示,否则将不会进行等待触发。 + /// + public interface IFlipflopContext { + /// + /// 触发器完成的状态(根据业务场景手动设置) + /// FlipflopStateType State { get; set; } - TriggerData TriggerData { get; set; } + /// + /// 触发传递的数据 + /// + //TriggerData TriggerData { get; set; } + + TriggerType Type { get; set; } + /// + /// 触发传递的数据 + /// + TResult Value { get; } } } diff --git a/Library/Enums/NodeType.cs b/Library/Enums/NodeType.cs index 388049b..a009330 100644 --- a/Library/Enums/NodeType.cs +++ b/Library/Enums/NodeType.cs @@ -22,7 +22,7 @@ namespace Serein.Library.Enums Exit, /// - /// 触发器(建议手动设置返回类型,方便视图直观) + /// 触发器(返回值必须为Task<IFlipflopContext<TResult>>) /// Flipflop, /// diff --git a/Library/Network/Http/Attribute.cs b/Library/Network/Http/Attribute.cs index 8468ba1..485b3f3 100644 --- a/Library/Network/Http/Attribute.cs +++ b/Library/Network/Http/Attribute.cs @@ -34,7 +34,7 @@ namespace Serein.Library.Web } /// - /// 自动注册控制器 + /// 标记该类为 Web Api 处理类 /// public class AutoHostingAttribute : Attribute { diff --git a/Library/Network/WebSocket/Attribute.cs b/Library/Network/WebSocket/Attribute.cs index fec73bb..730e38b 100644 --- a/Library/Network/WebSocket/Attribute.cs +++ b/Library/Network/WebSocket/Attribute.cs @@ -13,14 +13,14 @@ namespace Serein.Library.Network.WebSocketCommunication /// 使用方式: /// [AutoSocketModule(ThemeKey = "theme", DataKey = "data")] /// public class PlcSocketService : ISocketHandleModule - /// 类中方法示例:void AddUser(string name,age 35) + /// 类中方法示例:void AddUser(string name,int age) /// Json示例:{ "theme":"AddUser", //【ThemeKey】 /// "data": { // 【DataKey】 /// "name":"张三", /// "age":35, } } - /// WebSocket中收到以上该Json时,通过ThemeKey获取到"AddUser",然后找到AddUser()方法,并将"data"作为数据对象,取出相应数据作为入参(args:"张三",35)进行调用(如果有) + /// WebSocket中收到以上该Json时,通过ThemeKey获取到"AddUser",然后找到AddUser()方法 + /// 然后根据方法入参名称,从data对应的json数据中取出"name""age"对应的数据作为入参进行调用。AddUser("张三",35) /// - /// /// [AttributeUsage(AttributeTargets.Class)] public sealed class AutoSocketModuleAttribute : Attribute diff --git a/Library/Network/WebSocket/WebSocketServer.cs b/Library/Network/WebSocket/WebSocketServer.cs index 0471a7e..5ae87ed 100644 --- a/Library/Network/WebSocket/WebSocketServer.cs +++ b/Library/Network/WebSocket/WebSocketServer.cs @@ -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) { diff --git a/Library/NodeAttribute.cs b/Library/NodeAttribute.cs index 16b652e..7a720a4 100644 --- a/Library/NodeAttribute.cs +++ b/Library/NodeAttribute.cs @@ -104,10 +104,6 @@ namespace Serein.Library.Attributes /// public NodeType MethodDynamicType; /// - /// 显示标注方法返回类型,不影响运行逻辑(用于表示触发器触发后返回的数据) - /// - public Type ReturnType; - /// /// 暂无意义 /// public string LockName; diff --git a/Library/Utils/ConvertHelper.cs b/Library/Utils/ConvertHelper.cs new file mode 100644 index 0000000..c85cb10 --- /dev/null +++ b/Library/Utils/ConvertHelper.cs @@ -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(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(object value) where T : struct, IComparable + { + string valueStr = value.ToString(); + return valueStr.ToValueData() ; + } + + public static object ValueParse(Type type, object value) + { + string valueStr = value.ToString(); + return valueStr.ToValueData(type); + + } + + + public static T ToValueData(this string valueStr) where T : struct, IComparable + { + 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; + } + } +} diff --git a/Library/Utils/ExpressionHelper.cs b/Library/Utils/ExpressionHelper.cs index 88541d3..02c2615 100644 --- a/Library/Utils/ExpressionHelper.cs +++ b/Library/Utils/ExpressionHelper.cs @@ -358,8 +358,8 @@ namespace Serein.Library.Utils ); // 创建 lambda 表达式 - var lambda = Expression.Lambda>>( - Expression.Convert(methodCall, typeof(Task)), + var lambda = Expression.Lambda>>>( + Expression.Convert(methodCall, typeof(Task>)), instanceParam, argsParam ); diff --git a/Library/Utils/ChannelFlowTrigger.cs b/Library/Utils/FlowTrigger.cs similarity index 63% rename from Library/Utils/ChannelFlowTrigger.cs rename to Library/Utils/FlowTrigger.cs index 9363c0b..384975c 100644 --- a/Library/Utils/ChannelFlowTrigger.cs +++ b/Library/Utils/FlowTrigger.cs @@ -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 { public TriggerType Type { get; set; } - public object Value { get; set; } + public T Value { get; set; } } /// - /// 信号触发器类,带有消息广播功能 + /// 信号触发器类,带有消息广播功能。 + /// 使用枚举作为标记,创建 /// - public class ChannelFlowTrigger where TSignal : struct, Enum + public class FlowTrigger where TSignal : struct, Enum { // 使用并发字典管理每个信号对应的广播列表 - private readonly ConcurrentDictionary> _subscribers = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary> _subscribers = new ConcurrentDictionary>(); /// /// 获取或创建指定信号的 Subject(消息广播者) /// /// 枚举信号标识符 /// 对应的 Subject - private Subject GetOrCreateSubject(TSignal signal) + private Subject<(TriggerType, object)> GetOrCreateSubject(TSignal signal) { - return _subscribers.GetOrAdd(signal, _ => new Subject()); + return _subscribers.GetOrAdd(signal, _ => new Subject<(TriggerType, object)>()); } /// @@ -52,9 +55,10 @@ namespace Serein.Library.NodeFlow.Tool /// 枚举信号标识符 /// 订阅者 /// 取消订阅的句柄 - public IDisposable Subscribe(TSignal signal, IObserver 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 /// /// 枚举信号标识符 /// 超时时间 - /// 等待任务 - public async Task CreateChannelWithTimeoutAsync(TSignal signal, TimeSpan outTime, TResult outValue) + /// 等待任务,返回值为:状态(超时触发,手动触发),数据(超时触发时会使用设置好的数据) + public async Task<(TriggerType, TResult)> CreateTaskWithTimeoutAsync(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(signal);// 返回一个可以超时触发的等待任务 + return result; } + + + /// + /// 创建等待任务,触发时通知所有订阅者 + /// + /// 枚举信号标识符 + /// 超时时间 + /// 等待任务 + public async Task CreateTaskAsync(TSignal signal) + { + var subject = GetOrCreateSubject(signal); + (_,var result) = await WaitSignalAsync(signal); + + return result;// 返回一个等待的任务 + } + + /// /// 等待指定信号的触发 /// /// 枚举信号标识符 /// 等待任务 - public async Task WaitForSignalAsync(TSignal signal) + public async Task<(TriggerType, TResult)> WaitSignalAsync(TSignal signal) { - var taskCompletionSource = new TaskCompletionSource(); - var subscription = Subscribe(signal, new Observer(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()); } + + /// /// 手动触发信号,并广播给所有订阅者 /// /// 枚举信号标识符 /// 是否成功触发 - public bool TriggerSignal(TSignal signal, TResult value) + public bool Trigger(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; } diff --git a/Net462DllTest/LogicControl/ParkingLogicControl.cs b/Net462DllTest/LogicControl/ParkingLogicControl.cs index 8976855..c90fd79 100644 --- a/Net462DllTest/LogicControl/ParkingLogicControl.cs +++ b/Net462DllTest/LogicControl/ParkingLogicControl.cs @@ -28,42 +28,19 @@ namespace Net462DllTest.LogicControl } - [NodeAction(NodeType.Flipflop, "等待车位调取命令",ReturnType=typeof(string))] - public async Task GetPparkingSpace(ParkingCommand parkingCommand = ParkingCommand.GetPparkingSpace) + [NodeAction(NodeType.Flipflop, "等待车位调取命令")] + public async Task> 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(parkingCommand); + await Console.Out.WriteLineAsync("收到命令:调取车位,车位号" + spaceNum); + return new FlipflopContext(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); diff --git a/Net462DllTest/LogicControl/PlcLogicControl.cs b/Net462DllTest/LogicControl/PlcLogicControl.cs index db48bfd..d985a72 100644 --- a/Net462DllTest/LogicControl/PlcLogicControl.cs +++ b/Net462DllTest/LogicControl/PlcLogicControl.cs @@ -50,27 +50,20 @@ namespace Net462DllTest.LogicControl #region 触发器节点 - [NodeAction(NodeType.Flipflop, "等待变量更新", ReturnType = typeof(int))] - public async Task WaitTask(PlcVarName varName = PlcVarName.ErrorCode) + [NodeAction(NodeType.Flipflop, "等待变量更新")] + public async Task> 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(varName); + + await Console.Out.WriteLineAsync($"PLC变量触发器[{varName}]传递数据:{triggerData}"); + return new FlipflopContext(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; } diff --git a/Net462DllTest/LogicControl/ViewLogicControl.cs b/Net462DllTest/LogicControl/ViewLogicControl.cs index 164a60f..50d1844 100644 --- a/Net462DllTest/LogicControl/ViewLogicControl.cs +++ b/Net462DllTest/LogicControl/ViewLogicControl.cs @@ -34,25 +34,18 @@ namespace Net462DllTest.LogicControl #region 触发器节点 - [NodeAction(NodeType.Flipflop, "等待视图命令", ReturnType = typeof(int))] - public async Task WaitTask(CommandSignal command) + [NodeAction(NodeType.Flipflop, "等待视图命令")] + public async Task> 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(FlipflopStateType.Cancel, result); } - catch (FlipflopException) + else { - throw; - } - catch (Exception) - { - return new FlipflopContext(FlipflopStateType.Error); + + return new FlipflopContext(FlipflopStateType.Succeed, result); } } diff --git a/Net462DllTest/Trigger/PrakingDevice.cs b/Net462DllTest/Trigger/PrakingDevice.cs index 10420a6..6295a3b 100644 --- a/Net462DllTest/Trigger/PrakingDevice.cs +++ b/Net462DllTest/Trigger/PrakingDevice.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Net462DllTest.Trigger { [AutoRegister] - public class PrakingDevice : ChannelFlowTrigger + public class PrakingDevice : FlowTrigger { } diff --git a/Net462DllTest/Trigger/SiemensPlcDevice.cs b/Net462DllTest/Trigger/SiemensPlcDevice.cs index 07e327e..b73220d 100644 --- a/Net462DllTest/Trigger/SiemensPlcDevice.cs +++ b/Net462DllTest/Trigger/SiemensPlcDevice.cs @@ -24,7 +24,7 @@ namespace Net462DllTest.Trigger [AutoRegister] - public class SiemensPlcDevice : ChannelFlowTrigger + public class SiemensPlcDevice : FlowTrigger { 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); } diff --git a/Net462DllTest/Trigger/ViewManagement.cs b/Net462DllTest/Trigger/ViewManagement.cs index 4fe192b..6c24936 100644 --- a/Net462DllTest/Trigger/ViewManagement.cs +++ b/Net462DllTest/Trigger/ViewManagement.cs @@ -15,7 +15,7 @@ namespace Net462DllTest.Trigger /// 视图管理 /// [AutoRegister] - public class ViewManagement : ChannelFlowTrigger + public class ViewManagement : FlowTrigger { public ViewManagement(IFlowEnvironment environment) { diff --git a/Net462DllTest/ViewModel/FromWorkBenchViewModel.cs b/Net462DllTest/ViewModel/FromWorkBenchViewModel.cs index 4f60a8c..881c3c6 100644 --- a/Net462DllTest/ViewModel/FromWorkBenchViewModel.cs +++ b/Net462DllTest/ViewModel/FromWorkBenchViewModel.cs @@ -131,7 +131,7 @@ namespace Net462DllTest.ViewModel }); CommandGetParkingSpace = new RelayCommand((p) => { - viewManagement.TriggerSignal(SelectedSignal, SpcaeNumber); + viewManagement.Trigger(SelectedSignal, SpcaeNumber); }); CommandCloseForm = new RelayCommand((p) => { diff --git a/Net462DllTest/Web/FlowController.cs b/Net462DllTest/Web/FlowController.cs index aa1dd90..6035c99 100644 --- a/Net462DllTest/Web/FlowController.cs +++ b/Net462DllTest/Web/FlowController.cs @@ -42,7 +42,7 @@ namespace Net462DllTest.Web if (EnumHelper.TryConvertEnum(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(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 diff --git a/Net462DllTest/Web/PlcSocketService.cs b/Net462DllTest/Web/PlcSocketService.cs index 0c5f976..5821118 100644 --- a/Net462DllTest/Web/PlcSocketService.cs +++ b/Net462DllTest/Web/PlcSocketService.cs @@ -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(); context.Env.IOC.Register(); - - } [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 SendMsg, Func 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 send) - { - send("开始读取"); - MyPlc.BatchRefresh(); - send("读取完成"); - return plcVarModelDataProxy; - } + public void OpenTimedRefresh() { Task.Run(async () => await MyPlc.OpenTimedRefreshAsync()); diff --git a/NodeFlow/Base/NodeModelBaseFunc.cs b/NodeFlow/Base/NodeModelBaseFunc.cs index ff7e3ae..a49481c 100644 --- a/NodeFlow/Base/NodeModelBaseFunc.cs +++ b/NodeFlow/Base/NodeModelBaseFunc.cs @@ -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 stack = new Stack(); stack.Push(this); var flowCts = context.Env.IOC.Get(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().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().ToList(), + Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter, + _ => inputParameter, + }; + } + + + } return parameters; } @@ -384,10 +342,10 @@ namespace Serein.NodeFlow.Base /// 更新节点数据,并检查监视表达式是否生效 /// /// - 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 } } - + /// /// 释放对象 diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs index ad258eb..4e61fa1 100644 --- a/NodeFlow/Model/SingleConditionNode.cs +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -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; } diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs index 88c8bea..856e08c 100644 --- a/NodeFlow/Model/SingleFlipflopNode.cs +++ b/NodeFlow/Model/SingleFlipflopNode.cs @@ -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 flipflopTask; - //var delType = dd.EmitMethodType; - //var del = dd.EmitDelegate; - //if (delType == EmitHelper.EmitMethodType.HasResultTask && del is Func> 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) diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs index 83d3a61..dfaa766 100644 --- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs +++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs @@ -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]"); + // 获取 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>。"); + return (null, null); + } } + else + { + Console.WriteLine($"[{dllTypeMethodName}]跳过创建,因为触发器方法的返回值并非Task<>,将无法等待。"); + return (null, null); + } + + //if (!isTask || taskResult != typeof(IFlipflopContext)) + //{ + // + //} } else if(isTask) diff --git a/NodeFlow/Tool/SereinExpression/Resolver/ValueTypeConditionResolver.cs b/NodeFlow/Tool/SereinExpression/Resolver/ValueTypeConditionResolver.cs index f5c9c97..f1cf7e1 100644 --- a/NodeFlow/Tool/SereinExpression/Resolver/ValueTypeConditionResolver.cs +++ b/NodeFlow/Tool/SereinExpression/Resolver/ValueTypeConditionResolver.cs @@ -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(); + if (!string.IsNullOrEmpty(ArithmeticExpression)) { - double numericValue = Convert.ToDouble(typedObj); - if (!string.IsNullOrEmpty(ArithmeticExpression)) - { - numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue); - } + evaluatedValue = SerinArithmeticExpressionEvaluator.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; } } diff --git a/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs b/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs index 7b6ccab..178902f 100644 --- a/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs +++ b/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs @@ -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(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(operatorStr); - if (op == ValueTypeConditionResolver.Operator.InRange || op == ValueTypeConditionResolver.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 - { - Op = op, - RangeStart = rangeStart, - RangeEnd = rangeEnd, - ArithmeticExpression = GetArithmeticExpression(parts[0]), - }; - } - else - { - int value = int.Parse(valueStr, CultureInfo.InvariantCulture); - return new ValueTypeConditionResolver - { - 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 + 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(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 + { + SereinConditionResolver resolver = valueType switch + { + Type t when t == typeof(float) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(decimal) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(double) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(sbyte) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(byte) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(short) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(ushort) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(int) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(uint) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(long) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(ulong) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(nint) => GetValueResolver(valueStr, operatorStr, parts), + Type t when t == typeof(nuint) => GetValueResolver(valueStr, operatorStr, parts), + _ => throw new ArgumentException("非预期值类型") + }; + return resolver; + } + + + private static ValueTypeConditionResolver GetValueResolver(string valueStr, string operatorStr, string[] parts) + where T :struct, IComparable + { + var op = ParseValueTypeOperator(operatorStr); + if (op == ValueTypeConditionResolver.Operator.InRange || op == ValueTypeConditionResolver.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 + { + Op = op, + RangeStart = leftNum.ToValueData(), + RangeEnd = rightNum.ToValueData(), + ArithmeticExpression = GetArithmeticExpression(parts[0]), + }; + } + else + { + return new ValueTypeConditionResolver + { + Op = op, + Value = valueStr.ToValueData(), + ArithmeticExpression = GetArithmeticExpression(parts[0]) + }; + + } + } + //public static T ValueParse(object value) where T : struct, IComparable + //{ + // 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; + //} + /// diff --git a/NodeFlow/Tool/SereinExpression/SerinExpressionEvaluator.cs b/NodeFlow/Tool/SereinExpression/SerinExpressionEvaluator.cs index 03d0d64..b813e41 100644 --- a/NodeFlow/Tool/SereinExpression/SerinExpressionEvaluator.cs +++ b/NodeFlow/Tool/SereinExpression/SerinExpressionEvaluator.cs @@ -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 /// /// 操作的对象 /// - public class SerinArithmeticExpressionEvaluator + public class SerinArithmeticExpressionEvaluator where T : struct, IComparable { 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 /// 方法名称 /// /// - 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 /// /// /// - 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(value, expression); + } - return numericValue; + private static T ComputedNumber(object value, string expression) where T : struct, IComparable + { + T result = value.ToConvert(); + return SerinArithmeticExpressionEvaluator.Evaluate(expression, result); } } }