mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-15 20:36:35 +08:00
更新
This commit is contained in:
337
Library/SerinExpression/ConditionResolver.cs
Normal file
337
Library/SerinExpression/ConditionResolver.cs
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Serein.Library.SerinExpression
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 条件解析抽象类
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ConditionResolver
|
||||||
|
{
|
||||||
|
public abstract bool Evaluate(object obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PassConditionResolver : ConditionResolver
|
||||||
|
{
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
return Op switch
|
||||||
|
{
|
||||||
|
Operator.Pass => true,
|
||||||
|
Operator.NotPass => false,
|
||||||
|
_ => throw new NotSupportedException("不支持的条件类型")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
Pass,
|
||||||
|
NotPass,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ValueTypeConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 不进行任何操作
|
||||||
|
/// </summary>
|
||||||
|
Node,
|
||||||
|
/// <summary>
|
||||||
|
/// 大于
|
||||||
|
/// </summary>
|
||||||
|
GreaterThan,
|
||||||
|
/// <summary>
|
||||||
|
/// 小于
|
||||||
|
/// </summary>
|
||||||
|
LessThan,
|
||||||
|
/// <summary>
|
||||||
|
/// 等于
|
||||||
|
/// </summary>
|
||||||
|
Equal,
|
||||||
|
/// <summary>
|
||||||
|
/// 大于或等于
|
||||||
|
/// </summary>
|
||||||
|
GreaterThanOrEqual,
|
||||||
|
/// <summary>
|
||||||
|
/// 小于或等于
|
||||||
|
/// </summary>
|
||||||
|
LessThanOrEqual,
|
||||||
|
/// <summary>
|
||||||
|
/// 在两者之间
|
||||||
|
/// </summary>
|
||||||
|
InRange,
|
||||||
|
/// <summary>
|
||||||
|
/// 不在两者之间
|
||||||
|
/// </summary>
|
||||||
|
OutOfRange
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
public T Value { get; set; }
|
||||||
|
public T RangeStart { get; set; }
|
||||||
|
public T RangeEnd { get; set; }
|
||||||
|
|
||||||
|
public string ArithmeticExpression { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
if (obj is T typedObj)
|
||||||
|
{
|
||||||
|
double numericValue = Convert.ToDouble(typedObj);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BoolConditionResolver : ConditionResolver
|
||||||
|
{
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是
|
||||||
|
/// </summary>
|
||||||
|
Is
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
public bool Value { get; set; }
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (obj is bool boolObj)
|
||||||
|
{
|
||||||
|
return boolObj == Value;
|
||||||
|
/*switch (Op)
|
||||||
|
{
|
||||||
|
case Operator.Is:
|
||||||
|
return boolObj == Value;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StringConditionResolver : ConditionResolver
|
||||||
|
{
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 出现过
|
||||||
|
/// </summary>
|
||||||
|
Contains,
|
||||||
|
/// <summary>
|
||||||
|
/// 没有出现过
|
||||||
|
/// </summary>
|
||||||
|
DoesNotContain,
|
||||||
|
/// <summary>
|
||||||
|
/// 相等
|
||||||
|
/// </summary>
|
||||||
|
Equal,
|
||||||
|
/// <summary>
|
||||||
|
/// 不相等
|
||||||
|
/// </summary>
|
||||||
|
NotEqual,
|
||||||
|
/// <summary>
|
||||||
|
/// 起始字符串等于
|
||||||
|
/// </summary>
|
||||||
|
StartsWith,
|
||||||
|
/// <summary>
|
||||||
|
/// 结束字符串等于
|
||||||
|
/// </summary>
|
||||||
|
EndsWith
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
if (obj is string strObj)
|
||||||
|
{
|
||||||
|
return Op switch
|
||||||
|
{
|
||||||
|
Operator.Contains => strObj.Contains(Value),
|
||||||
|
Operator.DoesNotContain => !strObj.Contains(Value),
|
||||||
|
Operator.Equal => strObj == Value,
|
||||||
|
Operator.NotEqual => strObj != Value,
|
||||||
|
Operator.StartsWith => strObj.StartsWith(Value),
|
||||||
|
Operator.EndsWith => strObj.EndsWith(Value),
|
||||||
|
_ => throw new NotSupportedException("不支持的条件类型"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/* switch (Op)
|
||||||
|
{
|
||||||
|
case Operator.Contains:
|
||||||
|
return strObj.Contains(Value);
|
||||||
|
case Operator.DoesNotContain:
|
||||||
|
return !strObj.Contains(Value);
|
||||||
|
case Operator.Equal:
|
||||||
|
return strObj == Value;
|
||||||
|
case Operator.NotEqual:
|
||||||
|
return strObj != Value;
|
||||||
|
case Operator.StartsWith:
|
||||||
|
return strObj.StartsWith(Value);
|
||||||
|
case Operator.EndsWith:
|
||||||
|
return strObj.EndsWith(Value);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class MemberConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
//public string MemberPath { get; set; }
|
||||||
|
public ValueTypeConditionResolver<T>.Operator Op { get; set; }
|
||||||
|
public object? TargetObj { get; set; }
|
||||||
|
public T Value { get; set; }
|
||||||
|
|
||||||
|
public string ArithmeticExpression { get; set; }
|
||||||
|
|
||||||
|
public override bool Evaluate(object? obj)
|
||||||
|
{
|
||||||
|
//object? memberValue = GetMemberValue(obj, MemberPath);
|
||||||
|
if (TargetObj is T typedObj)
|
||||||
|
{
|
||||||
|
return new ValueTypeConditionResolver<T>
|
||||||
|
{
|
||||||
|
Op = Op,
|
||||||
|
Value = Value,
|
||||||
|
ArithmeticExpression = ArithmeticExpression,
|
||||||
|
}.Evaluate(typedObj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//private object? GetMemberValue(object? obj, string memberPath)
|
||||||
|
//{
|
||||||
|
// string[] members = memberPath[1..].Split('.');
|
||||||
|
// foreach (var member in members)
|
||||||
|
// {
|
||||||
|
// if (obj == null) return null;
|
||||||
|
// Type type = obj.GetType();
|
||||||
|
// PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
|
// FieldInfo? fieldInfo = type.GetField(member);
|
||||||
|
// if (propertyInfo != null)
|
||||||
|
// obj = propertyInfo.GetValue(obj);
|
||||||
|
// else if (fieldInfo != null)
|
||||||
|
// obj = fieldInfo.GetValue(obj);
|
||||||
|
// else
|
||||||
|
// throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
||||||
|
// }
|
||||||
|
// return obj;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MemberStringConditionResolver : ConditionResolver
|
||||||
|
{
|
||||||
|
|
||||||
|
public string MemberPath { get; set; }
|
||||||
|
|
||||||
|
public StringConditionResolver.Operator Op { get; set; }
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
object memberValue = GetMemberValue(obj, MemberPath);
|
||||||
|
if (memberValue is string strObj)
|
||||||
|
{
|
||||||
|
return new StringConditionResolver
|
||||||
|
{
|
||||||
|
Op = Op,
|
||||||
|
Value = Value
|
||||||
|
}.Evaluate(strObj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object GetMemberValue(object? obj, string memberPath)
|
||||||
|
{
|
||||||
|
string[] members = memberPath[1..].Split('.');
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
Type type = obj.GetType();
|
||||||
|
PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
|
FieldInfo? fieldInfo = type.GetField(member);
|
||||||
|
if (propertyInfo != null)
|
||||||
|
obj = propertyInfo.GetValue(obj);
|
||||||
|
else if (fieldInfo != null)
|
||||||
|
obj = fieldInfo.GetValue(obj);
|
||||||
|
else
|
||||||
|
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static string GetArithmeticExpression(string part)
|
||||||
|
{
|
||||||
|
int startIndex = part.IndexOf('[');
|
||||||
|
int endIndex = part.IndexOf(']');
|
||||||
|
if (startIndex >= 0 && endIndex > startIndex)
|
||||||
|
{
|
||||||
|
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
337
Library/SerinExpression/SerinConditionParser.cs
Normal file
337
Library/SerinExpression/SerinConditionParser.cs
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Serein.Library.SerinExpression;
|
||||||
|
|
||||||
|
public class SerinConditionParser
|
||||||
|
{
|
||||||
|
public static bool To<T>(T data, string expression)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
return ConditionParse(data, expression).Evaluate(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConditionResolver ConditionParse(object data, string expression)
|
||||||
|
{
|
||||||
|
if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
||||||
|
{
|
||||||
|
return ParseObjectExpression(data, expression);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ParseSimpleExpression(data, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//bool ContainsArithmeticOperators(string expression)
|
||||||
|
//{
|
||||||
|
// return expression.Contains('+') || expression.Contains('-') || expression.Contains('*') || expression.Contains('/');
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取计算表达式的部分
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="part"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string GetArithmeticExpression(string part)
|
||||||
|
{
|
||||||
|
int startIndex = part.IndexOf('[');
|
||||||
|
int endIndex = part.IndexOf(']');
|
||||||
|
if (startIndex >= 0 && endIndex > startIndex)
|
||||||
|
{
|
||||||
|
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取对象指定名称的成员
|
||||||
|
/// </summary>
|
||||||
|
private static object? GetMemberValue(object? obj, string memberPath)
|
||||||
|
{
|
||||||
|
string[] members = memberPath[1..].Split('.');
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
if (obj == null) return null;
|
||||||
|
Type type = obj.GetType();
|
||||||
|
PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
|
FieldInfo? fieldInfo = type.GetField(member);
|
||||||
|
if (propertyInfo != null)
|
||||||
|
obj = propertyInfo.GetValue(obj);
|
||||||
|
else if (fieldInfo != null)
|
||||||
|
obj = fieldInfo.GetValue(obj);
|
||||||
|
else
|
||||||
|
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 解析对象表达式
|
||||||
|
/// </summary>
|
||||||
|
private static ConditionResolver ParseObjectExpression(object data, string expression)
|
||||||
|
{
|
||||||
|
var parts = expression.Split(' ');
|
||||||
|
string operatorStr = parts[0];
|
||||||
|
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
||||||
|
|
||||||
|
int typeStartIndex = expression.IndexOf('<');
|
||||||
|
int typeEndIndex = expression.IndexOf('>');
|
||||||
|
|
||||||
|
string memberPath;
|
||||||
|
Type type;
|
||||||
|
object? targetObj;
|
||||||
|
if (typeStartIndex + typeStartIndex == -2)
|
||||||
|
{
|
||||||
|
memberPath = operatorStr;
|
||||||
|
targetObj = GetMemberValue(data, operatorStr);
|
||||||
|
|
||||||
|
type = targetObj.GetType();
|
||||||
|
|
||||||
|
operatorStr = parts[1].ToLower();
|
||||||
|
valueStr = string.Join(' ', parts.Skip(2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (typeStartIndex >= typeEndIndex)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("无效的表达式格式");
|
||||||
|
}
|
||||||
|
memberPath = expression.Substring(0, typeStartIndex).Trim();
|
||||||
|
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1).Trim().ToLower();
|
||||||
|
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
|
||||||
|
if (parts.Length == 3)
|
||||||
|
{
|
||||||
|
operatorStr = parts[1].ToLower();
|
||||||
|
valueStr = string.Join(' ', parts.Skip(2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
operatorStr = parts[0].ToLower();
|
||||||
|
valueStr = string.Join(' ', parts.Skip(1));
|
||||||
|
}
|
||||||
|
targetObj = GetMemberValue(data, memberPath);
|
||||||
|
|
||||||
|
Type? tempType = typeStr switch
|
||||||
|
{
|
||||||
|
"int" => typeof(int),
|
||||||
|
"double" => typeof(double),
|
||||||
|
"bool" => typeof(bool),
|
||||||
|
"string" => typeof(string),
|
||||||
|
_ => Type.GetType(typeStr)
|
||||||
|
};
|
||||||
|
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (type == typeof(int))
|
||||||
|
{
|
||||||
|
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
return new MemberConditionResolver<int>
|
||||||
|
{
|
||||||
|
TargetObj = targetObj,
|
||||||
|
//MemberPath = memberPath,
|
||||||
|
Op = ParseValueTypeOperator<int>(operatorStr),
|
||||||
|
Value = value,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (type == typeof(double))
|
||||||
|
{
|
||||||
|
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
return new MemberConditionResolver<double>
|
||||||
|
{
|
||||||
|
//MemberPath = memberPath,
|
||||||
|
TargetObj = targetObj,
|
||||||
|
Op = ParseValueTypeOperator<double>(operatorStr),
|
||||||
|
Value = value,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (type == typeof(bool))
|
||||||
|
{
|
||||||
|
return new MemberConditionResolver<bool>
|
||||||
|
{
|
||||||
|
//MemberPath = memberPath,
|
||||||
|
TargetObj = targetObj,
|
||||||
|
Op = (ValueTypeConditionResolver<bool>.Operator)ParseBoolOperator(operatorStr)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (type == typeof(string))
|
||||||
|
{
|
||||||
|
return new MemberStringConditionResolver
|
||||||
|
{
|
||||||
|
MemberPath = memberPath,
|
||||||
|
Op = ParseStringOperator(operatorStr),
|
||||||
|
Value = valueStr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException($"Type {type} is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConditionResolver ParseSimpleExpression(object data, string expression)
|
||||||
|
{
|
||||||
|
if ("pass".Equals(expression.ToLower()))
|
||||||
|
{
|
||||||
|
return new PassConditionResolver
|
||||||
|
{
|
||||||
|
Op = PassConditionResolver.Operator.Pass,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ("not pass".Equals(expression.ToLower()))
|
||||||
|
{
|
||||||
|
return new PassConditionResolver
|
||||||
|
{
|
||||||
|
Op = PassConditionResolver.Operator.NotPass,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ("!pass".Equals(expression.ToLower()))
|
||||||
|
{
|
||||||
|
return new PassConditionResolver
|
||||||
|
{
|
||||||
|
Op = PassConditionResolver.Operator.NotPass,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var parts = expression.Split(' ');
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
var op = ParseValueTypeOperator<int>(operatorStr);
|
||||||
|
if (op == ValueTypeConditionResolver<int>.Operator.InRange || op == ValueTypeConditionResolver<int>.Operator.OutOfRange)
|
||||||
|
{
|
||||||
|
var temp = valueStr.Split('-');
|
||||||
|
if (temp.Length < 2)
|
||||||
|
throw new ArgumentException($"范围无效:{valueStr}。");
|
||||||
|
int rangeStart = int.Parse(temp[0], CultureInfo.InvariantCulture);
|
||||||
|
int rangeEnd = int.Parse(temp[1], CultureInfo.InvariantCulture);
|
||||||
|
return new ValueTypeConditionResolver<int>
|
||||||
|
{
|
||||||
|
Op = op,
|
||||||
|
RangeStart = rangeStart,
|
||||||
|
RangeEnd = rangeEnd,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
return new ValueTypeConditionResolver<int>
|
||||||
|
{
|
||||||
|
Op = op,
|
||||||
|
Value = value,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (type == typeof(double))
|
||||||
|
{
|
||||||
|
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
|
return new ValueTypeConditionResolver<double>
|
||||||
|
{
|
||||||
|
Op = ParseValueTypeOperator<double>(operatorStr),
|
||||||
|
Value = value,
|
||||||
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (type == typeof(bool))
|
||||||
|
{
|
||||||
|
bool value = bool.Parse(valueStr);
|
||||||
|
return new BoolConditionResolver
|
||||||
|
{
|
||||||
|
Op = ParseBoolOperator(operatorStr),
|
||||||
|
Value = value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (type == typeof(string))
|
||||||
|
{
|
||||||
|
return new StringConditionResolver
|
||||||
|
{
|
||||||
|
Op = ParseStringOperator(operatorStr),
|
||||||
|
Value = valueStr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException($"Type {type} is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static ValueTypeConditionResolver<T>.Operator ParseValueTypeOperator<T>(string operatorStr) where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
return operatorStr switch
|
||||||
|
{
|
||||||
|
">" => ValueTypeConditionResolver<T>.Operator.GreaterThan,
|
||||||
|
"<" => ValueTypeConditionResolver<T>.Operator.LessThan,
|
||||||
|
"==" => ValueTypeConditionResolver<T>.Operator.Equal,
|
||||||
|
">=" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
||||||
|
"≥" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
||||||
|
"<=" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
||||||
|
"≤" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
||||||
|
"equals" => ValueTypeConditionResolver<T>.Operator.Equal,
|
||||||
|
"in" => ValueTypeConditionResolver<T>.Operator.InRange,
|
||||||
|
"!in" => ValueTypeConditionResolver<T>.Operator.OutOfRange,
|
||||||
|
_ => throw new ArgumentException($"Invalid operator {operatorStr} for value type.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr)
|
||||||
|
{
|
||||||
|
return operatorStr switch
|
||||||
|
{
|
||||||
|
"is" => BoolConditionResolver.Operator.Is,
|
||||||
|
"==" => BoolConditionResolver.Operator.Is,
|
||||||
|
"equals" => BoolConditionResolver.Operator.Is,
|
||||||
|
//"isFalse" => BoolConditionNode.Operator.IsFalse,
|
||||||
|
_ => throw new ArgumentException($"Invalid operator {operatorStr} for bool type.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StringConditionResolver.Operator ParseStringOperator(string operatorStr)
|
||||||
|
{
|
||||||
|
return operatorStr switch
|
||||||
|
{
|
||||||
|
"c" => StringConditionResolver.Operator.Contains,
|
||||||
|
"nc" => StringConditionResolver.Operator.DoesNotContain,
|
||||||
|
"sw" => StringConditionResolver.Operator.StartsWith,
|
||||||
|
"ew" => StringConditionResolver.Operator.EndsWith,
|
||||||
|
|
||||||
|
"contains" => StringConditionResolver.Operator.Contains,
|
||||||
|
"doesNotContain" => StringConditionResolver.Operator.DoesNotContain,
|
||||||
|
"equals" => StringConditionResolver.Operator.Equal,
|
||||||
|
"==" => StringConditionResolver.Operator.Equal,
|
||||||
|
"notEquals" => StringConditionResolver.Operator.NotEqual,
|
||||||
|
"!=" => StringConditionResolver.Operator.NotEqual,
|
||||||
|
"startsWith" => StringConditionResolver.Operator.StartsWith,
|
||||||
|
"endsWith" => StringConditionResolver.Operator.EndsWith,
|
||||||
|
_ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
207
Library/SerinExpression/SerinExpressionEvaluator.cs
Normal file
207
Library/SerinExpression/SerinExpressionEvaluator.cs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Serein.Library.SerinExpression
|
||||||
|
{
|
||||||
|
public class SerinArithmeticExpressionEvaluator
|
||||||
|
{
|
||||||
|
private static readonly DataTable table = new DataTable();
|
||||||
|
|
||||||
|
public static double Evaluate(string expression, double inputValue)
|
||||||
|
{
|
||||||
|
// 替换占位符@为输入值
|
||||||
|
expression = expression.Replace("@", inputValue.ToString());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 使用 DataTable.Compute 方法计算表达式
|
||||||
|
var result = table.Compute(expression, string.Empty);
|
||||||
|
return Convert.ToDouble(result);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid arithmetic expression.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SerinExpressionEvaluator
|
||||||
|
{
|
||||||
|
public static object Evaluate(string expression, object targetObJ, out bool IsChange)
|
||||||
|
{
|
||||||
|
var parts = expression.Split([' '], 2);
|
||||||
|
if (parts.Length != 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid expression format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var operation = parts[0].ToLower();
|
||||||
|
var operand = parts[1][0] == '.' ? parts[1][1..] : parts[1];
|
||||||
|
|
||||||
|
var result = operation switch
|
||||||
|
{
|
||||||
|
"@num" => ComputedNumber(targetObJ, operand),
|
||||||
|
"@call" => InvokeMethod(targetObJ, operand),
|
||||||
|
"@get" => GetMember(targetObJ, operand),
|
||||||
|
"@set" => SetMember(targetObJ, operand),
|
||||||
|
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
||||||
|
};
|
||||||
|
|
||||||
|
IsChange = operation switch
|
||||||
|
{
|
||||||
|
"@num" => true,
|
||||||
|
"@call" => true,
|
||||||
|
"@get" => true,
|
||||||
|
"@set" => false,
|
||||||
|
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static readonly char[] separator = ['(', ')'];
|
||||||
|
private static readonly char[] separatorArray = [','];
|
||||||
|
|
||||||
|
private static object InvokeMethod(object target, string methodCall)
|
||||||
|
{
|
||||||
|
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (methodParts.Length != 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid method call format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodName = methodParts[0];
|
||||||
|
var parameterList = methodParts[1];
|
||||||
|
var parameters = parameterList.Split(separatorArray, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(p => p.Trim())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var method = target.GetType().GetMethod(methodName);
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Method {methodName} not found on target.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var parameterValues = method.GetParameters()
|
||||||
|
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
return method.Invoke(target, parameterValues);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GetMember(object target, string memberPath)
|
||||||
|
{
|
||||||
|
var members = memberPath.Split('.');
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (target == null) return null;
|
||||||
|
|
||||||
|
|
||||||
|
var property = target.GetType().GetProperty(member);
|
||||||
|
if (property != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
target = property.GetValue(target);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var field = target.GetType().GetField(member);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
target = field.GetValue(target);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Member {member} not found on target.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return target;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object SetMember(object target, string assignment)
|
||||||
|
{
|
||||||
|
var parts = assignment.Split(new[] { '=' }, 2);
|
||||||
|
if (parts.Length != 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid assignment format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var memberPath = parts[0].Trim();
|
||||||
|
var value = parts[1].Trim();
|
||||||
|
|
||||||
|
var members = memberPath.Split('.');
|
||||||
|
for (int i = 0; i < members.Length - 1; i++)
|
||||||
|
{
|
||||||
|
var member = members[i];
|
||||||
|
|
||||||
|
var property = target.GetType().GetProperty(member);
|
||||||
|
|
||||||
|
if (property != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
target = property.GetValue(target);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var field = target.GetType().GetField(member);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
target = field.GetValue(target);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Member {member} not found on target.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastMember = members.Last();
|
||||||
|
|
||||||
|
var lastProperty = target.GetType().GetProperty(lastMember);
|
||||||
|
|
||||||
|
if (lastProperty != null)
|
||||||
|
{
|
||||||
|
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
||||||
|
lastProperty.SetValue(target, convertedValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var lastField = target.GetType().GetField(lastMember);
|
||||||
|
if (lastField != null)
|
||||||
|
{
|
||||||
|
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
|
||||||
|
lastField.SetValue(target, convertedValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Member {lastMember} not found on target.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double ComputedNumber(object value, string expression)
|
||||||
|
{
|
||||||
|
double numericValue = Convert.ToDouble(value);
|
||||||
|
if (!string.IsNullOrEmpty(expression))
|
||||||
|
{
|
||||||
|
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return numericValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,10 @@
|
|||||||
<None Remove="bin\**" />
|
<None Remove="bin\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="IoTClient" Version="1.0.40" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||||
<ProjectReference Include="..\NodeFlow\Serein.NodeFlow.csproj" />
|
<ProjectReference Include="..\NodeFlow\Serein.NodeFlow.csproj" />
|
||||||
|
|||||||
46
MyDll/test.cs
Normal file
46
MyDll/test.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using IoTClient.Clients.PLC;
|
||||||
|
using IoTClient.Common.Enums;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MyDll
|
||||||
|
{
|
||||||
|
internal class test
|
||||||
|
{
|
||||||
|
private void T()
|
||||||
|
{
|
||||||
|
SiemensClient client = new SiemensClient(SiemensVersion.S7_200Smart, "127.0.0.1", 102);
|
||||||
|
|
||||||
|
//2、写操作
|
||||||
|
client.Write("Q1.3", true);
|
||||||
|
client.Write("V2205", (short)11);
|
||||||
|
client.Write("V2209", 33);
|
||||||
|
client.Write("V2305", "orderCode"); //写入字符串
|
||||||
|
|
||||||
|
//3、读操作
|
||||||
|
var value1 = client.ReadBoolean("Q1.3").Value;
|
||||||
|
var value2 = client.ReadInt16("V2205").Value;
|
||||||
|
var value3 = client.ReadInt32("V2209").Value;
|
||||||
|
var value4 = client.ReadString("V2305").Value; //读取字符串
|
||||||
|
|
||||||
|
//4、如果没有主动Open,则会每次读写操作的时候自动打开自动和关闭连接,这样会使读写效率大大减低。所以建议手动Open和Close。
|
||||||
|
client.Open();
|
||||||
|
|
||||||
|
//5、读写操作都会返回操作结果对象Result
|
||||||
|
var result = client.ReadInt16("V2205");
|
||||||
|
//5.1 读取是否成功(true或false)
|
||||||
|
var isSucceed = result.IsSucceed;
|
||||||
|
//5.2 读取失败的异常信息
|
||||||
|
var errMsg = result.Err;
|
||||||
|
//5.3 读取操作实际发送的请求报文
|
||||||
|
var requst = result.Requst;
|
||||||
|
//5.4 读取操作服务端响应的报文
|
||||||
|
var response = result.Response;
|
||||||
|
//5.5 读取到的值
|
||||||
|
var value = result.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Serein.NodeFlow.SerinExpression;
|
|
||||||
|
using Serein.Library.SerinExpression;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Model
|
namespace Serein.NodeFlow.Model
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Serein.NodeFlow;
|
using Serein.Library.SerinExpression;
|
||||||
using Serein.NodeFlow.SerinExpression;
|
using Serein.NodeFlow;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|||||||
@@ -9,8 +9,11 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="bin\**" />
|
<Compile Remove="bin\**" />
|
||||||
|
<Compile Remove="SerinExpression\**" />
|
||||||
<EmbeddedResource Remove="bin\**" />
|
<EmbeddedResource Remove="bin\**" />
|
||||||
|
<EmbeddedResource Remove="SerinExpression\**" />
|
||||||
<None Remove="bin\**" />
|
<None Remove="bin\**" />
|
||||||
|
<None Remove="SerinExpression\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ using static Serein.WorkBench.MainWindow;
|
|||||||
|
|
||||||
namespace Serein.WorkBench
|
namespace Serein.WorkBench
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 拖拽创建节点类型
|
||||||
|
/// </summary>
|
||||||
public static class MouseNodeType
|
public static class MouseNodeType
|
||||||
{
|
{
|
||||||
public static string RegionType { get; } = nameof(RegionType);
|
public static string RegionType { get; } = nameof(RegionType);
|
||||||
@@ -1997,10 +1999,6 @@ namespace Serein.WorkBench
|
|||||||
return Uri.UnescapeDataString(relativeUri.ToString().Replace('/', System.IO.Path.DirectorySeparatorChar));
|
return Uri.UnescapeDataString(relativeUri.ToString().Replace('/', System.IO.Path.DirectorySeparatorChar));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线
|
#region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线
|
||||||
|
|
||||||
|
|
||||||
@@ -2251,7 +2249,7 @@ namespace Serein.WorkBench
|
|||||||
}
|
}
|
||||||
else if (localhost == Localhost.Right)
|
else if (localhost == Localhost.Right)
|
||||||
{
|
{
|
||||||
centerPoint.X += halfWidth;
|
centerPoint.X -= -halfWidth;
|
||||||
centerPoint.Y -= direction.Y / Math.Abs(direction.X) * halfHeight - margin;
|
centerPoint.Y -= direction.Y / Math.Abs(direction.X) * halfHeight - margin;
|
||||||
}
|
}
|
||||||
else if (localhost == Localhost.Top)
|
else if (localhost == Localhost.Top)
|
||||||
@@ -2261,7 +2259,7 @@ namespace Serein.WorkBench
|
|||||||
}
|
}
|
||||||
else if (localhost == Localhost.Bottom)
|
else if (localhost == Localhost.Bottom)
|
||||||
{
|
{
|
||||||
centerPoint.Y += halfHeight;
|
centerPoint.Y -= -halfHeight;
|
||||||
centerPoint.X -= direction.X / Math.Abs(direction.Y) * halfWidth - margin;
|
centerPoint.X -= direction.X / Math.Abs(direction.Y) * halfWidth - margin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<CheckBox VerticalContentAlignment="Center">
|
<CheckBox VerticalContentAlignment="Center">
|
||||||
|
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
<TextBlock Text="测试方法" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<TextBlock Text="{Binding MethodDetails.MethodTips}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user