重写了触发器底层逻辑

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<> 的泛型参数类型 // 获取 Task<> 的泛型参数类型
var innerType = type.GetGenericArguments()[0]; var innerType = type.GetGenericArguments()[0];
if (innerType.IsGenericType && type.GetGenericTypeDefinition() == typeof(IFlipflopContext<>))
{
var flipflop = type.GetGenericArguments()[0];
return true;
}
// 判断 innerType 是否继承 IFlipflopContext // 判断 innerType 是否继承 IFlipflopContext
//if (typeof(IFlipflopContext).IsAssignableFrom(innerType)) //if (typeof(IFlipflopContext).IsAssignableFrom(innerType))
@@ -50,11 +55,11 @@ namespace Serein.Library.Core.NodeFlow
//} //}
// 检查泛型参数是否为 Flipflop<> // 检查泛型参数是否为 Flipflop<>
if (innerType == typeof(IFlipflopContext)) //if (innerType == typeof(IFlipflopContext))
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>)) //if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
{ //{
return true; //return true;
} //}
} }
return false; return false;
@@ -64,20 +69,21 @@ namespace Serein.Library.Core.NodeFlow
/// <summary> /// <summary>
/// 触发器上下文 /// 触发器上下文
/// </summary> /// </summary>
public class FlipflopContext : IFlipflopContext public class FlipflopContext<TResult> : IFlipflopContext<TResult>
{ {
public FlipflopStateType State { get; set; } public FlipflopStateType State { get; set; }
public TriggerData TriggerData { get; set; } public TriggerType Type { get; set; }
public TResult Value { get; set; }
public FlipflopContext(FlipflopStateType ffState) public FlipflopContext(FlipflopStateType ffState)
{ {
State = ffState; State = ffState;
} }
public FlipflopContext(FlipflopStateType ffState, TriggerData data) public FlipflopContext(FlipflopStateType ffState, TResult value)
{ {
State = ffState; State = ffState;
TriggerData = data; Value = value;
} }

View File

@@ -40,13 +40,18 @@ namespace Serein.Library.Framework.NodeFlow
{ {
// 获取 Task<> 的泛型参数类型 // 获取 Task<> 的泛型参数类型
var innerType = type.GetGenericArguments()[0]; var innerType = type.GetGenericArguments()[0];
if (innerType.IsGenericType && type.GetGenericTypeDefinition() == typeof(IFlipflopContext<>))
// 检查泛型参数是否为 Flipflop<>
if (innerType == typeof(FlipflopContext))
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
{ {
var flipflop = type.GetGenericArguments()[0];
return true; return true;
} }
//// 检查泛型参数是否为 Flipflop<>
//if (innerType == typeof(FlipflopContext))
////if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
//{
// return true;
//}
} }
return false; return false;
@@ -56,24 +61,22 @@ namespace Serein.Library.Framework.NodeFlow
/// <summary> /// <summary>
/// 触发器上下文 /// 触发器上下文
/// </summary> /// </summary>
public class FlipflopContext : IFlipflopContext public class FlipflopContext<TResult> : IFlipflopContext<TResult>
{ {
public FlipflopStateType State { get; set; } public FlipflopStateType State { get; set; }
//public TResult? Data { get; set; }
public TriggerData TriggerData { get; set; } public TriggerType Type { get; set; }
public TResult Value { get; set; }
public FlipflopContext(FlipflopStateType ffState) public FlipflopContext(FlipflopStateType ffState)
{ {
State = ffState; State = ffState;
} }
public FlipflopContext(FlipflopStateType ffState, object data)
public FlipflopContext(FlipflopStateType ffState, TResult value)
{ {
State = ffState; State = ffState;
TriggerData = new TriggerData Value = value;
{
Type = TriggerType.External,
Value = data
};
} }
} }

View File

@@ -3,9 +3,24 @@ using Serein.Library.NodeFlow.Tool;
namespace Serein.Library.Api namespace Serein.Library.Api
{ {
public interface IFlipflopContext /// <summary>
/// 触发器必须使用该接口作为返回值同时必须用Task泛型表示否则将不会进行等待触发。
/// </summary>
public interface IFlipflopContext<out TResult>
{ {
/// <summary>
/// 触发器完成的状态(根据业务场景手动设置)
/// </summary>
FlipflopStateType State { get; set; } FlipflopStateType State { get; set; }
TriggerData TriggerData { get; set; } /// <summary>
/// 触发传递的数据
/// </summary>
//TriggerData TriggerData { get; set; }
TriggerType Type { get; set; }
/// <summary>
/// 触发传递的数据
/// </summary>
TResult Value { get; }
} }
} }

View File

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

View File

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

View File

@@ -13,14 +13,14 @@ namespace Serein.Library.Network.WebSocketCommunication
/// <para>使用方式:</para> /// <para>使用方式:</para>
/// <para>[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]</para> /// <para>[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]</para>
/// <para>public class PlcSocketService : ISocketHandleModule</para> /// <para>public class PlcSocketService : ISocketHandleModule</para>
/// <para>类中方法示例void AddUser(string name,age 35)</para> /// <para>类中方法示例void AddUser(string name,int age)</para>
/// <para>Json示例{ "theme":"AddUser", //【ThemeKey】 </para> /// <para>Json示例{ "theme":"AddUser", //【ThemeKey】 </para>
/// <para> "data": { // 【DataKey】 </para> /// <para> "data": { // 【DataKey】 </para>
/// <para> "name":"张三", </para> /// <para> "name":"张三", </para>
/// <para> "age":35, } } </para> /// <para> "age":35, } } </para>
/// <para>WebSocket中收到以上该Json时通过ThemeKey获取到"AddUser"然后找到AddUser()方法,并将"data"作为数据对象,取出相应数据作为入参(args:"张三",35)进行调用(如果有)</para> /// <para>WebSocket中收到以上该Json时通过ThemeKey获取到"AddUser"然后找到AddUser()方法</para>
/// <para>然后根据方法入参名称从data对应的json数据中取出"name""age"对应的数据作为入参进行调用。AddUser("张三",35)</para>
/// <para></para> /// <para></para>
///
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Class)]
public sealed class AutoSocketModuleAttribute : Attribute public sealed class AutoSocketModuleAttribute : Attribute

View File

@@ -24,7 +24,16 @@ namespace Serein.Library.Network.WebSocketCommunication
{ {
listener = new HttpListener(); listener = new HttpListener();
listener.Prefixes.Add(url); listener.Prefixes.Add(url);
listener.Start(); try
{
listener.Start();
}
catch (Exception ex)
{
await Console.Out.WriteLineAsync(ex.Message);
return;
}
while (true) while (true)
{ {

View File

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

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 表达式 // 创建 lambda 表达式
var lambda = Expression.Lambda<Func<object, object[], Task<IFlipflopContext>>>( var lambda = Expression.Lambda<Func<object, object[], Task<IFlipflopContext<object>>>>(
Expression.Convert(methodCall, typeof(Task<IFlipflopContext>)), Expression.Convert(methodCall, typeof(Task<IFlipflopContext<object>>)),
instanceParam, instanceParam,
argsParam argsParam
); );

View File

@@ -1,5 +1,7 @@
using System; using Serein.Library.Utils;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Reactive.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using System.Threading; using System.Threading;
using System.Threading.Channels; using System.Threading.Channels;
@@ -22,28 +24,29 @@ namespace Serein.Library.NodeFlow.Tool
Overtime Overtime
} }
public class TriggerData public class TriggerResult<T>
{ {
public TriggerType Type { get; set; } public TriggerType Type { get; set; }
public object Value { get; set; } public T Value { get; set; }
} }
/// <summary> /// <summary>
/// 信号触发器类,带有消息广播功能 /// 信号触发器类,带有消息广播功能
/// 使用枚举作为标记,创建
/// </summary> /// </summary>
public class ChannelFlowTrigger<TSignal> where TSignal : struct, Enum public class FlowTrigger<TSignal> where TSignal : struct, Enum
{ {
// 使用并发字典管理每个信号对应的广播列表 // 使用并发字典管理每个信号对应的广播列表
private readonly ConcurrentDictionary<TSignal, Subject<TriggerData>> _subscribers = new ConcurrentDictionary<TSignal, Subject<TriggerData>>(); private readonly ConcurrentDictionary<TSignal, Subject<(TriggerType,object)>> _subscribers = new ConcurrentDictionary<TSignal, Subject<(TriggerType, object)>>();
/// <summary> /// <summary>
/// 获取或创建指定信号的 Subject消息广播者 /// 获取或创建指定信号的 Subject消息广播者
/// </summary> /// </summary>
/// <param name="signal">枚举信号标识符</param> /// <param name="signal">枚举信号标识符</param>
/// <returns>对应的 Subject</returns> /// <returns>对应的 Subject</returns>
private Subject<TriggerData> GetOrCreateSubject(TSignal signal) private Subject<(TriggerType, object)> GetOrCreateSubject(TSignal signal)
{ {
return _subscribers.GetOrAdd(signal, _ => new Subject<TriggerData>()); return _subscribers.GetOrAdd(signal, _ => new Subject<(TriggerType, object)>());
} }
/// <summary> /// <summary>
@@ -52,9 +55,10 @@ namespace Serein.Library.NodeFlow.Tool
/// <param name="signal">枚举信号标识符</param> /// <param name="signal">枚举信号标识符</param>
/// <param name="observer">订阅者</param> /// <param name="observer">订阅者</param>
/// <returns>取消订阅的句柄</returns> /// <returns>取消订阅的句柄</returns>
public IDisposable Subscribe(TSignal signal, IObserver<TriggerData> observer) public IDisposable Subscribe(TSignal signal, IObserver<(TriggerType, object)> observer)
{ {
var subject = GetOrCreateSubject(signal); var subject = GetOrCreateSubject(signal);
// (IObserver<(TriggerType, object)>)
return subject.Subscribe(observer); // 返回取消订阅的句柄 return subject.Subscribe(observer); // 返回取消订阅的句柄
} }
@@ -63,8 +67,8 @@ namespace Serein.Library.NodeFlow.Tool
/// </summary> /// </summary>
/// <param name="signal">枚举信号标识符</param> /// <param name="signal">枚举信号标识符</param>
/// <param name="outTime">超时时间</param> /// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns> /// <returns>等待任务,返回值为:状态(超时触发,手动触发),数据(超时触发时会使用设置好的数据)</returns>
public async Task<TriggerData> CreateChannelWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime, TResult outValue) public async Task<(TriggerType, TResult)> CreateTaskWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime, TResult outValue)
{ {
var subject = GetOrCreateSubject(signal); var subject = GetOrCreateSubject(signal);
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
@@ -77,12 +81,7 @@ namespace Serein.Library.NodeFlow.Tool
await Task.Delay(outTime, cts.Token); await Task.Delay(outTime, cts.Token);
if (!cts.IsCancellationRequested) // 如果还没有被取消 if (!cts.IsCancellationRequested) // 如果还没有被取消
{ {
TriggerData triggerData = new TriggerData() subject.OnNext((TriggerType.Overtime, outValue)); // 广播给所有订阅者
{
Value = outValue,
Type = TriggerType.Overtime,
};
subject.OnNext(triggerData); // 广播给所有订阅者
subject.OnCompleted(); // 通知订阅结束 subject.OnCompleted(); // 通知订阅结束
} }
} }
@@ -96,39 +95,54 @@ namespace Serein.Library.NodeFlow.Tool
} }
}, cts.Token); }, cts.Token);
// 返回一个等待任务 var result = await WaitSignalAsync<TResult>(signal);// 返回一个可以超时触发的等待任务
return await WaitForSignalAsync(signal); return result;
} }
/// <summary>
/// 创建等待任务,触发时通知所有订阅者
/// </summary>
/// <param name="signal">枚举信号标识符</param>
/// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns>
public async Task<TResult> CreateTaskAsync<TResult>(TSignal signal)
{
var subject = GetOrCreateSubject(signal);
(_,var result) = await WaitSignalAsync<TResult>(signal);
return result;// 返回一个等待的任务
}
/// <summary> /// <summary>
/// 等待指定信号的触发 /// 等待指定信号的触发
/// </summary> /// </summary>
/// <param name="signal">枚举信号标识符</param> /// <param name="signal">枚举信号标识符</param>
/// <returns>等待任务</returns> /// <returns>等待任务</returns>
public async Task<TriggerData> WaitForSignalAsync(TSignal signal) public async Task<(TriggerType, TResult)> WaitSignalAsync<TResult>(TSignal signal)
{ {
var taskCompletionSource = new TaskCompletionSource<TriggerData>(); var taskCompletionSource = new TaskCompletionSource<(TriggerType, object)>();
var subscription = Subscribe(signal, new Observer<TriggerData>(taskCompletionSource.SetResult)); var subscription = Subscribe(signal, new Observer<(TriggerType, object)>(taskCompletionSource.SetResult));
var result = await taskCompletionSource.Task; (var type,var result) = await taskCompletionSource.Task;
subscription.Dispose(); // 取消订阅 subscription.Dispose(); // 取消订阅
return result;
return (type, result.ToConvert<TResult>());
} }
/// <summary> /// <summary>
/// 手动触发信号,并广播给所有订阅者 /// 手动触发信号,并广播给所有订阅者
/// </summary> /// </summary>
/// <param name="signal">枚举信号标识符</param> /// <param name="signal">枚举信号标识符</param>
/// <returns>是否成功触发</returns> /// <returns>是否成功触发</returns>
public bool TriggerSignal<TResult>(TSignal signal, TResult value) public bool Trigger<TResult>(TSignal signal, TResult value)
{ {
if (_subscribers.TryGetValue(signal, out var subject)) if (_subscribers.TryGetValue(signal, out var subject))
{ {
TriggerData triggerData = new TriggerData() subject.OnNext((TriggerType.External, value)); // 广播给所有订阅者
{
Value = value,
Type = TriggerType.External,
};
subject.OnNext(triggerData); // 广播给所有订阅者
//subject.OnCompleted(); // 通知订阅结束 //subject.OnCompleted(); // 通知订阅结束
return true; return true;
} }

View File

@@ -28,42 +28,19 @@ namespace Net462DllTest.LogicControl
} }
[NodeAction(NodeType.Flipflop, "等待车位调取命令",ReturnType=typeof(string))] [NodeAction(NodeType.Flipflop, "等待车位调取命令")]
public async Task<IFlipflopContext> GetPparkingSpace(ParkingCommand parkingCommand = ParkingCommand.GetPparkingSpace) public async Task<IFlipflopContext<string>> GetPparkingSpace(ParkingCommand parkingCommand = ParkingCommand.GetPparkingSpace)
{ {
try var spaceNum = await PrakingDevice.CreateTaskAsync<string>(parkingCommand);
{ await Console.Out.WriteLineAsync("收到命令:调取车位,车位号" + spaceNum);
TriggerData triggerData = await PrakingDevice.CreateChannelWithTimeoutAsync(parkingCommand, TimeSpan.FromMinutes(120), 0); return new FlipflopContext<string>(FlipflopStateType.Succeed, spaceNum);
if (triggerData.Type == TriggerType.Overtime)
{
throw new FlipflopException("超时取消");
}
if(triggerData.Value is string spaceNum)
{
await Console.Out.WriteLineAsync("收到命令:调取车位,车位号"+ spaceNum);
return new FlipflopContext(FlipflopStateType.Succeed, spaceNum);
}
else
{
throw new FlipflopException("并非车位号");
}
}
catch (FlipflopException)
{
throw;
}
catch (Exception)
{
return new FlipflopContext(FlipflopStateType.Error);
}
} }
[NodeAction(NodeType.Action, "调取指定车位")] [NodeAction(NodeType.Action, "调取指定车位")]
public void Storage(string spaceNum = "101") public void Storage(string spaceNum = "101")
{ {
if (PrakingDevice.TriggerSignal(ParkingCommand.GetPparkingSpace, spaceNum)) if (PrakingDevice.Trigger(ParkingCommand.GetPparkingSpace, spaceNum))
{ {
Console.WriteLine("发送命令成功:调取车位" + spaceNum); Console.WriteLine("发送命令成功:调取车位" + spaceNum);

View File

@@ -50,27 +50,20 @@ namespace Net462DllTest.LogicControl
#region #region
[NodeAction(NodeType.Flipflop, "等待变量更新", ReturnType = typeof(int))] [NodeAction(NodeType.Flipflop, "等待变量更新")]
public async Task<IFlipflopContext> WaitTask(PlcVarName varName = PlcVarName.ErrorCode) public async Task<IFlipflopContext<dynamic>> WaitTask(PlcVarName varName = PlcVarName.ErrorCode)
{ {
try try
{ {
TriggerData triggerData = await MyPlc.CreateChannelWithTimeoutAsync(varName, TimeSpan.FromMinutes(120), 0); var triggerData = await MyPlc.CreateTaskAsync<dynamic>(varName);
if (triggerData.Type == TriggerType.Overtime)
{ await Console.Out.WriteLineAsync($"PLC变量触发器[{varName}]传递数据:{triggerData}");
throw new FlipflopException("超时取消"); return new FlipflopContext<dynamic>(FlipflopStateType.Succeed, triggerData);
}
await Console.Out.WriteLineAsync($"PLC变量触发器[{varName}]传递数据:{triggerData.Value}");
return new FlipflopContext(FlipflopStateType.Succeed, triggerData.Value);
}
catch (FlipflopException)
{
throw;
} }
catch (Exception) catch (Exception)
{ {
return new FlipflopContext(FlipflopStateType.Error); throw;
} }
} }
@@ -94,8 +87,8 @@ namespace Net462DllTest.LogicControl
string ip = "192.168.10.100", string ip = "192.168.10.100",
int port = 102) int port = 102)
{ {
MyPlc.Model.Set(PlcVarName.DoorVar,(Int16)1); //MyPlc.Model.Set(PlcVarName.DoorVar,(Int16)1);
MyPlc.Model.Get(PlcVarName.DoorVar); //MyPlc.Model.Get(PlcVarName.DoorVar);
if (MyPlc.Client is null) if (MyPlc.Client is null)
{ {
try try
@@ -144,7 +137,7 @@ namespace Net462DllTest.LogicControl
[NodeAction(NodeType.Action, "批量读取")] [NodeAction(NodeType.Action, "批量读取")]
public PlcVarModelDataProxy BatchReadVar() public PlcVarModelDataProxy BatchReadVar()
{ {
MyPlc.BatchRefresh(); //MyPlc.BatchRefresh();
return plcVarModelDataProxy; return plcVarModelDataProxy;
} }

View File

@@ -34,25 +34,18 @@ namespace Net462DllTest.LogicControl
#region #region
[NodeAction(NodeType.Flipflop, "等待视图命令", ReturnType = typeof(int))] [NodeAction(NodeType.Flipflop, "等待视图命令")]
public async Task<IFlipflopContext> WaitTask(CommandSignal command) public async Task<IFlipflopContext<int>> WaitTask(CommandSignal command)
{ {
try (var type, var result) = await ViewManagement.CreateTaskWithTimeoutAsync(command, TimeSpan.FromHours(10), 0);
if (type == TriggerType.Overtime)
{ {
TriggerData triggerData = await ViewManagement.CreateChannelWithTimeoutAsync(command, TimeSpan.FromHours(10), 0); return new FlipflopContext<int>(FlipflopStateType.Cancel, result);
if (triggerData.Type == TriggerType.Overtime)
{
return new FlipflopContext(FlipflopStateType.Cancel, triggerData.Value);
}
return new FlipflopContext(FlipflopStateType.Succeed, triggerData.Value);
} }
catch (FlipflopException) else
{ {
throw;
} return new FlipflopContext<int>(FlipflopStateType.Succeed, result);
catch (Exception)
{
return new FlipflopContext(FlipflopStateType.Error);
} }
} }

View File

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

View File

@@ -24,7 +24,7 @@ namespace Net462DllTest.Trigger
[AutoRegister] [AutoRegister]
public class SiemensPlcDevice : ChannelFlowTrigger<PlcVarName> public class SiemensPlcDevice : FlowTrigger<PlcVarName>
{ {
public SiemensClient Client { get; set; } public SiemensClient Client { get; set; }
public SiemensVersion Version { get; set; } public SiemensVersion Version { get; set; }
@@ -202,7 +202,7 @@ namespace Net462DllTest.Trigger
if (isNotification) if (isNotification)
{ {
Console.WriteLine($"VarName: {signal}\t\tOld Data: {oldData}\tNew Data: {newData}"); Console.WriteLine($"VarName: {signal}\t\tOld Data: {oldData}\tNew Data: {newData}");
TriggerSignal(signal, newData); Trigger(signal, newData);
} }

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ namespace Net462DllTest.Web
if (EnumHelper.TryConvertEnum<PlcVarName>(var,out var signal)) if (EnumHelper.TryConvertEnum<PlcVarName>(var,out var signal))
{ {
Console.WriteLine($"外部触发 {signal} 信号,信号内容 {value} "); Console.WriteLine($"外部触发 {signal} 信号,信号内容 {value} ");
plcDevice.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号 plcDevice.Trigger(signal, value);// 通过 Web Api 模拟外部输入信号
return new { state = "succeed" }; return new { state = "succeed" };
} }
else else
@@ -67,7 +67,7 @@ namespace Net462DllTest.Web
if (EnumHelper.TryConvertEnum<CommandSignal>(command, out var signal)) if (EnumHelper.TryConvertEnum<CommandSignal>(command, out var signal))
{ {
Console.WriteLine($"外部触发 {signal} 信号,信号内容 {value} "); Console.WriteLine($"外部触发 {signal} 信号,信号内容 {value} ");
viewManagement.TriggerSignal(signal, value);// 通过 Web Api 模拟外部输入信号 viewManagement.Trigger(signal, value);// 通过 Web Api 模拟外部输入信号
return new { state = "succeed" }; return new { state = "succeed" };
} }
else else

View File

@@ -16,8 +16,8 @@ using System.Threading.Tasks;
namespace Net462DllTest.Web namespace Net462DllTest.Web
{ {
[DynamicFlow("[PlcSocketService]")]
[AutoRegister] [AutoRegister]
[DynamicFlow("[PlcSocketService]")]
[AutoSocketModule(ThemeKey = "theme", DataKey = "data")] [AutoSocketModule(ThemeKey = "theme", DataKey = "data")]
public class PlcSocketService : ISocketHandleModule public class PlcSocketService : ISocketHandleModule
{ {
@@ -42,8 +42,6 @@ namespace Net462DllTest.Web
context.Env.IOC.Register<IRouter, Router>(); context.Env.IOC.Register<IRouter, Router>();
context.Env.IOC.Register<WebApiServer>(); context.Env.IOC.Register<WebApiServer>();
} }
[NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作 [NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作
@@ -90,17 +88,18 @@ namespace Net462DllTest.Web
} }
#endregion #endregion
[AutoSocketHandle]
public async Task BatchReadVar(Func<string, Task> SendMsg, Func<object, Task> SendObj)
[NodeAction(NodeType.Action, "等待")]
public async Task Delay(int ms = 5000)
{ {
await Console.Out.WriteLineAsync("开始等待"); await SendMsg("开始刷新数据");
await Task.Delay(ms); //MyPlc.BatchRefresh();
await Console.Out.WriteLineAsync("不再等待"); await Task.Delay(1000);
await SendMsg("刷新完成");
await SendObj(plcVarModelDataProxy);
await SendMsg("发送完成");
} }
[AutoSocketHandle] [AutoSocketHandle]
public object ReadVar(PlcVarName varName) public object ReadVar(PlcVarName varName)
{ {
@@ -109,6 +108,14 @@ namespace Net462DllTest.Web
return result; return result;
} }
[AutoSocketHandle(IsReturnValue = false)]
public SiemensPlcDevice WriteVar(object value, PlcVarName varName)
{
MyPlc.Write(varName, value); // 新数据
return MyPlc;
}
[AutoSocketHandle(IsReturnValue = false)] [AutoSocketHandle(IsReturnValue = false)]
public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None, public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None,
string ip = "192.168.10.100", string ip = "192.168.10.100",
@@ -142,24 +149,8 @@ namespace Net462DllTest.Web
return MyPlc; 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() public void OpenTimedRefresh()
{ {
Task.Run(async () => await MyPlc.OpenTimedRefreshAsync()); Task.Run(async () => await MyPlc.OpenTimedRefreshAsync());

View File

@@ -77,7 +77,7 @@ namespace Serein.NodeFlow.Base
internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo) internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
{ {
this.Guid = nodeInfo.Guid; this.Guid = nodeInfo.Guid;
if(this.MethodDetails is not null) if (this.MethodDetails is not null)
{ {
for (int i = 0; i < nodeInfo.ParameterData.Length; i++) for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
{ {
@@ -86,7 +86,7 @@ namespace Serein.NodeFlow.Base
this.MethodDetails.ExplicitDatas[i].DataValue = pd.Value; this.MethodDetails.ExplicitDatas[i].DataValue = pd.Value;
} }
} }
return this; return this;
} }
@@ -104,9 +104,9 @@ namespace Serein.NodeFlow.Base
Stack<NodeModelBase> stack = new Stack<NodeModelBase>(); Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
stack.Push(this); stack.Push(this);
var flowCts = context.Env.IOC.Get<CancellationTokenSource>(FlowStarter.FlipFlopCtsName); var flowCts = context.Env.IOC.Get<CancellationTokenSource>(FlowStarter.FlipFlopCtsName);
while (stack.Count > 0 ) // 循环中直到栈为空才会退出循环 while (stack.Count > 0) // 循环中直到栈为空才会退出循环
{ {
if(flowCts is not null) if (flowCts is not null)
{ {
if (flowCts.IsCancellationRequested) if (flowCts.IsCancellationRequested)
break; break;
@@ -118,7 +118,7 @@ namespace Serein.NodeFlow.Base
// 筛选出上游分支 // 筛选出上游分支
var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].Where( var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].Where(
node => node.DebugSetting.IsEnable node => node.DebugSetting.IsEnable
).ToArray(); ).ToArray();
// 执行上游分支 // 执行上游分支
foreach (var upstreamNode in upstreamNodes) foreach (var upstreamNode in upstreamNodes)
@@ -141,10 +141,10 @@ namespace Serein.NodeFlow.Base
} }
} }
} }
// 上游分支执行完成,才执行当前节点 // 上游分支执行完成,才执行当前节点
object? newFlowData = await currentNode.ExecutingAsync(context); object? newFlowData = await currentNode.ExecutingAsync(context);
if (flowCts is null || flowCts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None) if (flowCts is null || flowCts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None)
{ {
// 不再执行 // 不再执行
break; break;
@@ -204,14 +204,11 @@ namespace Serein.NodeFlow.Base
md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType); md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
object instance = md.ActingInstance; object instance = md.ActingInstance;
object? result = null; object? result = null;
//Console.WriteLine($"(isTask, isTaskHaveResult):{(isTask, isTaskHaveResult)}");
try try
{ {
// Action/Func([方法作用的实例],[可能的参数值],[可能的返回值])
object?[]? args = GetParameters(context, this, md); object?[]? args = GetParameters(context, this, md);
result = await dd.Invoke(md.ActingInstance, args); result = await dd.Invoke(md.ActingInstance, args);
NextOrientation = ConnectionType.IsSucceed; NextOrientation = ConnectionType.IsSucceed;
@@ -219,7 +216,7 @@ namespace Serein.NodeFlow.Base
} }
catch (Exception ex) catch (Exception ex)
{ {
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex.Message); await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
NextOrientation = ConnectionType.IsError; NextOrientation = ConnectionType.IsError;
RuningException = ex; RuningException = ex;
return null; return null;
@@ -268,8 +265,8 @@ namespace Serein.NodeFlow.Base
inputParameter = flowData; // 使用上一节点的对象 inputParameter = flowData; // 使用上一节点的对象
} }
// 存在转换器 // 入参存在取值转换器
if (ed.Convertor is not null) if (ed.ExplicitType.IsEnum && ed.Convertor is not null)
{ {
if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum)) if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum))
{ {
@@ -286,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); var type = EnumHelper.GetBoundValue(ed.ExplicitType, resultEnum, attr => attr.Value);
if(type is Type enumBindType && enumBindType is not null) if (type is Type enumBindType && enumBindType is not null)
{ {
var value = context.Env.IOC.Instantiate(enumBindType); var value = context.Env.IOC.Instantiate(enumBindType);
if(value is not null) if (value is not null)
{ {
parameters[i] = value; parameters[i] = value;
continue; continue;
} }
} }
} }
}
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(); var valueStr = inputParameter?.ToString();
Console.WriteLine(ex); 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; return parameters;
} }
@@ -384,10 +342,10 @@ namespace Serein.NodeFlow.Base
/// 更新节点数据,并检查监视表达式是否生效 /// 更新节点数据,并检查监视表达式是否生效
/// </summary> /// </summary>
/// <param name="newData"></param> /// <param name="newData"></param>
public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context,NodeModelBase nodeModel, object? newData = null) public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object? newData = null)
{ {
string guid = nodeModel.Guid; string guid = nodeModel.Guid;
if(newData is not null) if (newData is not null)
{ {
await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象 await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象
await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点 await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点
@@ -399,7 +357,7 @@ namespace Serein.NodeFlow.Base
{ {
MonitorObjectEventArgs.ObjSourceType sourceType; MonitorObjectEventArgs.ObjSourceType sourceType;
string? key; string? key;
if(monitorType == 0) if (monitorType == 0)
{ {
key = data?.GetType()?.FullName; key = data?.GetType()?.FullName;
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
@@ -444,7 +402,7 @@ namespace Serein.NodeFlow.Base
} }
} }
/// <summary> /// <summary>
/// 释放对象 /// 释放对象

View File

@@ -43,6 +43,23 @@ namespace Serein.NodeFlow.Model
} }
try try
{ {
var getObjExp = CustomData?.ToString();
if (IsCustomData && !string.IsNullOrEmpty(getObjExp) && getObjExp.Length >= 4)
{
var ExpOpOption = getObjExp[..4];
if(ExpOpOption.ToLower() == "@get")
{
result = PreviousNode?.GetFlowData();
if (result is not null)
{
result = SerinExpressionEvaluator.Evaluate(getObjExp, result, out _);
}
}
}
var isPass = SereinConditionParser.To(result, Expression); var isPass = SereinConditionParser.To(result, Expression);
NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail; NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail;
} }

View File

@@ -2,6 +2,7 @@
using Serein.Library.Entity; using Serein.Library.Entity;
using Serein.Library.Enums; using Serein.Library.Enums;
using Serein.Library.Ex; using Serein.Library.Ex;
using Serein.Library.NodeFlow.Tool;
using Serein.Library.Utils; using Serein.Library.Utils;
using Serein.NodeFlow.Base; using Serein.NodeFlow.Base;
using static Serein.Library.Utils.ChannelFlowInterrupt; using static Serein.Library.Utils.ChannelFlowInterrupt;
@@ -42,44 +43,15 @@ namespace Serein.NodeFlow.Model
{ {
var args = GetParameters(context, this, md); var args = GetParameters(context, this, md);
var result = await dd.Invoke(md.ActingInstance, args); var result = await dd.Invoke(md.ActingInstance, args);
if (result is IFlipflopContext flipflopContext) dynamic flipflopContext = result;
FlipflopStateType flipflopStateType = flipflopContext.State;
NextOrientation = flipflopStateType.ToContentType();
if (flipflopContext.Type == TriggerType.Overtime)
{ {
NextOrientation = flipflopContext.State.ToContentType(); throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
if (flipflopContext.TriggerData is null || flipflopContext.TriggerData.Type == Library.NodeFlow.Tool.TriggerType.Overtime)
{
throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
}
return flipflopContext.TriggerData.Value;
} }
else return flipflopContext.Value;
{
throw new FlipflopException("触发器节点返回了非预期的类型", true, FlipflopException.CancelClass.Flow);
}
// Task<IFlipflopContext> flipflopTask;
//var delType = dd.EmitMethodType;
//var del = dd.EmitDelegate;
//if (delType == EmitHelper.EmitMethodType.HasResultTask && del is Func<object, object?[]?, Task<object>> hasResultTask)
//{
// var flipflopTaskObj = await hasResultTask(instance, args);
// if(flipflopTaskObj is IFlipflopContext flipflopContext)
// {
// NextOrientation = flipflopContext.State.ToContentType();
// if (flipflopContext.TriggerData is null || flipflopContext.TriggerData.Type == Library.NodeFlow.Tool.TriggerType.Overtime)
// {
// throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
// }
// return flipflopContext.TriggerData.Value;
// }
// else
// {
// throw new FlipflopException("触发器节点返回了非预期的类型", true, FlipflopException.CancelClass.Flow);
// }
//}
//else
//{
// throw new FlipflopException("触发器节点构造了非预期的委托", true, FlipflopException.CancelClass.Flow);
//}
} }
catch (FlipflopException ex) catch (FlipflopException ex)
{ {
@@ -87,14 +59,14 @@ namespace Serein.NodeFlow.Model
{ {
throw; throw;
} }
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex.Message); await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
NextOrientation = ConnectionType.None; NextOrientation = ConnectionType.None;
RuningException = ex; RuningException = ex;
return null; return null;
} }
catch (Exception ex) catch (Exception ex)
{ {
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex.Message); await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
NextOrientation = ConnectionType.IsError; NextOrientation = ConnectionType.IsError;
RuningException = ex; RuningException = ex;
return null; return null;
@@ -104,7 +76,10 @@ namespace Serein.NodeFlow.Model
// flipflopTask?.Dispose(); // flipflopTask?.Dispose();
} }
} }
public static object GetContextValueDynamic(dynamic context)
{
return context.Value; // dynamic 会在运行时处理类型
}
internal override Parameterdata[] GetParameterdatas() internal override Parameterdata[] GetParameterdatas()
{ {
if (base.MethodDetails.ExplicitDatas.Length > 0) if (base.MethodDetails.ExplicitDatas.Length > 0)

View File

@@ -79,11 +79,31 @@ public static class NodeMethodDetailsHelper
if (attribute.MethodDynamicType == Library.Enums.NodeType.Flipflop) if (attribute.MethodDynamicType == Library.Enums.NodeType.Flipflop)
{ {
returnType = attribute.ReturnType; if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
if (!isTask || taskResult != typeof(IFlipflopContext))
{ {
Console.WriteLine($"触发器节点的返回类型非预期类型,可能会导致流程异常。[{dllTypeMethodName}]当前返回类型为[{method.ReturnType}],而预期的返回类型应为[Task<IFlipflopContext>]"); // 获取 Task<> 的泛型参数类型
var innerType = method.ReturnType.GetGenericArguments()[0];
if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(IFlipflopContext<>))
{
var flipflopType = innerType.GetGenericArguments()[0];
returnType = flipflopType;
}
else
{
Console.WriteLine($"[{dllTypeMethodName}]跳过创建返回类型非预期的Task<IFlipflopContext<TResult>>。");
return (null, null);
}
} }
else
{
Console.WriteLine($"[{dllTypeMethodName}]跳过创建因为触发器方法的返回值并非Task<>,将无法等待。");
return (null, null);
}
//if (!isTask || taskResult != typeof(IFlipflopContext<object>))
//{
//
//}
} }
else if(isTask) else if(isTask)

View File

@@ -1,4 +1,5 @@
using System; using Serein.Library.Utils;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -54,46 +55,73 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
public override bool Evaluate(object obj) public override bool Evaluate(object obj)
{ {
if (obj is T typedObj)
var evaluatedValue = obj.ToConvert<T>();
if (!string.IsNullOrEmpty(ArithmeticExpression))
{ {
double numericValue = Convert.ToDouble(typedObj); evaluatedValue = SerinArithmeticExpressionEvaluator<T>.Evaluate(ArithmeticExpression, evaluatedValue);
if (!string.IsNullOrEmpty(ArithmeticExpression)) }
{
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
}
T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T)); switch (Op)
{
/*return Op switch case Operator.GreaterThan:
{ return evaluatedValue.CompareTo(Value) > 0;
Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0, case Operator.LessThan:
Operator.LessThan => evaluatedValue.CompareTo(Value) < 0, return evaluatedValue.CompareTo(Value) < 0;
Operator.Equal => evaluatedValue.CompareTo(Value) == 0, case Operator.Equal:
Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0, return evaluatedValue.CompareTo(Value) == 0;
Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0, case Operator.GreaterThanOrEqual:
Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0, return evaluatedValue.CompareTo(Value) >= 0;
Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0, case Operator.LessThanOrEqual:
_ => throw new NotSupportedException("不支持的条件类型") return evaluatedValue.CompareTo(Value) <= 0;
};*/ case Operator.InRange:
switch (Op) return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
{ case Operator.OutOfRange:
case Operator.GreaterThan: return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
return evaluatedValue.CompareTo(Value) > 0;
case Operator.LessThan:
return evaluatedValue.CompareTo(Value) < 0;
case Operator.Equal:
return evaluatedValue.CompareTo(Value) == 0;
case Operator.GreaterThanOrEqual:
return evaluatedValue.CompareTo(Value) >= 0;
case Operator.LessThanOrEqual:
return evaluatedValue.CompareTo(Value) <= 0;
case Operator.InRange:
return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
case Operator.OutOfRange:
return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
}
} }
return false; return false;
//if (obj is T typedObj)
//{
// numericValue = Convert.ToDouble(typedObj);
// numericValue = Convert.ToDouble(obj);
// if (!string.IsNullOrEmpty(ArithmeticExpression))
// {
// numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
// }
// T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T));
// /*return Op switch
// {
// Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0,
// Operator.LessThan => evaluatedValue.CompareTo(Value) < 0,
// Operator.Equal => evaluatedValue.CompareTo(Value) == 0,
// Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0,
// Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0,
// Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0,
// Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0,
// _ => throw new NotSupportedException("不支持的条件类型")
// };*/
// switch (Op)
// {
// case Operator.GreaterThan:
// return evaluatedValue.CompareTo(Value) > 0;
// case Operator.LessThan:
// return evaluatedValue.CompareTo(Value) < 0;
// case Operator.Equal:
// return evaluatedValue.CompareTo(Value) == 0;
// case Operator.GreaterThanOrEqual:
// return evaluatedValue.CompareTo(Value) >= 0;
// case Operator.LessThanOrEqual:
// return evaluatedValue.CompareTo(Value) <= 0;
// case Operator.InRange:
// return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
// case Operator.OutOfRange:
// return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
// }
//}
//return false;
} }
} }

View File

@@ -1,4 +1,5 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Serein.Library.Utils;
using Serein.NodeFlow.Tool.SereinExpression.Resolver; using Serein.NodeFlow.Tool.SereinExpression.Resolver;
using System.ComponentModel.Design; using System.ComponentModel.Design;
using System.Globalization; using System.Globalization;
@@ -31,7 +32,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
public static SereinConditionResolver ConditionParse(object? data, string expression) public static SereinConditionResolver ConditionParse(object? data, string expression)
{ {
if (expression.StartsWith('.') || expression.StartsWith('<')) // 表达式前缀属于从上一个节点数据对象获取成员值 if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
{ {
return ParseObjectExpression(data, expression); return ParseObjectExpression(data, expression);
} }
@@ -138,11 +139,22 @@ namespace Serein.NodeFlow.Tool.SereinExpression
} }
Type? tempType = typeStr switch Type? tempType = typeStr switch
{ {
"int" => typeof(int),
"double" => typeof(double),
"bool" => typeof(bool), "bool" => typeof(bool),
"float" => typeof(float),
"decimal" => typeof(decimal),
"double" => typeof(double),
"sbyte" => typeof(sbyte),
"byte" => typeof(byte),
"short" => typeof(short),
"ushort" => typeof(ushort),
"int" => typeof(int),
"uint" => typeof(uint),
"long" => typeof(long),
"ulong" => typeof(ulong),
"nint" => typeof(nint),
"nuint" => typeof(nuint),
"string" => typeof(string), "string" => typeof(string),
_ => Type.GetType(typeStr) _ => Type.GetType(typeStr),
}; };
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明"); type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
if (string.IsNullOrWhiteSpace(memberPath)) if (string.IsNullOrWhiteSpace(memberPath))
@@ -157,6 +169,10 @@ namespace Serein.NodeFlow.Tool.SereinExpression
} }
#region int #region int
if (type.IsValueType)
{
//return GetValueResolver(type, valueStr, operatorStr, parts);
}
if (type == typeof(int)) if (type == typeof(int))
{ {
var op = ParseValueTypeOperator<int>(operatorStr); var op = ParseValueTypeOperator<int>(operatorStr);
@@ -278,52 +294,76 @@ namespace Serein.NodeFlow.Tool.SereinExpression
if (parts.Length < 2) if (parts.Length < 2)
throw new ArgumentException("无效的表达式格式。"); throw new ArgumentException("无效的表达式格式。");
//string typeStr = parts[0]; string operatorStr;
string operatorStr = parts[0]; string valueStr;
string valueStr = string.Join(' ', parts, 1, parts.Length - 1); Type type;
// 尝试获取指定类型
Type type = data.GetType();//Type.GetType(typeStr); int typeStartIndex = expression.IndexOf('<');
if (type == typeof(int)) int typeEndIndex = expression.IndexOf('>');
if (typeStartIndex + typeStartIndex == -2)
{ {
var op = ParseValueTypeOperator<int>(operatorStr); // 如果不需要转为指定类型
if (op == ValueTypeConditionResolver<int>.Operator.InRange || op == ValueTypeConditionResolver<int>.Operator.OutOfRange) operatorStr = parts[0];
{ valueStr = string.Join(' ', parts, 1, parts.Length - 1);
var temp = valueStr.Split('-'); type = data.GetType();
if (temp.Length < 2)
throw new ArgumentException($"范围无效:{valueStr}。");
int rangeStart = int.Parse(temp[0], CultureInfo.InvariantCulture);
int rangeEnd = int.Parse(temp[1], CultureInfo.InvariantCulture);
return new ValueTypeConditionResolver<int>
{
Op = op,
RangeStart = rangeStart,
RangeEnd = rangeEnd,
ArithmeticExpression = GetArithmeticExpression(parts[0]),
};
}
else
{
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
return new ValueTypeConditionResolver<int>
{
Op = op,
Value = value,
ArithmeticExpression = GetArithmeticExpression(parts[0])
};
}
} }
else if (type == typeof(double)) else
{ {//string typeStr = parts[0];
double value = double.Parse(valueStr, CultureInfo.InvariantCulture); string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1)
return new ValueTypeConditionResolver<double> .Trim().ToLower(); // 手动置顶的类型
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
operatorStr = parts[0].ToLower(); // 操作类型
valueStr = string.Join(' ', parts.Skip(1)); // 表达式值
type = typeStr switch
{ {
Op = ParseValueTypeOperator<double>(operatorStr), "bool" => typeof(bool),
Value = value, "float" => typeof(float),
ArithmeticExpression = GetArithmeticExpression(parts[0]) "decimal" => typeof(decimal),
"double" => typeof(double),
"sbyte" => typeof(sbyte),
"byte" => typeof(byte),
"short" => typeof(short),
"ushort" => typeof(ushort),
"int" => typeof(int),
"uint" => typeof(uint),
"long" => typeof(long),
"ulong" => typeof(ulong),
"nint" => typeof(nint),
"nuint" => typeof(nuint),
_ => typeof(string),
}; };
} }
else if (type == typeof(bool))
if (type == typeof(bool))
{ {
bool value = bool.Parse(valueStr); bool value = bool.Parse(valueStr);
return new BoolConditionResolver return new BoolConditionResolver
@@ -332,6 +372,10 @@ namespace Serein.NodeFlow.Tool.SereinExpression
Value = value, Value = value,
}; };
} }
else if (type.IsValueType)
{
return GetValueResolver(type, valueStr, operatorStr, parts);
}
else if (type == typeof(string)) else if (type == typeof(string))
{ {
return new StringConditionResolver return new StringConditionResolver
@@ -344,6 +388,133 @@ namespace Serein.NodeFlow.Tool.SereinExpression
throw new NotSupportedException($"Type {type} is not supported."); throw new NotSupportedException($"Type {type} is not supported.");
} }
public static SereinConditionResolver GetValueResolver(Type valueType, string valueStr, string operatorStr, string[] parts)// where T : struct, IComparable<T>
{
SereinConditionResolver resolver = valueType switch
{
Type t when t == typeof(float) => GetValueResolver<float>(valueStr, operatorStr, parts),
Type t when t == typeof(decimal) => GetValueResolver<decimal>(valueStr, operatorStr, parts),
Type t when t == typeof(double) => GetValueResolver<double>(valueStr, operatorStr, parts),
Type t when t == typeof(sbyte) => GetValueResolver<sbyte>(valueStr, operatorStr, parts),
Type t when t == typeof(byte) => GetValueResolver<byte>(valueStr, operatorStr, parts),
Type t when t == typeof(short) => GetValueResolver<short>(valueStr, operatorStr, parts),
Type t when t == typeof(ushort) => GetValueResolver<ushort>(valueStr, operatorStr, parts),
Type t when t == typeof(int) => GetValueResolver<int>(valueStr, operatorStr, parts),
Type t when t == typeof(uint) => GetValueResolver<uint>(valueStr, operatorStr, parts),
Type t when t == typeof(long) => GetValueResolver<long>(valueStr, operatorStr, parts),
Type t when t == typeof(ulong) => GetValueResolver<ulong>(valueStr, operatorStr, parts),
Type t when t == typeof(nint) => GetValueResolver<nint>(valueStr, operatorStr, parts),
Type t when t == typeof(nuint) => GetValueResolver<nuint>(valueStr, operatorStr, parts),
_ => throw new ArgumentException("非预期值类型")
};
return resolver;
}
private static ValueTypeConditionResolver<T> GetValueResolver<T>(string valueStr, string operatorStr, string[] parts)
where T :struct, IComparable<T>
{
var op = ParseValueTypeOperator<T>(operatorStr);
if (op == ValueTypeConditionResolver<T>.Operator.InRange || op == ValueTypeConditionResolver<T>.Operator.OutOfRange)
{
var temp = valueStr.Split('-');
var leftNum = string.Empty;
var rightNum = string.Empty;
if (temp.Length < 2 || temp.Length > 4)
{
throw new ArgumentException($"范围无效:{valueStr}。");
}
else if (temp.Length == 2)
{
leftNum = temp[0];
rightNum = temp[1];
}
else if (temp.Length == 3)
{
if (string.IsNullOrEmpty(temp[0])
&& !string.IsNullOrEmpty(temp[1])
&& !string.IsNullOrEmpty(temp[2]))
{
leftNum = "-" + temp[1];
rightNum = temp[2];
}
else
{
throw new ArgumentException($"范围无效:{valueStr}。");
}
}
else if (temp.Length == 4)
{
if (string.IsNullOrEmpty(temp[0])
&& !string.IsNullOrEmpty(temp[1])
&& string.IsNullOrEmpty(temp[2])
&& !string.IsNullOrEmpty(temp[3]))
{
leftNum = "-" + temp[1];
rightNum = temp[3];
}
else
{
throw new ArgumentException($"范围无效:{valueStr}。");
}
}
return new ValueTypeConditionResolver<T>
{
Op = op,
RangeStart = leftNum.ToValueData<T>(),
RangeEnd = rightNum.ToValueData<T>(),
ArithmeticExpression = GetArithmeticExpression(parts[0]),
};
}
else
{
return new ValueTypeConditionResolver<T>
{
Op = op,
Value = valueStr.ToValueData<T>(),
ArithmeticExpression = GetArithmeticExpression(parts[0])
};
}
}
//public static T ValueParse<T>(object value) where T : struct, IComparable<T>
//{
// return (T)ValueParse(typeof(T), value);
//}
//public static object ValueParse(Type type, object value)
//{
// string? valueStr = value.ToString();
// if (string.IsNullOrEmpty(valueStr))
// {
// throw new ArgumentException("value is null");
// }
// object result = type switch
// {
// Type t when t.IsEnum => Enum.Parse(type, valueStr),
// Type t when t == typeof(bool) => bool.Parse(valueStr),
// Type t when t == typeof(float) => float.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(decimal) => decimal.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(double) => double.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(sbyte) => sbyte.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(byte) => byte.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(short) => short.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(ushort) => ushort.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(int) => int.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(uint) => uint.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(long) => long.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(ulong) => ulong.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(nint) => nint.Parse(valueStr, CultureInfo.InvariantCulture),
// Type t when t == typeof(nuint) => nuint.Parse(valueStr, CultureInfo.InvariantCulture),
// _ => throw new ArgumentException("非预期值类型")
// };
// return result;
//}
/// <summary> /// <summary>

View File

@@ -1,4 +1,5 @@
using System.Data; using Serein.Library.Utils;
using System.Data;
namespace Serein.NodeFlow.Tool.SereinExpression namespace Serein.NodeFlow.Tool.SereinExpression
{ {
@@ -9,19 +10,20 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// </summary> /// </summary>
/// <param name="obj">操作的对象</param> /// <param name="obj">操作的对象</param>
/// <returns></returns> /// <returns></returns>
public class SerinArithmeticExpressionEvaluator public class SerinArithmeticExpressionEvaluator<T> where T : struct, IComparable<T>
{ {
private static readonly DataTable table = new DataTable(); private static readonly DataTable table = new DataTable();
public static double Evaluate(string expression, double inputValue) public static T Evaluate(string expression, T inputValue)
{ {
// 替换占位符@为输入值 // 替换占位符@为输入值
expression = expression.Replace("@", inputValue.ToString()); expression = expression.Replace("@", inputValue.ToString());
try try
{ {
// 使用 DataTable.Compute 方法计算表达式 // 使用 DataTable.Compute 方法计算表达式
var result = table.Compute(expression, string.Empty); var result = table.Compute(expression, string.Empty);
return Convert.ToDouble(result); return (T)result;
} }
catch catch
{ {
@@ -84,8 +86,9 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <param name="methodCall">方法名称</param> /// <param name="methodCall">方法名称</param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentException"></exception>
private static object? InvokeMethod(object target, string methodCall) private static object? InvokeMethod(object? target, string methodCall)
{ {
if (target is null) return null;
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries); var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
if (methodParts.Length != 2) if (methodParts.Length != 2)
{ {
@@ -344,15 +347,15 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <param name="value"></param> /// <param name="value"></param>
/// <param name="expression"></param> /// <param name="expression"></param>
/// <returns></returns> /// <returns></returns>
private static double ComputedNumber(object value, string expression) private static decimal ComputedNumber(object value, string expression)
{ {
double numericValue = Convert.ToDouble(value); return ComputedNumber<decimal>(value, expression);
if (!string.IsNullOrEmpty(expression)) }
{
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
}
return numericValue; private static T ComputedNumber<T>(object value, string expression) where T : struct, IComparable<T>
{
T result = value.ToConvert<T>();
return SerinArithmeticExpressionEvaluator<T>.Evaluate(expression, result);
} }
} }
} }