更改了拖入的DLL显示名称

尝试添加了web自动化测试(基于Selenium)
This commit is contained in:
fengjiayi
2024-08-05 19:43:57 +08:00
parent 989a2c0800
commit 5b15871f65
37 changed files with 700 additions and 80 deletions

View File

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

View File

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

View File

@@ -39,7 +39,9 @@ namespace Serein.DynamicFlow
/// <summary>
/// 动态流程上下文
/// </summary>
public class DynamicContext(IServiceContainer serviceContainer)
{
private readonly string contextGuid = "";//System.Guid.NewGuid().ToString();

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
using Serein.DynamicFlow;
using System.Collections.Generic;
using System.Diagnostics;
namespace Serein.DynamicFlow.NodeModel

View File

@@ -1,4 +1,6 @@
using Serein.DynamicFlow.Tool;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Serein.DynamicFlow.NodeModel

View File

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

View File

@@ -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自定义参数内容

View File

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

View File

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

View File

@@ -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("对象表达式无效的类型声明");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,9 @@ namespace Serein.Web
private readonly RequestLimiter requestLimiter; //接口防刷
public WebServer()
{
listener = new HttpListener();

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,9 @@ namespace Serein.WorkBench.Node.View
private Point _dragStartPoint;
private new readonly CompositeActionNode Node;
public ActionRegionControl() : base()
{
InitializeComponent();
}

View File

@@ -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 用于拖拽操作,并设置拖拽效果

View File

@@ -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}" />

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,9 @@ namespace Serein.WorkBench.Themes
/// </summary>
public partial class TypeViewerWindow : Window
{
public TypeViewerWindow()
{
InitializeComponent();
}