mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
更改了拖入的DLL显示名称
尝试添加了web自动化测试(基于Selenium)
This commit is contained in:
@@ -114,7 +114,9 @@ namespace Serein.DbSql
|
||||
{
|
||||
// Console.WriteLine($"主数据库无法连接,IP:{IP},端口:{Port}");
|
||||
DBSync.SetIsNeedSyncData(true); // 网络不可达
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
// 检查是否需要同步数据
|
||||
@@ -134,7 +136,9 @@ namespace Serein.DbSql
|
||||
catch // (Exception ex)
|
||||
{
|
||||
// Console.WriteLine($"发生异常:{ex.Message}");
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,8 +172,14 @@ namespace Serein.DbSql
|
||||
}
|
||||
public static string GetDescription(DBSyncExType value)
|
||||
{
|
||||
|
||||
FieldInfo field = value.GetType().GetField(value.ToString());
|
||||
|
||||
|
||||
|
||||
DescriptionAttribute attribute = (DescriptionAttribute)field.GetCustomAttribute(typeof(DescriptionAttribute));
|
||||
|
||||
|
||||
return attribute == null ? value.ToString() : attribute.Description;
|
||||
}
|
||||
|
||||
@@ -184,15 +194,21 @@ namespace Serein.DbSql
|
||||
/// <summary>
|
||||
/// 主数据库配置
|
||||
/// </summary>
|
||||
|
||||
private static ConnectionConfig PrimaryConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 从数据库配置
|
||||
/// </summary>
|
||||
|
||||
private static ConnectionConfig SecondaryConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主数据库IP
|
||||
/// </summary>
|
||||
|
||||
private static string Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主数据库端口
|
||||
/// </summary>
|
||||
@@ -200,8 +216,12 @@ namespace Serein.DbSql
|
||||
/// <summary>
|
||||
/// 同步数据事件(远程数据库,本地数据库,是否执行成功)
|
||||
/// </summary>
|
||||
|
||||
private static Func<SqlSugarClient, SqlSugarClient, bool> SyncDataEvent { get; set; }
|
||||
|
||||
|
||||
private static Action<bool> StateChangeEvent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据库设置锁
|
||||
/// </summary>
|
||||
@@ -467,8 +487,14 @@ namespace Serein.DbSql
|
||||
|
||||
public static string GetDescription(DBSyncStart value)
|
||||
{
|
||||
|
||||
FieldInfo field = value.GetType().GetField(value.ToString());
|
||||
|
||||
|
||||
|
||||
DescriptionAttribute attribute = (DescriptionAttribute)field.GetCustomAttribute(typeof(DescriptionAttribute));
|
||||
|
||||
|
||||
return attribute == null ? value.ToString() : attribute.Description;
|
||||
}
|
||||
|
||||
@@ -565,11 +591,15 @@ namespace Serein.DbSql
|
||||
};
|
||||
break;
|
||||
default:
|
||||
|
||||
config = null;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -398,7 +398,9 @@ namespace Serein.Helper
|
||||
|
||||
isHaveErr = true;
|
||||
ErrMsg = ex.Message;
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,7 +505,9 @@ namespace Serein.Helper
|
||||
}
|
||||
|
||||
|
||||
|
||||
public virtual string GetPageList(Pagination pagination, Expression<Func<TEntity, bool>> where = null)
|
||||
|
||||
{
|
||||
//DatabaseSync.StartcaControls();
|
||||
return new
|
||||
@@ -523,7 +527,9 @@ namespace Serein.Helper
|
||||
}
|
||||
|
||||
|
||||
|
||||
public virtual List<TEntity> GetTop(int Top, Expression<Func<TEntity, object>> expression, OrderByType _OrderByType = OrderByType.Asc, Expression<Func<TEntity, bool>> where = null, string selstr = "*")
|
||||
|
||||
{
|
||||
return SyncRead(db => db.Queryable<TEntity>().Select(selstr).WhereIF(where != null, where)
|
||||
.Take(Top)
|
||||
@@ -539,14 +545,18 @@ namespace Serein.Helper
|
||||
/// <param name="_OrderByType"></param>
|
||||
/// <param name="where"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
public virtual TEntity GetFirst(Expression<Func<TEntity, object>> OrderExpression, OrderByType _OrderByType = OrderByType.Asc, Expression<Func<TEntity, bool>> where = null)
|
||||
|
||||
{
|
||||
return SyncRead(db => db.Queryable<TEntity>().Filter(filterName, isDisabledGobalFilter: true).WhereIF(where != null, where)
|
||||
.OrderBy(OrderExpression, _OrderByType)
|
||||
.First());
|
||||
}
|
||||
|
||||
|
||||
public virtual List<TEntity> GetList(Pagination pagination, Expression<Func<TEntity, bool>> where = null)
|
||||
|
||||
{
|
||||
int totalNumber = 0;
|
||||
List<TEntity> result = SyncRead(db => db.Queryable<TEntity>().WhereIF(where != null, where).OrderBy(pagination.sidx + " " + pagination.sord)
|
||||
@@ -557,7 +567,9 @@ namespace Serein.Helper
|
||||
}
|
||||
|
||||
|
||||
|
||||
public virtual List<TEntity> GetList(Expression<Func<TEntity, bool>> where = null)
|
||||
|
||||
{
|
||||
return SyncRead(db => db.Queryable<TEntity>().WhereIF(where != null, where).Filter(filterName, isDisabledGobalFilter: true)
|
||||
.ToList());
|
||||
@@ -569,7 +581,11 @@ namespace Serein.Helper
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public virtual DataTable GetDataTable(Expression<Func<TEntity, bool>> where = null, Pagination pagination = null)
|
||||
|
||||
|
||||
{
|
||||
if (pagination != null)
|
||||
{
|
||||
@@ -598,7 +614,9 @@ namespace Serein.Helper
|
||||
return 0;
|
||||
});
|
||||
|
||||
|
||||
filterName = null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -818,13 +836,17 @@ namespace Serein.Helper
|
||||
/// <summary>
|
||||
/// /排序列
|
||||
/// </summary>
|
||||
|
||||
public string sidx { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 排序类型
|
||||
/// </summary>
|
||||
|
||||
public string sord { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 总记录数
|
||||
/// </summary>
|
||||
|
||||
@@ -39,7 +39,9 @@ namespace Serein.DynamicFlow
|
||||
/// <summary>
|
||||
/// 动态流程上下文
|
||||
/// </summary>
|
||||
|
||||
public class DynamicContext(IServiceContainer serviceContainer)
|
||||
|
||||
{
|
||||
|
||||
private readonly string contextGuid = "";//System.Guid.NewGuid().ToString();
|
||||
|
||||
@@ -34,19 +34,25 @@ namespace Serein.DynamicFlow
|
||||
/// 方法需要的类型
|
||||
/// </summary>
|
||||
public Type DataType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 方法入参参数名称
|
||||
/// </summary>
|
||||
public string ParameterName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 入参值
|
||||
/// </summary>
|
||||
|
||||
public string DataValue { get; set; }
|
||||
|
||||
|
||||
|
||||
public string[] Items { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public ExplicitData Clone() => new()
|
||||
{
|
||||
Index = Index,
|
||||
@@ -64,7 +70,7 @@ namespace Serein.DynamicFlow
|
||||
|
||||
public class MethodDetails
|
||||
{
|
||||
public MethodDetails CpoyNew()
|
||||
public MethodDetails Clone()
|
||||
{
|
||||
return new MethodDetails
|
||||
{
|
||||
@@ -73,37 +79,45 @@ namespace Serein.DynamicFlow
|
||||
MethodDelegate = MethodDelegate,
|
||||
MethodDynamicType = MethodDynamicType,
|
||||
MethodGuid = Guid.NewGuid().ToString(),
|
||||
MethodTips = MethodTips + " Cpoy",
|
||||
//ParameterTypes = ParameterTypes,
|
||||
MethodTips = MethodTips ,
|
||||
ReturnType = ReturnType,
|
||||
MethodName = MethodName,
|
||||
MethodLockName = MethodLockName,
|
||||
//ExplicitDataValues = ExplicitDataValues.Select(it => "").ToArray(),
|
||||
|
||||
ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(),
|
||||
//IsExplicits = IsExplicits,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 作用实例
|
||||
/// </summary>
|
||||
|
||||
public Type ActingInstanceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 作用实例
|
||||
/// </summary>
|
||||
|
||||
public object ActingInstance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 方法GUID
|
||||
/// </summary>
|
||||
|
||||
public string MethodGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 方法名称
|
||||
/// </summary>
|
||||
|
||||
public string MethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 方法委托
|
||||
/// </summary>
|
||||
|
||||
public Delegate MethodDelegate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点类型
|
||||
/// </summary>
|
||||
@@ -111,28 +125,36 @@ namespace Serein.DynamicFlow
|
||||
/// <summary>
|
||||
/// 锁名称
|
||||
/// </summary>
|
||||
|
||||
public string MethodLockName { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 方法说明
|
||||
/// </summary>
|
||||
|
||||
public string MethodTips { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 参数内容
|
||||
/// </summary>
|
||||
|
||||
public ExplicitData[] ExplicitDatas { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 出参类型
|
||||
/// </summary>
|
||||
|
||||
public Type ReturnType { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public bool IsCanConnect(Type returnType)
|
||||
{
|
||||
if (ExplicitDatas.Length == 0)
|
||||
|
||||
@@ -4,7 +4,12 @@ using Serein.DynamicFlow.NodeModel;
|
||||
using Serein.DynamicFlow.Tool;
|
||||
using Serein.Web;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DynamicDemo.Node
|
||||
{
|
||||
@@ -14,13 +19,19 @@ namespace DynamicDemo.Node
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class NodeFlowStarter(IServiceContainer serviceContainer,List<MethodDetails> methodDetails)
|
||||
|
||||
{
|
||||
private readonly IServiceContainer ServiceContainer = serviceContainer;
|
||||
private readonly List<MethodDetails> methodDetails = methodDetails;
|
||||
|
||||
private Action ExitAction = null;
|
||||
|
||||
|
||||
private DynamicContext context = null;
|
||||
|
||||
|
||||
public NodeRunTcs MainCts;
|
||||
|
||||
/// <summary>
|
||||
@@ -28,6 +39,11 @@ namespace DynamicDemo.Node
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
//public async Task RunAsync1(List<NodeBase> nodes)
|
||||
//{
|
||||
// await Task.Run(async ()=> await StartRunAsync(nodes));
|
||||
//}
|
||||
|
||||
public async Task RunAsync(List<NodeBase> nodes)
|
||||
{
|
||||
var startNode = nodes.FirstOrDefault(p => p.IsStart);
|
||||
@@ -109,18 +125,24 @@ namespace DynamicDemo.Node
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if (!DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var func = md.ExplicitDatas.Length == 0 ? ((Func<object, object, Task<FlipflopContext>>)del) : ((Func<object, object[], Task<FlipflopContext>>)del);
|
||||
|
||||
while (!MainCts.IsCancellationRequested) // 循环中直到栈为空才会退出
|
||||
{
|
||||
object?[]? parameters = singleFlipFlopNode.GetParameters(context, md);
|
||||
// 调用委托并获取结果
|
||||
|
||||
|
||||
FlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters);
|
||||
|
||||
|
||||
|
||||
if (flipflopContext == null)
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Serein.DynamicFlow;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Serein.DynamicFlow.NodeModel
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Serein.DynamicFlow.Tool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Serein.DynamicFlow.NodeModel
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
using Serein.DynamicFlow.Tool;
|
||||
using Newtonsoft.Json;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Serein.DynamicFlow.NodeModel
|
||||
{
|
||||
@@ -18,12 +23,20 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
/// </summary>
|
||||
public abstract class NodeBase : IDynamicFlowNode
|
||||
{
|
||||
|
||||
public MethodDetails MethodDetails { get; set; }
|
||||
|
||||
|
||||
public string Guid { get; set; }
|
||||
|
||||
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
public bool IsStart { get; set; }
|
||||
|
||||
public string DelegateName { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 运行时的上一节点
|
||||
/// </summary>
|
||||
@@ -63,6 +76,7 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
{
|
||||
MethodDetails md = MethodDetails;
|
||||
object? result = null;
|
||||
|
||||
if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
||||
{
|
||||
if (md.ExplicitDatas.Length == 0)
|
||||
@@ -81,16 +95,25 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
object?[]? parameters = GetParameters(context, MethodDetails);
|
||||
if (md.ReturnType == typeof(void))
|
||||
{
|
||||
|
||||
|
||||
((Action<object, object[]>)del).Invoke(md.ActingInstance, parameters);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
result = ((Func<object, object[], object>)del).Invoke(md.ActingInstance, parameters);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
// context.SetFlowData(result);
|
||||
// CurrentData = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -99,6 +122,7 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
{
|
||||
MethodDetails md = MethodDetails;
|
||||
object? result = null;
|
||||
|
||||
if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
||||
{
|
||||
if (md.ExplicitDatas.Length == 0)
|
||||
@@ -130,8 +154,12 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
{
|
||||
object?[]? parameters = GetParameters(context, MethodDetails);
|
||||
// 调用委托并获取结果
|
||||
|
||||
|
||||
FlipflopContext flipflopContext = await ((Func<object, object[], Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance, parameters);
|
||||
|
||||
|
||||
|
||||
if (flipflopContext != null)
|
||||
{
|
||||
if (flipflopContext.State == FfState.Cancel)
|
||||
@@ -148,6 +176,7 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
// context.SetFlowData(result);
|
||||
// CurrentData = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -251,7 +280,11 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -261,30 +294,42 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
var tmpParameter = PreviousNode?.FlowData?.ToString();
|
||||
if (mdEd.DataType.IsEnum)
|
||||
{
|
||||
|
||||
var enumValue = Enum.Parse(mdEd.DataType, tmpParameter);
|
||||
|
||||
parameters[i] = enumValue;
|
||||
}
|
||||
else if (mdEd.DataType == typeof(string))
|
||||
{
|
||||
|
||||
parameters[i] = tmpParameter;
|
||||
|
||||
}
|
||||
else if (mdEd.DataType == typeof(bool))
|
||||
{
|
||||
|
||||
parameters[i] = bool.Parse(tmpParameter);
|
||||
|
||||
}
|
||||
else if (mdEd.DataType == typeof(int))
|
||||
{
|
||||
|
||||
parameters[i] = int.Parse(tmpParameter);
|
||||
|
||||
}
|
||||
else if (mdEd.DataType == typeof(double))
|
||||
{
|
||||
|
||||
parameters[i] = double.Parse(tmpParameter);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tmpParameter != null && mdEd.DataType!= null)
|
||||
{
|
||||
|
||||
parameters[i] = ConvertValue(tmpParameter, mdEd.DataType);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Serein.DynamicFlow.SerinExpression;
|
||||
using Serein.DynamicFlow.Tool;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
@@ -22,8 +23,10 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
/// <summary>
|
||||
/// 条件表达式
|
||||
/// </summary>
|
||||
|
||||
public string Expression { get; set; }
|
||||
|
||||
|
||||
public override object? Execute(DynamicContext context)
|
||||
{
|
||||
// 接收上一节点参数or自定义参数内容
|
||||
|
||||
@@ -12,22 +12,16 @@ namespace Serein.DynamicFlow.NodeModel
|
||||
/// </summary>
|
||||
public class SingleExpOpNode : NodeBase
|
||||
{
|
||||
|
||||
public string Expression { get; set; }
|
||||
|
||||
|
||||
public override object? Execute(DynamicContext context)
|
||||
{
|
||||
//if (PreviousNode != null && PreviousNode.FlowData == null)
|
||||
//{
|
||||
// // 存在
|
||||
// throw new InvalidOperationException("previous node data is null.");
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
|
||||
//}
|
||||
var data = PreviousNode?.FlowData;
|
||||
|
||||
var newData = SerinExpressionEvaluator.Evaluate(Expression, data, out bool isChange);
|
||||
|
||||
FlowState = true;
|
||||
Console.WriteLine(newData);
|
||||
if (isChange)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Serein.DynamicFlow.SerinExpression
|
||||
@@ -72,8 +73,10 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
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)
|
||||
@@ -179,8 +182,10 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
}
|
||||
|
||||
public Operator Op { get; set; }
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
|
||||
public override bool Evaluate(object obj)
|
||||
{
|
||||
if (obj is string strObj)
|
||||
@@ -221,7 +226,9 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
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);
|
||||
@@ -259,10 +266,14 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
|
||||
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);
|
||||
@@ -282,7 +293,9 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
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);
|
||||
@@ -293,7 +306,9 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
else
|
||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -308,7 +323,9 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
{
|
||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Serein.DynamicFlow.SerinExpression;
|
||||
@@ -10,7 +11,9 @@ public class SerinConditionParser
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
return ConditionParse(data, expression).Evaluate(data);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -30,10 +33,12 @@ public class SerinConditionParser
|
||||
return ParseSimpleExpression(data, expression);
|
||||
}
|
||||
|
||||
|
||||
bool ContainsArithmeticOperators(string expression)
|
||||
{
|
||||
return expression.Contains('+') || expression.Contains('-') || expression.Contains('*') || expression.Contains('/');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static string GetArithmeticExpression(string part)
|
||||
@@ -44,7 +49,9 @@ public class SerinConditionParser
|
||||
{
|
||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
private static object? GetMemberValue(object? obj, string memberPath)
|
||||
{
|
||||
@@ -81,7 +88,9 @@ public class SerinConditionParser
|
||||
{
|
||||
memberPath = operatorStr;
|
||||
targetObj = GetMemberValue(data, operatorStr);
|
||||
|
||||
type = targetObj.GetType();
|
||||
|
||||
operatorStr = parts[1].ToLower();
|
||||
valueStr = string.Join(' ', parts.Skip(2));
|
||||
}
|
||||
@@ -105,14 +114,16 @@ public class SerinConditionParser
|
||||
valueStr = string.Join(' ', parts.Skip(1));
|
||||
}
|
||||
targetObj = GetMemberValue(data, memberPath);
|
||||
Type tempType = typeStr switch
|
||||
|
||||
Type? tempType = typeStr switch
|
||||
{
|
||||
"int" => typeof(int),
|
||||
"double" => typeof(double),
|
||||
"bool" => typeof(bool),
|
||||
"string" => typeof(string),
|
||||
_ => Type.GetType(typeStr)
|
||||
};
|
||||
type = (tempType ?? Type.GetType(typeStr)) ?? throw new ArgumentException("对象表达式无效的类型声明");
|
||||
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -92,7 +92,9 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
|
||||
.ToArray();
|
||||
|
||||
|
||||
return method.Invoke(target, parameterValues);
|
||||
|
||||
}
|
||||
|
||||
private static object GetMember(object target, string memberPath)
|
||||
@@ -100,19 +102,25 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
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
|
||||
{
|
||||
@@ -121,7 +129,9 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
private static object SetMember(object target, string assignment)
|
||||
@@ -139,17 +149,23 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
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
|
||||
{
|
||||
@@ -159,7 +175,9 @@ namespace Serein.DynamicFlow.SerinExpression
|
||||
}
|
||||
|
||||
var lastMember = members.Last();
|
||||
|
||||
var lastProperty = target.GetType().GetProperty(lastMember);
|
||||
|
||||
if (lastProperty != null)
|
||||
{
|
||||
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
||||
|
||||
@@ -3,8 +3,11 @@ using Serein.DynamicFlow;
|
||||
using Serein.DynamicFlow.NodeModel;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.DynamicFlow.Tool;
|
||||
|
||||
@@ -38,7 +41,9 @@ public static class DelegateGenerator
|
||||
|
||||
foreach (var method in methods)
|
||||
{
|
||||
|
||||
var methodDetails = CreateMethodDetails(serviceContainer, type, method, assemblyName);
|
||||
|
||||
methodDetailsDictionary.TryAdd(methodDetails.MethodName, methodDetails);
|
||||
}
|
||||
|
||||
@@ -76,6 +81,7 @@ public static class DelegateGenerator
|
||||
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
|
||||
|
||||
|
||||
|
||||
return new MethodDetails
|
||||
{
|
||||
ActingInstanceType = type,
|
||||
@@ -88,6 +94,7 @@ public static class DelegateGenerator
|
||||
ExplicitDatas = explicitDataOfParameters,
|
||||
ReturnType = method.ReturnType,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private static ExplicitData[] GetExplicitDataOfParameters(ParameterInfo[] parameters)
|
||||
@@ -99,6 +106,9 @@ public static class DelegateGenerator
|
||||
string explicitTypeName = GetExplicitTypeName(it.ParameterType);
|
||||
var items = GetExplicitItems(it.ParameterType, explicitTypeName);
|
||||
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
|
||||
|
||||
|
||||
|
||||
return new ExplicitData
|
||||
{
|
||||
IsExplicitData = it.GetCustomAttribute(typeof(ExplicitAttribute)) is ExplicitAttribute,
|
||||
@@ -110,6 +120,9 @@ public static class DelegateGenerator
|
||||
DataValue = it.HasDefaultValue ? it.DefaultValue.ToString() : "",
|
||||
Items = items.ToArray(),
|
||||
};
|
||||
|
||||
|
||||
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.DynamicFlow.Tool
|
||||
{
|
||||
|
||||
@@ -29,15 +29,15 @@ namespace Serein.DynamicFlow.Tool
|
||||
{
|
||||
while (waitTcss.Count > 0)
|
||||
{
|
||||
|
||||
waitTcss.Pop().SetResult(state);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
lock (TcsEvent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public TaskCompletionSource<object> CreateTcs(TSignal signal)
|
||||
@@ -46,18 +46,9 @@ namespace Serein.DynamicFlow.Tool
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
TcsEvent.GetOrAdd(signal, _ => new Stack<TaskCompletionSource<object>>()).Push(tcs);
|
||||
return tcs;
|
||||
lock (TcsEvent)
|
||||
{
|
||||
/*if(TcsEvent.TryRemove(signal, out var tcss))
|
||||
{
|
||||
//tcs.TrySetException(new TcsSignalException("试图获取已存在的任务"));
|
||||
throw new TcsSignalException("试图获取已存在的任务");
|
||||
}*/
|
||||
|
||||
|
||||
/*TcsEvent.TryAdd(signal, tcs);
|
||||
return tcs;*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
//public TaskCompletionSource<object> GetOrCreateTcs(TSignal signal)
|
||||
//{
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="obj\**" />
|
||||
<EmbeddedResource Remove="obj\**" />
|
||||
<None Remove="obj\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.166" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -3,6 +3,10 @@ using Microsoft.Win32;
|
||||
using NetTaste;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Serein
|
||||
{
|
||||
@@ -46,10 +50,12 @@ namespace Serein
|
||||
|
||||
public ServiceContainer()
|
||||
{
|
||||
|
||||
_dependencies = new ConcurrentDictionary<string, object>
|
||||
{
|
||||
[typeof(IServiceContainer).FullName] = this
|
||||
};
|
||||
|
||||
_typeMappings = new ConcurrentDictionary<string, Type>();
|
||||
_waitingForInstantiation = [];
|
||||
}
|
||||
@@ -57,16 +63,24 @@ namespace Serein
|
||||
{
|
||||
Register(type);
|
||||
object instance;
|
||||
|
||||
if (_dependencies.ContainsKey(type.FullName))
|
||||
{
|
||||
instance = _dependencies[type.FullName];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
instance = Activator.CreateInstance(type);
|
||||
|
||||
|
||||
_dependencies[type.FullName] = instance;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return instance;
|
||||
|
||||
}
|
||||
public T CreateServiceInstance<T>(params object[] parameters)
|
||||
{
|
||||
@@ -90,10 +104,12 @@ namespace Serein
|
||||
|
||||
public IServiceContainer Register(Type type, params object[] parameters)
|
||||
{
|
||||
|
||||
if (!_typeMappings.ContainsKey(type.FullName))
|
||||
{
|
||||
_typeMappings[type.FullName] = type;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
public IServiceContainer Register<T>(params object[] parameters)
|
||||
@@ -111,34 +127,56 @@ namespace Serein
|
||||
|
||||
public object Get(Type type)
|
||||
{
|
||||
|
||||
|
||||
if (!_dependencies.TryGetValue(type.FullName, out object value))
|
||||
{
|
||||
Register(type);
|
||||
|
||||
value = Instantiate(type);
|
||||
|
||||
InjectDependencies(type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public T Get<T>()
|
||||
{
|
||||
|
||||
|
||||
if(!_dependencies.TryGetValue(typeof(T).FullName, out object value))
|
||||
{
|
||||
Register<T>();
|
||||
|
||||
value = Instantiate(typeof(T));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return (T)value;
|
||||
|
||||
|
||||
//throw new InvalidOperationException("目标类型未创建实例");
|
||||
}
|
||||
public IServiceContainer Build()
|
||||
{
|
||||
foreach (var type in _typeMappings.Values)
|
||||
{
|
||||
|
||||
if(!_dependencies.ContainsKey(type.FullName))
|
||||
{
|
||||
|
||||
_dependencies[type.FullName] = Activator.CreateInstance(type);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach (var instance in _dependencies.Values)
|
||||
@@ -171,10 +209,12 @@ namespace Serein
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var propertyType = property.PropertyType;
|
||||
|
||||
if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
|
||||
{
|
||||
property.SetValue(instance, dependencyInstance);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +227,9 @@ namespace Serein
|
||||
var instance = Instantiate(implementationType);
|
||||
if (instance != null)
|
||||
{
|
||||
|
||||
_dependencies[waitingType.FullName] = instance;
|
||||
|
||||
_waitingForInstantiation.Remove(waitingType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,13 +42,17 @@ namespace Serein.Tool
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<T>(input);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
// return default(T);
|
||||
|
||||
return default;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +83,9 @@ namespace Serein.Tool
|
||||
return dataTable;
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public static bool IsExistRows(DataTable dt)
|
||||
@@ -111,10 +117,14 @@ namespace Serein.Tool
|
||||
{
|
||||
if (entitys == null || entitys.Count < 1)
|
||||
{
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Type type = entitys[0].GetType();
|
||||
|
||||
PropertyInfo[] properties = type.GetProperties();
|
||||
DataTable dataTable = new DataTable();
|
||||
for (int i = 0; i < properties.Length; i++)
|
||||
@@ -124,16 +134,22 @@ namespace Serein.Tool
|
||||
|
||||
foreach (T entity in entitys)
|
||||
{
|
||||
|
||||
object obj = entity;
|
||||
|
||||
|
||||
if (obj.GetType() != type)
|
||||
{
|
||||
throw new Exception("要转换的集合元素类型不一致");
|
||||
}
|
||||
|
||||
|
||||
object[] array = new object[properties.Length];
|
||||
for (int j = 0; j < properties.Length; j++)
|
||||
{
|
||||
|
||||
array[j] = properties[j].GetValue(obj, null);
|
||||
|
||||
}
|
||||
|
||||
dataTable.Rows.Add(array);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Serein.Web
|
||||
using System;
|
||||
|
||||
namespace Serein.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示参数为url中的数据(Get请求中不需要显式标注)
|
||||
@@ -61,7 +63,9 @@
|
||||
/// <param name="http"></param>
|
||||
/// <param name="url"></param>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
|
||||
public sealed class WebApiAttribute() : Attribute
|
||||
|
||||
{
|
||||
public API Type ;
|
||||
public string Url ;
|
||||
@@ -71,7 +75,9 @@
|
||||
public bool IsUrl;
|
||||
}
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
|
||||
public sealed class ApiPostAttribute() : Attribute
|
||||
|
||||
{
|
||||
public string Url;
|
||||
/// <summary>
|
||||
@@ -80,7 +86,9 @@
|
||||
public bool IsUrl = true;
|
||||
}
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
|
||||
public sealed class ApiGetAttribute() : Attribute
|
||||
|
||||
{
|
||||
public string Url;
|
||||
/// <summary>
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
{
|
||||
public class ControllerBase
|
||||
{
|
||||
|
||||
public string Url { get; set; }
|
||||
|
||||
|
||||
public string BobyData { get; set; }
|
||||
|
||||
|
||||
public string GetLog(Exception ex)
|
||||
{
|
||||
return "Url : " + Url + Environment.NewLine +
|
||||
|
||||
@@ -89,6 +89,8 @@ namespace Serein.Web
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
||||
{
|
||||
Type = apiGetAttribute != null ? API.GET : API.POST,
|
||||
@@ -96,6 +98,8 @@ namespace Serein.Web
|
||||
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
||||
};
|
||||
|
||||
|
||||
|
||||
if (apiPostAttribute != null) // 如果存在 WebAPIAttribute 属性
|
||||
{
|
||||
var url = AddRoutesUrl(autoHostingAttribute,
|
||||
@@ -105,7 +109,9 @@ namespace Serein.Web
|
||||
if (url == null) continue;
|
||||
_controllerAutoHosting[url] = true;
|
||||
_controllerTypes[url] = controllerType;
|
||||
|
||||
_controllerInstances[url] = null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -137,17 +143,25 @@ namespace Serein.Web
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
||||
{
|
||||
Type = apiGetAttribute != null ? API.GET : API.POST,
|
||||
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
||||
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
||||
};
|
||||
|
||||
|
||||
|
||||
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
|
||||
|
||||
if (url == null) continue;
|
||||
_controllerAutoHosting[url] = true;
|
||||
_controllerTypes[url] = controllerType;
|
||||
|
||||
_controllerInstances[url] = null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,13 +183,19 @@ namespace Serein.Web
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
||||
{
|
||||
Type = apiGetAttribute != null ? API.GET : API.POST,
|
||||
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
||||
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
||||
};
|
||||
|
||||
|
||||
|
||||
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
|
||||
|
||||
if (url == null) continue;
|
||||
_controllerInstances[url] = controllerInstance;
|
||||
_controllerAutoHosting[url] = false;
|
||||
@@ -275,13 +295,19 @@ namespace Serein.Web
|
||||
var response = context.Response; // 获取响应对象
|
||||
var url = request.Url; // 获取请求的 URL
|
||||
var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法
|
||||
|
||||
var template = request.Url.AbsolutePath.ToLower();
|
||||
|
||||
|
||||
if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
|
||||
|
||||
ControllerBase controllerInstance;
|
||||
if (!_controllerAutoHosting[template])
|
||||
{
|
||||
@@ -289,8 +315,10 @@ namespace Serein.Web
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
controllerInstance = (ControllerBase)serviceRegistry.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (controllerInstance == null)
|
||||
@@ -313,10 +341,14 @@ namespace Serein.Web
|
||||
result = InvokeControllerMethod(method, controllerInstance, requestJObject, routeValues);
|
||||
break;
|
||||
default:
|
||||
|
||||
result = null;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Return(response, result); // 返回结果
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -343,10 +375,12 @@ namespace Serein.Web
|
||||
public object InvokeControllerMethod(MethodInfo method, object controllerInstance, dynamic requestData, Dictionary<string, string> routeValues)
|
||||
{
|
||||
object?[]? cachedMethodParameters;
|
||||
|
||||
if (!methodParameterCache.TryGetValue(method, out ParameterInfo[] parameters))
|
||||
{
|
||||
parameters = method.GetParameters();
|
||||
}
|
||||
|
||||
cachedMethodParameters = new object[parameters.Length];
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
@@ -407,7 +441,9 @@ namespace Serein.Web
|
||||
|
||||
|
||||
// 调用方法
|
||||
|
||||
return method.Invoke(controllerInstance, cachedMethodParameters);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -424,15 +460,21 @@ namespace Serein.Web
|
||||
|
||||
for (int i = 0; i < methodParameters.Length; i++)
|
||||
{
|
||||
|
||||
string paramName = methodParameters[i].Name;
|
||||
|
||||
|
||||
if (routeValues.TryGetValue(paramName, out string? value))
|
||||
{
|
||||
parameters[i] = ConvertValue(value, methodParameters[i].ParameterType);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
parameters[i] = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return parameters;
|
||||
@@ -479,9 +521,12 @@ namespace Serein.Web
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
return JsonConvert.DeserializeObject(value.ToString(), targetType);
|
||||
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
@@ -493,7 +538,9 @@ namespace Serein.Web
|
||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
||||
int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引
|
||||
var typeInfo = ex.Message[startIndex..endIndex]; // 提取出错类型信息,该怎么传出去?
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
catch // (Exception ex)
|
||||
{
|
||||
@@ -510,10 +557,14 @@ namespace Serein.Web
|
||||
/// <returns></returns>
|
||||
private static object InvokeMethod(MethodInfo method, object controllerInstance, object[] methodParameters)
|
||||
{
|
||||
|
||||
object result = null;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
result = method?.Invoke(controllerInstance, methodParameters);
|
||||
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
@@ -539,7 +590,9 @@ namespace Serein.Web
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
|
||||
return result; // 调用方法并返回结果
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -561,7 +614,9 @@ namespace Serein.Web
|
||||
foreach (string key in queryParams) // 遍历查询字符串的键值对
|
||||
{
|
||||
if (key == null) continue;
|
||||
|
||||
routeValues[key] = queryParams[key]; // 将键值对添加到路由参数字典中
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,7 +740,9 @@ namespace Serein.Web
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ namespace Serein.Web
|
||||
private readonly RequestLimiter requestLimiter; //接口防刷
|
||||
|
||||
|
||||
|
||||
public WebServer()
|
||||
|
||||
{
|
||||
listener = new HttpListener();
|
||||
|
||||
|
||||
@@ -175,6 +175,7 @@ namespace MyDll
|
||||
|
||||
var tcs = MyPlc.CreateTcs(triggerType);
|
||||
var result = await tcs.Task;
|
||||
|
||||
Interlocked.Increment(ref MyPlc.Count); // 原子自增
|
||||
Console.WriteLine($"信号触发[{triggerType}] : {MyPlc.Count}");
|
||||
return new FlipflopContext(FfState.Succeed, MyPlc.Count);
|
||||
|
||||
@@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.WorkBench", "WorkBen
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Module.WAT", "SereinWAT\Serein.Module.WAT.csproj", "{C2F68A15-5D07-4418-87C0-E7402DD9F83A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -27,6 +29,10 @@ Global
|
||||
{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
18
SereinWAT/Serein.Module.WAT.csproj
Normal file
18
SereinWAT/Serein.Module.WAT.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.23.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
152
SereinWAT/SereinWAT.cs
Normal file
152
SereinWAT/SereinWAT.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Chrome;
|
||||
using OpenQA.Selenium.Edge;
|
||||
using OpenQA.Selenium.Firefox;
|
||||
using OpenQA.Selenium.IE;
|
||||
using Serein.DynamicFlow;
|
||||
using Serein.DynamicFlow.Tool;
|
||||
using Serein.Web;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Serein.Module
|
||||
{
|
||||
public enum DriverType
|
||||
{
|
||||
Chrome,
|
||||
Edge,
|
||||
IE,
|
||||
Firefox,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网页自动化测试
|
||||
/// Web test automation
|
||||
/// </summary>
|
||||
[DynamicFlow]
|
||||
public class WebSelenium
|
||||
{
|
||||
|
||||
public WebDriver WebDriver { get; set; }
|
||||
|
||||
|
||||
public DriverType DriverType { get; set; }
|
||||
|
||||
//public ChromeDriver Driver { get; set; }
|
||||
#region Init and Exit
|
||||
[MethodDetail(DynamicNodeType.Init)]
|
||||
public void Init(DynamicContext context)
|
||||
{
|
||||
}
|
||||
|
||||
[MethodDetail(DynamicNodeType.Exit)]
|
||||
public void Exit(DynamicContext context)
|
||||
{
|
||||
WebDriver?.Quit();
|
||||
}
|
||||
#endregion
|
||||
|
||||
[MethodDetail(DynamicNodeType.Action,"启动浏览器")]
|
||||
public WebDriver OpenDriver([Explicit] bool isVisible = true,
|
||||
[Explicit] DriverType driverType = DriverType.Chrome)
|
||||
{
|
||||
|
||||
|
||||
if(driverType == DriverType.Chrome)
|
||||
{
|
||||
ChromeOptions options = new ChromeOptions();
|
||||
if (!isVisible)
|
||||
{
|
||||
options.AddArgument("headless"); // 添加无头模式参数
|
||||
options.AddArgument("disable-gpu"); // 需要禁用 GPU
|
||||
options.LeaveBrowserRunning = true; // 设置浏览器不自动关闭
|
||||
}
|
||||
WebDriver = new ChromeDriver(options);
|
||||
return WebDriver;
|
||||
}
|
||||
else if (driverType == DriverType.Edge)
|
||||
{
|
||||
EdgeOptions options = new EdgeOptions();
|
||||
if (!isVisible)
|
||||
{
|
||||
options.AddArgument("headless"); // 添加无头模式参数
|
||||
options.AddArgument("disable-gpu"); // 需要禁用 GPU
|
||||
options.LeaveBrowserRunning = true; // 设置浏览器不自动关闭
|
||||
}
|
||||
WebDriver = new EdgeDriver(options);
|
||||
return WebDriver;
|
||||
}
|
||||
else if (driverType == DriverType.IE)
|
||||
{
|
||||
InternetExplorerOptions options = new InternetExplorerOptions();
|
||||
// IE浏览器不支持无头模式,因此这里不添加无头模式参数
|
||||
WebDriver = new InternetExplorerDriver(options);
|
||||
return WebDriver;
|
||||
}
|
||||
else if (driverType == DriverType.Firefox)
|
||||
{
|
||||
FirefoxOptions options = new FirefoxOptions();
|
||||
if (!isVisible)
|
||||
{
|
||||
options.AddArgument("-headless"); // 添加无头模式参数
|
||||
// Firefox没有直接的LeaveBrowserRunning选项,但可以使用调试器暂停关闭
|
||||
}
|
||||
|
||||
// FirefoxDriver 没有直接的 LeaveBrowserRunning 选项
|
||||
WebDriver = new FirefoxDriver(options);
|
||||
return WebDriver;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[MethodDetail(DynamicNodeType.Action,"等待")]
|
||||
public void Wait([Explicit]int time = 1000)
|
||||
{
|
||||
Thread.Sleep(time);
|
||||
}
|
||||
|
||||
[MethodDetail(DynamicNodeType.Action,"进入网页")]
|
||||
public void ToPage([Explicit] string url)
|
||||
{
|
||||
if (url.StartsWith("https://") || url.StartsWith("http://"))
|
||||
{
|
||||
WebDriver.Navigate().GoToUrl($"{url}");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("请输入完整的Url。Please enter the full Url.");
|
||||
}
|
||||
}
|
||||
|
||||
[MethodDetail(DynamicNodeType.Action,"XPath定位元素")]
|
||||
public IWebElement XPathFind(string xpath,
|
||||
[Explicit] int index = 0)
|
||||
{
|
||||
var element = WebDriver.FindElements(By.XPath(xpath))[0];
|
||||
return element;
|
||||
}
|
||||
|
||||
[MethodDetail(DynamicNodeType.Action,"Js添加元素内部属性")]
|
||||
public void AddAttribute(IWebElement element, [Explicit] string attributeName = "", [Explicit] string value = "")
|
||||
{
|
||||
//IJavaScriptExecutor js = (IJavaScriptExecutor)Driver;
|
||||
WebDriver.ExecuteScript($"arguments[0].{attributeName} = arguments[1];", element, value);
|
||||
}
|
||||
|
||||
[MethodDetail(DynamicNodeType.Action, "Js设置元素内部属性")]
|
||||
public void SetAttribute(IWebElement element, [Explicit] string attributeName = "", [Explicit] string value = "")
|
||||
{
|
||||
//IJavaScriptExecutor js = (IJavaScriptExecutor)Driver;
|
||||
WebDriver.ExecuteScript("arguments[0].setAttribute(arguments[1], arguments[2]);", element, attributeName, value);
|
||||
}
|
||||
|
||||
[MethodDetail(DynamicNodeType.Action, "Js获取元素内部属性")]
|
||||
public string GetAttribute(IWebElement element, [Explicit] string attributeName = "")
|
||||
{
|
||||
return element.GetAttribute(attributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,16 @@ namespace Serein.WorkBench
|
||||
{
|
||||
public class TestObject
|
||||
{
|
||||
|
||||
public NestedObject Data { get; set; }
|
||||
|
||||
public class NestedObject
|
||||
{
|
||||
public int Code { get; set; }
|
||||
public int Code2 { get; set; }
|
||||
|
||||
public string Tips { get; set; }
|
||||
|
||||
}
|
||||
public string ToUpper(string input)
|
||||
{
|
||||
|
||||
@@ -202,7 +202,9 @@ namespace Serein.WorkBench
|
||||
|
||||
|
||||
|
||||
|
||||
public MainWindow()
|
||||
|
||||
{
|
||||
InitializeComponent();
|
||||
logWindow = new LogWindow();
|
||||
@@ -231,8 +233,8 @@ namespace Serein.WorkBench
|
||||
if (nf != null)
|
||||
{
|
||||
InitializeCanvas(nf.basic.canvas.width, nf.basic.canvas.lenght);
|
||||
LoadDll(nf);
|
||||
LoadNodes(nf);
|
||||
LoadDll(nf); // 加载DLL
|
||||
LoadNodeControls(nf); // 加载节点
|
||||
|
||||
var startNode = nodeControls.FirstOrDefault(item => item.Node.Guid.Equals(nf.startNode));
|
||||
if (startNode != null)
|
||||
@@ -242,7 +244,7 @@ namespace Serein.WorkBench
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置画布宽度高度
|
||||
private void InitializeCanvas(double width, double height)
|
||||
{
|
||||
FlowChartCanvas.Width = width;
|
||||
@@ -267,7 +269,7 @@ namespace Serein.WorkBench
|
||||
/// 加载配置文件时加载节点/区域
|
||||
/// </summary>
|
||||
/// <param name="nf"></param>
|
||||
private void LoadNodes(SereinOutputFileData nf)
|
||||
private void LoadNodeControls(SereinOutputFileData nf)
|
||||
{
|
||||
var nodeControls = new Dictionary<string, NodeControlBase>();
|
||||
var regionControls = new Dictionary<string, NodeControlBase>();
|
||||
@@ -413,25 +415,28 @@ namespace Serein.WorkBench
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置文件时配置节点
|
||||
/// 配置节点(加载配置文件时)
|
||||
/// </summary>
|
||||
/// <param name="nodeConfig">节点配置数据</param>
|
||||
/// <param name="nodeInfo">节点配置数据</param>
|
||||
/// <param name="nodeControl">需要配置的节点</param>
|
||||
/// <param name="nodeControls">节点列表</param>
|
||||
/// <param name="regionControls">区域列表</param>
|
||||
private void ConfigureNodeControl(NodeInfo nodeConfig, NodeControlBase nodeControl, Dictionary<string, NodeControlBase> nodeControls, Dictionary<string, NodeControlBase> regionControls)
|
||||
private void ConfigureNodeControl(NodeInfo nodeInfo,
|
||||
NodeControlBase nodeControl,
|
||||
Dictionary<string, NodeControlBase> nodeControls,
|
||||
Dictionary<string, NodeControlBase> regionControls)
|
||||
{
|
||||
FlowChartCanvas.Dispatcher.Invoke(() =>
|
||||
{
|
||||
FlowChartCanvas.Children.Add(nodeControl);
|
||||
Canvas.SetLeft(nodeControl, nodeConfig.position.x);
|
||||
Canvas.SetTop(nodeControl, nodeConfig.position.y);
|
||||
nodeControls[nodeConfig.guid] = nodeControl;
|
||||
Canvas.SetLeft(nodeControl, nodeInfo.position.x);
|
||||
Canvas.SetTop(nodeControl, nodeInfo.position.y);
|
||||
nodeControls[nodeInfo.guid] = nodeControl;
|
||||
this.nodeControls.Add(nodeControl);
|
||||
|
||||
if (nodeControl is ActionRegionControl || nodeControl is ConditionRegionControl)//如果是区域,则需要创建区域
|
||||
{
|
||||
regionControls[nodeConfig.guid] = nodeControl;
|
||||
regionControls[nodeInfo.guid] = nodeControl;
|
||||
}
|
||||
|
||||
ConfigureContextMenu(nodeControl); // 创建区域
|
||||
@@ -440,7 +445,7 @@ namespace Serein.WorkBench
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置文件时创建控件
|
||||
/// 创建控件并配置节点数据(加载配置文件时)
|
||||
/// </summary>
|
||||
/// <param name="nodeInfo"></param>
|
||||
/// <returns></returns>
|
||||
@@ -449,9 +454,10 @@ namespace Serein.WorkBench
|
||||
if (!DllMethodDetails.TryGetValue(nodeInfo.name, out var md))
|
||||
{
|
||||
WriteLog($"目标节点不存在方法信息: {nodeInfo.name}\r\n");
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
NodeControlBase control = nodeInfo.type switch
|
||||
{
|
||||
$"{NodeSpaceName}.{nameof(SingleActionNode)}" => CreateNodeControl<SingleActionNode, ActionNodeControl>(md),
|
||||
@@ -471,11 +477,20 @@ namespace Serein.WorkBench
|
||||
flipflopNodes.Add(flipflopNode);
|
||||
}
|
||||
}
|
||||
return control;// DNF文件加载时创建
|
||||
|
||||
var node = control.Node;
|
||||
if (node != null)
|
||||
{
|
||||
for (int i = 0; i < nodeInfo.parameterData.Length; i++)
|
||||
{
|
||||
Parameterdata? pd = nodeInfo.parameterData[i];
|
||||
node.MethodDetails.ExplicitDatas[i].IsExplicitData = pd.state;
|
||||
node.MethodDetails.ExplicitDatas[i].DataValue = pd.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return control;// DNF文件加载时创建
|
||||
/* NodeControl? nodeControl = nodeInfo.type switch
|
||||
{
|
||||
$"{NodeSpaceName}.{nameof(SingleActionNode)}" => CreateActionNodeControl(md),
|
||||
@@ -490,7 +505,7 @@ namespace Serein.WorkBench
|
||||
|
||||
#region 节点控件的创建
|
||||
|
||||
private static TControl CreateNodeControl<TNode, TControl>(MethodDetails? md = null)
|
||||
private static TControl CreateNodeControl<TNode, TControl>(MethodDetails? methodDetails = null)
|
||||
where TNode : NodeBase
|
||||
where TControl : NodeControlBase
|
||||
{
|
||||
@@ -502,10 +517,12 @@ namespace Serein.WorkBench
|
||||
}
|
||||
|
||||
nodeBase.Guid = Guid.NewGuid().ToString();
|
||||
if (md != null)
|
||||
|
||||
if (methodDetails != null)
|
||||
{
|
||||
var md = methodDetails.Clone();
|
||||
nodeBase.DelegateName = md.MethodName;
|
||||
nodeBase.DisplayName = md.MethodName;
|
||||
nodeBase.DisplayName = md.MethodTips;
|
||||
nodeBase.MethodDetails = md;
|
||||
}
|
||||
|
||||
@@ -618,7 +635,7 @@ namespace Serein.WorkBench
|
||||
/// </summary>
|
||||
// private static ConcurrentDictionary<string, Delegate> globalDicDelegates = new ConcurrentDictionary<string, Delegate>();
|
||||
|
||||
private static readonly ConcurrentDictionary<string, MethodDetails> DllMethodDetails = [];
|
||||
private static ConcurrentDictionary<string, MethodDetails> DllMethodDetails { get; } = [];
|
||||
/// <summary>
|
||||
/// 加载指定路径的DLL文件
|
||||
/// </summary>
|
||||
@@ -767,18 +784,14 @@ namespace Serein.WorkBench
|
||||
Header = "DLL name : " + assembly.GetName().Name // 设置控件标题为程序集名称
|
||||
};
|
||||
|
||||
foreach (var item in conditionTypes)
|
||||
{
|
||||
dllControl.AddCondition(item); // 添加动作类型到控件
|
||||
}
|
||||
|
||||
foreach (var item in actionTypes)
|
||||
{
|
||||
dllControl.AddAction(item); // 添加状态类型到控件
|
||||
dllControl.AddAction(item.Clone()); // 添加动作类型到控件
|
||||
}
|
||||
foreach (var item in flipflopMethods)
|
||||
{
|
||||
dllControl.AddFlipflop(item); // 添加触发器方法到控件
|
||||
dllControl.AddFlipflop(item.Clone()); // 添加触发器方法到控件
|
||||
}
|
||||
|
||||
/*foreach (var item in stateTypes)
|
||||
@@ -996,6 +1009,7 @@ namespace Serein.WorkBench
|
||||
{
|
||||
var data = e.Data.GetData(MouseNodeType.BaseNodeType);
|
||||
|
||||
|
||||
if (data == typeof(ConditionNodeControl))
|
||||
{
|
||||
|
||||
@@ -1010,6 +1024,7 @@ namespace Serein.WorkBench
|
||||
|
||||
}
|
||||
|
||||
|
||||
//if (e.Data.GetData(MouseNodeType.DllNodeType) is MethodDetails methodDetails)
|
||||
//{
|
||||
// if (methodDetails.MethodDynamicType == DynamicNodeType.Condition)
|
||||
@@ -1070,11 +1085,15 @@ namespace Serein.WorkBench
|
||||
{
|
||||
if (element is T)
|
||||
{
|
||||
|
||||
return element as T;
|
||||
|
||||
}
|
||||
element = VisualTreeHelper.GetParent(element);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1787,6 +1806,27 @@ namespace Serein.WorkBench
|
||||
var trueNodes = item.Node.TrueBranch.Select(item => item.Guid).ToArray();
|
||||
var falseNodes = item.Node.FalseBranch.Select(item => item.Guid).ToArray();
|
||||
|
||||
IEnumerable<object?>? parameterData = [];
|
||||
if (node?.MethodDetails?.ExplicitDatas is not null)
|
||||
{
|
||||
parameterData = node?.MethodDetails.ExplicitDatas.Select(it => {
|
||||
|
||||
if (it != null)
|
||||
{
|
||||
return new
|
||||
{
|
||||
state = it.IsExplicitData,
|
||||
value = it.DataValue,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return new
|
||||
{
|
||||
guid = node.Guid,
|
||||
@@ -1800,7 +1840,9 @@ namespace Serein.WorkBench
|
||||
},
|
||||
trueNodes = trueNodes,
|
||||
falseNodes = falseNodes,
|
||||
parameterData = parameterData,
|
||||
};
|
||||
|
||||
}).ToList();
|
||||
|
||||
|
||||
@@ -1881,8 +1923,12 @@ namespace Serein.WorkBench
|
||||
var dlls = loadedAssemblies.Select(assembly =>
|
||||
{
|
||||
var temp = assembly.GetName();
|
||||
|
||||
string codeBasePath = assembly.CodeBase;
|
||||
|
||||
|
||||
string filePath = new Uri(codeBasePath).LocalPath;
|
||||
|
||||
string relativePath;
|
||||
if (string.IsNullOrEmpty(App.FileDataPath))
|
||||
{
|
||||
@@ -1932,11 +1978,15 @@ namespace Serein.WorkBench
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
string targetPath = System.IO.Path.Combine(savePath, System.IO.Path.GetFileName(dll.CodeBase));
|
||||
|
||||
|
||||
// 确保目标目录存在
|
||||
Directory.CreateDirectory(savePath);
|
||||
|
||||
var sourceFile = new Uri(dll.CodeBase).LocalPath;
|
||||
|
||||
// 复制文件到目标目录
|
||||
File.Copy(sourceFile, targetPath, true);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
>
|
||||
<Grid>
|
||||
<Grid.ToolTip>
|
||||
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid.ToolTip>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
@@ -20,7 +20,7 @@
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Background="#FFCFDF" BorderBrush="#FFCFDF" BorderThickness="1">
|
||||
<TextBlock Text="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
|
||||
<Grid Grid.Row="2" >
|
||||
|
||||
@@ -15,7 +15,9 @@ namespace Serein.WorkBench.Node.View
|
||||
private Point _dragStartPoint;
|
||||
|
||||
private new readonly CompositeActionNode Node;
|
||||
|
||||
public ActionRegionControl() : base()
|
||||
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -41,16 +41,6 @@ namespace Serein.WorkBench.Node.View
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 向条件面板添加类型的文本块
|
||||
/// </summary>
|
||||
/// <param name="type">要添加的类型</param>
|
||||
public void AddCondition(MethodDetails md)
|
||||
{
|
||||
AddTypeToListBox(md, ConditionsListBox);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向动作面板添加类型的文本块
|
||||
/// </summary>
|
||||
@@ -61,7 +51,7 @@ namespace Serein.WorkBench.Node.View
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向状态面板添加类型的文本块
|
||||
/// 向触发器面板添加类型的文本块
|
||||
/// </summary>
|
||||
/// <param name="type">要添加的类型</param>
|
||||
public void AddFlipflop(MethodDetails md)
|
||||
@@ -79,7 +69,7 @@ namespace Serein.WorkBench.Node.View
|
||||
// 创建一个新的 TextBlock 并设置其属性
|
||||
TextBlock typeText = new TextBlock
|
||||
{
|
||||
Text = $"{md.MethodName}",
|
||||
Text = $"{md.MethodTips}",
|
||||
Margin = new Thickness(10, 2, 0, 0),
|
||||
Tag = md
|
||||
};
|
||||
@@ -125,7 +115,9 @@ namespace Serein.WorkBench.Node.View
|
||||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
||||
{
|
||||
// 获取触发事件的 TextBlock
|
||||
|
||||
TextBlock typeText = sender as TextBlock;
|
||||
|
||||
if (typeText != null)
|
||||
{
|
||||
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<Grid>
|
||||
<Grid.ToolTip>
|
||||
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid.ToolTip>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
@@ -25,7 +25,7 @@
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Background="#FFFFD2" BorderBrush="Black" BorderThickness="1">
|
||||
<TextBlock Text="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<!--<themes:ExplicitDataControl Grid.Row="1" ExplicitDatas="{Binding ExplicitDatas}" />-->
|
||||
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
|
||||
|
||||
@@ -17,12 +17,15 @@ namespace Serein.WorkBench.Node.View
|
||||
public abstract class NodeControlBase : UserControl, IDynamicFlowNode
|
||||
{
|
||||
public NodeBase Node { get; set; }
|
||||
|
||||
protected NodeControlBase()
|
||||
|
||||
{
|
||||
this.Background = Brushes.Transparent;
|
||||
}
|
||||
protected NodeControlBase(NodeBase node)
|
||||
{
|
||||
this.Background = Brushes.Transparent;
|
||||
Node = node;
|
||||
}
|
||||
}
|
||||
@@ -32,8 +35,10 @@ namespace Serein.WorkBench.Node.View
|
||||
public abstract class NodeControlViewModel : INotifyPropertyChanged
|
||||
{
|
||||
|
||||
|
||||
public MethodDetails methodDetails;
|
||||
|
||||
|
||||
public MethodDetails MethodDetails
|
||||
{
|
||||
get => methodDetails;
|
||||
@@ -44,9 +49,15 @@ namespace Serein.WorkBench.Node.View
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
|
||||
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ namespace Serein.WorkBench.Node.ViewModel
|
||||
public ActionNodeControlViewModel(SingleActionNode node)
|
||||
{
|
||||
this.node = node;
|
||||
|
||||
|
||||
MethodDetails = node.MethodDetails;
|
||||
//if (node.MethodDetails.ExplicitDatas.Length == 0)
|
||||
//{
|
||||
|
||||
@@ -12,23 +12,33 @@ namespace Serein.WorkBench
|
||||
/// <summary>
|
||||
/// 基础
|
||||
/// </summary>
|
||||
|
||||
public Basic basic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 依赖的DLL
|
||||
/// </summary>
|
||||
|
||||
public Library[] library { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始节点GUID
|
||||
/// </summary>
|
||||
|
||||
public string startNode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点信息集合
|
||||
/// </summary>
|
||||
|
||||
public NodeInfo[] nodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 区域集合
|
||||
/// </summary>
|
||||
|
||||
public Region[] regions { get; set; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,11 +49,15 @@ namespace Serein.WorkBench
|
||||
/// <summary>
|
||||
/// 画布
|
||||
/// </summary>
|
||||
|
||||
public FlowCanvas canvas { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 版本
|
||||
/// </summary>
|
||||
|
||||
public string versions { get; set; }
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 画布
|
||||
@@ -68,15 +82,21 @@ namespace Serein.WorkBench
|
||||
/// <summary>
|
||||
/// DLL名称
|
||||
/// </summary>
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 路径
|
||||
/// </summary>
|
||||
|
||||
public string path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提示
|
||||
/// </summary>
|
||||
|
||||
public string tips { get; set; }
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 节点
|
||||
@@ -86,32 +106,60 @@ namespace Serein.WorkBench
|
||||
/// <summary>
|
||||
/// GUID
|
||||
/// </summary>
|
||||
|
||||
public string guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
|
||||
public string name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 显示标签
|
||||
/// </summary>
|
||||
|
||||
public string label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// </summary>
|
||||
|
||||
public string type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 于画布中的位置
|
||||
/// </summary>
|
||||
|
||||
public Position position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 真分支节点GUID
|
||||
/// </summary>
|
||||
|
||||
public string[] trueNodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 假分支节点
|
||||
/// </summary>
|
||||
|
||||
public string[] falseNodes { get; set; }
|
||||
|
||||
|
||||
|
||||
public Parameterdata[] parameterData { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class Parameterdata
|
||||
{
|
||||
public bool state { get; set; }
|
||||
|
||||
public string value { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 节点于画布中的位置
|
||||
/// </summary>
|
||||
@@ -127,7 +175,11 @@ namespace Serein.WorkBench
|
||||
/// </summary>
|
||||
public class Region
|
||||
{
|
||||
|
||||
public string guid { get; set; }
|
||||
|
||||
|
||||
public NodeInfo[] childNodes { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ namespace Serein.WorkBench.Themes
|
||||
/// </summary>
|
||||
public partial class TypeViewerWindow : Window
|
||||
{
|
||||
|
||||
public TypeViewerWindow()
|
||||
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user