mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-16 04:46:34 +08:00
先想到这吧
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -20,3 +20,7 @@ obj/
|
|||||||
# 排除发布文件夹
|
# 排除发布文件夹
|
||||||
.Output/
|
.Output/
|
||||||
/.git1
|
/.git1
|
||||||
|
|
||||||
|
|
||||||
|
# 排除某些项目
|
||||||
|
Net461DllTest/
|
||||||
18
Library/Entity/CallChainInfo.cs
Normal file
18
Library/Entity/CallChainInfo.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Serein.Library.Entity
|
||||||
|
{
|
||||||
|
// 每次发生调用的时候,将当前节点调用信息拷贝一份,
|
||||||
|
// 调用完成后释放?
|
||||||
|
// 参数信息
|
||||||
|
public class CallChainInfo
|
||||||
|
{
|
||||||
|
public List<string> CallGuid { get; }
|
||||||
|
public List<object[]> InvokeData { get; }
|
||||||
|
public List<object> ResultData { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,6 +19,10 @@ namespace Serein.Library.Entity
|
|||||||
/// 是否为显式参数(固定值/表达式)
|
/// 是否为显式参数(固定值/表达式)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsExplicitData { get; set; }
|
public bool IsExplicitData { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为值转换器
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEnumConvertor { get; set; }
|
||||||
///// <summary>
|
///// <summary>
|
||||||
///// 显式类型
|
///// 显式类型
|
||||||
///// </summary>
|
///// </summary>
|
||||||
@@ -45,7 +49,7 @@ namespace Serein.Library.Entity
|
|||||||
|
|
||||||
public string DataValue { get; set; }
|
public string DataValue { get; set; }
|
||||||
|
|
||||||
public string[] Items { get; set; }
|
public object[] Items { get; set; }
|
||||||
|
|
||||||
public ExplicitData Clone() => new ExplicitData()
|
public ExplicitData Clone() => new ExplicitData()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace Serein.Library.Entity
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否保护参数
|
/// 是否保护参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsProtectionParameter { get; set; }
|
public bool IsProtectionParameter { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 作用实例的类型
|
/// 作用实例的类型
|
||||||
|
|||||||
@@ -49,4 +49,99 @@ namespace Serein.Library.Attributes
|
|||||||
public string LockName;
|
public string LockName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
//public class BindTypeAttribute : Attribute
|
||||||
|
//{
|
||||||
|
// public Type Type { get; }
|
||||||
|
|
||||||
|
// public BindTypeAttribute(Type type)
|
||||||
|
// {
|
||||||
|
// Type = type;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
public class BindValueAttribute : Attribute
|
||||||
|
{
|
||||||
|
public object Value { get; }
|
||||||
|
|
||||||
|
public BindValueAttribute(object value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
public class PLCValueAttribute : Attribute
|
||||||
|
{
|
||||||
|
public enum VarType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 可写入值
|
||||||
|
/// </summary>
|
||||||
|
Write,
|
||||||
|
/// <summary>
|
||||||
|
/// 定时读取的可写入值(用来写入前判断),应该几乎不会有这种类型?
|
||||||
|
/// </summary>
|
||||||
|
TimingReadOrWrite,
|
||||||
|
/// <summary>
|
||||||
|
/// 只读取值(使用时刷新)
|
||||||
|
/// </summary>
|
||||||
|
ReadOnly,
|
||||||
|
/// <summary>
|
||||||
|
/// 定时刷新的只读取值(定时刷新用来触发触发器)
|
||||||
|
/// </summary>
|
||||||
|
TimingReadOnly,
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsProtected { get; }
|
||||||
|
public Type DataType { get; }
|
||||||
|
public string Var { get; }
|
||||||
|
//public int Length { get; }
|
||||||
|
//public double Offset { get; }
|
||||||
|
public VarType Type { get; }
|
||||||
|
//public int RefreshInterval { get; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public PLCValueAttribute(Type type,
|
||||||
|
string @var,
|
||||||
|
VarType varType
|
||||||
|
//int refreshInterval = 100
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DataType = type;
|
||||||
|
Var = @var;
|
||||||
|
//Offset = offset;
|
||||||
|
//RefreshInterval = refreshInterval;
|
||||||
|
Type = varType;
|
||||||
|
//Length = length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 枚举值转换器
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
//[AttributeUsage(AttributeTargets.Parameter)]
|
||||||
|
//public class EnumConvertorAttribute : Attribute
|
||||||
|
//{
|
||||||
|
// public Type Enum { get; }
|
||||||
|
|
||||||
|
// public EnumConvertorAttribute(Type @enum)
|
||||||
|
// {
|
||||||
|
// if (@enum.IsEnum)
|
||||||
|
// {
|
||||||
|
// Enum = @enum;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// throw new ArgumentException("需要枚举类型");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,39 +46,38 @@ namespace Serein.Library.NodeFlow.Tool
|
|||||||
public async Task<TriggerData> CreateChannelWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime, TResult outValue)
|
public async Task<TriggerData> CreateChannelWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime, TResult outValue)
|
||||||
{
|
{
|
||||||
var channel = GetOrCreateChannel(signal);
|
var channel = GetOrCreateChannel(signal);
|
||||||
//var cts = new CancellationTokenSource();
|
var cts = new CancellationTokenSource();
|
||||||
//// 异步任务:超时后自动触发信号
|
// 异步任务:超时后自动触发信号
|
||||||
//_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
//{
|
{
|
||||||
// try
|
try
|
||||||
// {
|
{
|
||||||
// await Task.Delay(outTime, cts.Token);
|
await Task.Delay(outTime, cts.Token);
|
||||||
// if(!cts.IsCancellationRequested) // 如果还没有被取消
|
if(!cts.IsCancellationRequested) // 如果还没有被取消
|
||||||
// {
|
{
|
||||||
// TriggerData triggerData = new TriggerData()
|
TriggerData triggerData = new TriggerData()
|
||||||
// {
|
{
|
||||||
// Value = outValue,
|
Value = outValue,
|
||||||
// Type = TriggerType.Overtime,
|
Type = TriggerType.Overtime,
|
||||||
// };
|
};
|
||||||
// await channel.Writer.WriteAsync(triggerData);
|
await channel.Writer.WriteAsync(triggerData);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
// {
|
{
|
||||||
// // 超时任务被取消
|
// 超时任务被取消
|
||||||
// }
|
}
|
||||||
// finally
|
finally
|
||||||
// {
|
{
|
||||||
// cts?.Cancel();
|
cts?.Cancel();
|
||||||
// cts?.Dispose(); // 确保 cts 被释放
|
cts?.Dispose(); // 确保 cts 被释放
|
||||||
// }
|
}
|
||||||
//}, cts.Token);
|
}, cts.Token);
|
||||||
|
|
||||||
|
|
||||||
// 等待信号传入(超时或手动触发)
|
// 等待信号传入(超时或手动触发)
|
||||||
var result = await channel.Reader.ReadAsync();
|
var result = await channel.Reader.ReadAsync();
|
||||||
//cts?.Cancel();
|
cts?.Cancel();
|
||||||
//cts?.Dispose();
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
35
Library/Utils/EnumHelper.cs
Normal file
35
Library/Utils/EnumHelper.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Serein.Library.Attributes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Serein.Library.Utils
|
||||||
|
{
|
||||||
|
public static class EnumHelper
|
||||||
|
{
|
||||||
|
public static TResult GetBoundValue<TEnum, TResult>(TEnum enumValue, Func<BindValueAttribute, object> valueSelector)
|
||||||
|
where TEnum : Enum
|
||||||
|
{
|
||||||
|
var fieldInfo = typeof(TEnum).GetField(enumValue.ToString());
|
||||||
|
var attribute = fieldInfo.GetCustomAttribute<BindValueAttribute>();
|
||||||
|
|
||||||
|
return attribute != null ? (TResult)valueSelector(attribute) : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
//public static TResult GetBoundValue<TEnum, TAttribute, TResult>(TEnum enumValue,
|
||||||
|
// Func<TAttribute, TResult> valueSelector)
|
||||||
|
// where TEnum : Enum
|
||||||
|
// where TAttribute : Attribute
|
||||||
|
//{
|
||||||
|
// var fieldInfo = typeof(TEnum).GetField(enumValue.ToString());
|
||||||
|
// var attribute = fieldInfo.GetCustomAttribute<TAttribute>();
|
||||||
|
|
||||||
|
// return attribute != null ? valueSelector(attribute) : default;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ namespace Serein.Library.Web
|
|||||||
public interface IRouter
|
public interface IRouter
|
||||||
{
|
{
|
||||||
bool RegisterController(Type controllerType);
|
bool RegisterController(Type controllerType);
|
||||||
Task ProcessingAsync(HttpListenerContext context);
|
Task<bool> ProcessingAsync(HttpListenerContext context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -75,7 +75,7 @@ namespace Serein.Library.Web
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task ProcessingAsync(HttpListenerContext context)
|
public async Task<bool> ProcessingAsync(HttpListenerContext context)
|
||||||
{
|
{
|
||||||
var request = context.Request; // 获取请求对象
|
var request = context.Request; // 获取请求对象
|
||||||
var response = context.Response; // 获取响应对象
|
var response = context.Response; // 获取响应对象
|
||||||
@@ -84,16 +84,16 @@ namespace Serein.Library.Web
|
|||||||
var template = request.Url.AbsolutePath.ToLower();
|
var template = request.Url.AbsolutePath.ToLower();
|
||||||
if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
|
if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
|
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
|
||||||
|
|
||||||
ControllerBase controllerInstance = (ControllerBase)SereinIOC.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
|
ControllerBase controllerInstance = (ControllerBase)SereinIOC.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
|
||||||
|
|
||||||
if (controllerInstance == null)
|
if (controllerInstance is null)
|
||||||
{
|
{
|
||||||
return; // 未找到控制器实例
|
return false; // 未找到控制器实例
|
||||||
}
|
}
|
||||||
|
|
||||||
controllerInstance.Url = url.AbsolutePath;
|
controllerInstance.Url = url.AbsolutePath;
|
||||||
@@ -119,11 +119,13 @@ namespace Serein.Library.Web
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Return(response, result); // 返回结果
|
Return(response, result); // 返回结果
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
||||||
Return(response, ex.Message); // 返回结果
|
Return(response, ex.Message); // 返回结果
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -146,7 +148,7 @@ namespace Serein.Library.Web
|
|||||||
{
|
{
|
||||||
var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
|
var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
|
||||||
Console.WriteLine(url);
|
Console.WriteLine(url);
|
||||||
if (url == null) continue;
|
if (url is null) continue;
|
||||||
_controllerTypes[url] = controllerType;
|
_controllerTypes[url] = controllerType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,7 +213,7 @@ namespace Serein.Library.Web
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public object InvokeControllerMethod(MethodInfo method, object controllerInstance, JObject requestData, Dictionary<string, string> routeValues)
|
public object InvokeControllerMethod(MethodInfo method, object controllerInstance, JObject requestData, Dictionary<string, string> routeValues)
|
||||||
{
|
{
|
||||||
if (requestData == null) return null;
|
if (requestData is null) return null;
|
||||||
ParameterInfo[] parameters;
|
ParameterInfo[] parameters;
|
||||||
object[] cachedMethodParameters;
|
object[] cachedMethodParameters;
|
||||||
if (!methodParameterCache.TryGetValue(method, out parameters))
|
if (!methodParameterCache.TryGetValue(method, out parameters))
|
||||||
@@ -355,7 +357,6 @@ namespace Serein.Library.Web
|
|||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
#pragma warning restore CS0168 // 声明了变量,但从未使用过
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -554,7 +555,7 @@ namespace Serein.Library.Web
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,7 +563,7 @@ namespace Serein.Library.Web
|
|||||||
|
|
||||||
internal static class WebFunc
|
internal static class WebFunc
|
||||||
{
|
{
|
||||||
public static bool ToBool(this JToken token,bool defult = false)
|
public static bool ToBool(this JToken token, bool defult = false)
|
||||||
{
|
{
|
||||||
var value = token?.ToString();
|
var value = token?.ToString();
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
|||||||
@@ -100,18 +100,22 @@ namespace Serein.Library.Web
|
|||||||
}
|
}
|
||||||
|
|
||||||
var isPass = requestLimiter.AllowRequest(context.Request);
|
var isPass = requestLimiter.AllowRequest(context.Request);
|
||||||
|
if (!isPass)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
||||||
|
}
|
||||||
|
isPass = await Router.ProcessingAsync(context); // 路由解析
|
||||||
if (isPass)
|
if (isPass)
|
||||||
{
|
{
|
||||||
// 如果路由没有匹配,会返回 404
|
context.Response.StatusCode = (int)HttpStatusCode.OK; // 返回 404 错误
|
||||||
await Router.ProcessingAsync(context); // 路由解析
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
||||||
context.Response.Close(); // 关闭响应
|
|
||||||
}
|
}
|
||||||
|
context.Response.Close(); // 关闭响应
|
||||||
// var request = context.Request;
|
// var request = context.Request;
|
||||||
// 获取远程终结点信息
|
// 获取远程终结点信息
|
||||||
var remoteEndPoint = context.Request.RemoteEndPoint;
|
var remoteEndPoint = context.Request.RemoteEndPoint;
|
||||||
// 获取用户的IP地址和端口
|
// 获取用户的IP地址和端口
|
||||||
|
|||||||
@@ -99,17 +99,22 @@ namespace Serein.NodeFlow.Base
|
|||||||
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
||||||
stack.Push(this);
|
stack.Push(this);
|
||||||
var cts = context.Env.IOC.Get<CancellationTokenSource>(FlowStarter.FlipFlopCtsName);
|
var cts = context.Env.IOC.Get<CancellationTokenSource>(FlowStarter.FlipFlopCtsName);
|
||||||
while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环
|
while (stack.Count > 0 ) // 循环中直到栈为空才会退出循环
|
||||||
{
|
{
|
||||||
|
if(cts is not null)
|
||||||
|
{
|
||||||
|
if (cts.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
}
|
||||||
// 节点执行异常时跳过执行
|
// 节点执行异常时跳过执行
|
||||||
|
|
||||||
// 从栈中弹出一个节点作为当前节点进行处理
|
// 从栈中弹出一个节点作为当前节点进行处理
|
||||||
var currentNode = stack.Pop();
|
var currentNode = stack.Pop();
|
||||||
|
|
||||||
// 设置方法执行的对象
|
// 设置方法执行的对象
|
||||||
if (currentNode.MethodDetails?.ActingInstance == null && currentNode.MethodDetails?.ActingInstanceType is not null)
|
if (currentNode.MethodDetails?.ActingInstance is not null && currentNode.MethodDetails?.ActingInstanceType is not null)
|
||||||
{
|
{
|
||||||
currentNode.MethodDetails.ActingInstance ??= context.Env.IOC.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType);
|
currentNode.MethodDetails.ActingInstance = context.Env.IOC.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 执行相关
|
#region 执行相关
|
||||||
@@ -134,7 +139,7 @@ namespace Serein.NodeFlow.Base
|
|||||||
|
|
||||||
// 执行当前节点
|
// 执行当前节点
|
||||||
object? newFlowData = await currentNode.ExecutingAsync(context);
|
object? newFlowData = await currentNode.ExecutingAsync(context);
|
||||||
if (cts == null || cts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None)
|
if (cts is null || cts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None)
|
||||||
{
|
{
|
||||||
// 不再执行
|
// 不再执行
|
||||||
break;
|
break;
|
||||||
@@ -316,7 +321,7 @@ namespace Serein.NodeFlow.Base
|
|||||||
Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举
|
Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举
|
||||||
Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
|
Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
|
||||||
Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
|
Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
|
||||||
Type t when Nullable.GetUnderlyingType(t) != null => inputParameter == null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
|
Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
|
||||||
_ => inputParameter,
|
_ => inputParameter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,11 +322,21 @@ namespace Serein.NodeFlow
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
TryGetMethodDetails(nodeInfo.MethodName, out MethodDetails? methodDetails); // 加载项目时尝试获取方法信息
|
TryGetMethodDetails(nodeInfo.MethodName, out MethodDetails? methodDetails); // 加载项目时尝试获取方法信息
|
||||||
methodDetails ??= new MethodDetails();
|
if(controlType == NodeControlType.ExpOp || controlType == NodeControlType.ExpOp)
|
||||||
|
{
|
||||||
|
methodDetails ??= new MethodDetails();
|
||||||
|
}
|
||||||
|
if(methodDetails is null)
|
||||||
|
{
|
||||||
|
continue; // 节点对应的方法不存在于DLL中
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var nodeModel = CreateNode(controlType, methodDetails);
|
var nodeModel = CreateNode(controlType, methodDetails);
|
||||||
nodeModel.LoadInfo(nodeInfo); // 创建节点model
|
nodeModel.LoadInfo(nodeInfo); // 创建节点model
|
||||||
if (nodeModel is null)
|
if (nodeModel is null)
|
||||||
{
|
{
|
||||||
|
nodeInfo.Guid = string.Empty;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TryAddNode(nodeModel);
|
TryAddNode(nodeModel);
|
||||||
@@ -393,8 +403,8 @@ namespace Serein.NodeFlow
|
|||||||
|
|
||||||
List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
||||||
.Select(info => (info.connectionType,
|
.Select(info => (info.connectionType,
|
||||||
info.guids.Select(guid => Nodes[guid])
|
info.guids.Where(guid => Nodes.ContainsKey(guid)).Select(guid => Nodes[guid])
|
||||||
.ToArray()))
|
.ToArray()))
|
||||||
.ToList();
|
.ToList();
|
||||||
// 遍历每种类型的节点分支(四种)
|
// 遍历每种类型的节点分支(四种)
|
||||||
foreach ((ConnectionType connectionType, NodeModelBase[] toNodes) item in fromNodes)
|
foreach ((ConnectionType connectionType, NodeModelBase[] toNodes) item in fromNodes)
|
||||||
@@ -841,7 +851,7 @@ namespace Serein.NodeFlow
|
|||||||
foreach (var item in scanTypes)
|
foreach (var item in scanTypes)
|
||||||
{
|
{
|
||||||
// 加载DLL,创建 MethodDetails、实例作用对象、委托方法
|
// 加载DLL,创建 MethodDetails、实例作用对象、委托方法
|
||||||
var itemMethodDetails = MethodDetailsHelperTmp.GetList(item, false);
|
var itemMethodDetails = MethodDetailsHelperTmp.GetList(item);
|
||||||
methodDetails.AddRange(itemMethodDetails);
|
methodDetails.AddRange(itemMethodDetails);
|
||||||
//foreach (var md in itemMethodDetails)
|
//foreach (var md in itemMethodDetails)
|
||||||
//{
|
//{
|
||||||
@@ -905,7 +915,7 @@ namespace Serein.NodeFlow
|
|||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
|
|
||||||
if (nodeType == null)
|
if (nodeType is null)
|
||||||
{
|
{
|
||||||
throw new Exception($"节点类型错误[{nodeControlType}]");
|
throw new Exception($"节点类型错误[{nodeControlType}]");
|
||||||
}
|
}
|
||||||
@@ -953,7 +963,7 @@ namespace Serein.NodeFlow
|
|||||||
/// <param name="connectionType">连接关系</param>
|
/// <param name="connectionType">连接关系</param>
|
||||||
private void ConnectNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
|
private void ConnectNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
|
||||||
{
|
{
|
||||||
if (fromNode == null || toNode == null || fromNode == toNode)
|
if (fromNode is null || toNode is null || fromNode == toNode)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,10 +308,9 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
|
|
||||||
var context = new DynamicContext(env); // 启动全局触发器时新建上下文
|
var context = new DynamicContext(env); // 启动全局触发器时新建上下文
|
||||||
try
|
while (!_flipFlopCts.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
while (!_flipFlopCts.IsCancellationRequested)
|
|
||||||
{
|
{
|
||||||
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task
|
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task
|
||||||
await NodeModelBase.RefreshFlowDataAndExpInterrupt(context, singleFlipFlopNode, newFlowData); // 全局触发器触发后刷新该触发器的节点数据
|
await NodeModelBase.RefreshFlowDataAndExpInterrupt(context, singleFlipFlopNode, newFlowData); // 全局触发器触发后刷新该触发器的节点数据
|
||||||
@@ -321,7 +320,7 @@ namespace Serein.NodeFlow
|
|||||||
for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--)
|
for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--)
|
||||||
{
|
{
|
||||||
// 筛选出启用的节点
|
// 筛选出启用的节点
|
||||||
if (nextNodes[i].DebugSetting.IsEnable)
|
if (nextNodes[i].DebugSetting.IsEnable)
|
||||||
{
|
{
|
||||||
nextNodes[i].PreviousNode = singleFlipFlopNode;
|
nextNodes[i].PreviousNode = singleFlipFlopNode;
|
||||||
if (nextNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
|
if (nextNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
|
||||||
@@ -333,14 +332,12 @@ namespace Serein.NodeFlow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await Console.Out.WriteLineAsync(ex.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await Console.Out.WriteLineAsync(ex.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//MethodDetails md = singleFlipFlopNode.MethodDetails;
|
//MethodDetails md = singleFlipFlopNode.MethodDetails;
|
||||||
//var del = md.MethodDelegate;
|
//var del = md.MethodDelegate;
|
||||||
|
|||||||
@@ -32,12 +32,8 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
internal override NodeInfo ToInfo()
|
internal override NodeInfo ToInfo()
|
||||||
{
|
{
|
||||||
if (MethodDetails == null) return null;
|
if (MethodDetails is null) return null;
|
||||||
|
|
||||||
//var trueNodes = SucceedBranch.Select(item => item.Guid); // 真分支
|
|
||||||
//var falseNodes = FailBranch.Select(item => item.Guid);// 假分支
|
|
||||||
//var upstreamNodes = UpstreamBranch.Select(item => item.Guid);// 上游分支
|
|
||||||
//var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支
|
|
||||||
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
|
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
|
||||||
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
|
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
|
||||||
var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
|
var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
0 => ((Func<object, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance),
|
0 => ((Func<object, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance),
|
||||||
_ => ((Func<object, object?[]?, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance, GetParameters(context, this, md)), // 执行流程中的触发器方法时获取入参参数
|
_ => ((Func<object, object?[]?, Task<IFlipflopContext>>)del).Invoke(md.ActingInstance, GetParameters(context, this, md)), // 执行流程中的触发器方法时获取入参参数
|
||||||
};
|
};
|
||||||
|
|
||||||
IFlipflopContext flipflopContext = (await flipflopTask) ?? throw new FlipflopException("没有返回上下文");
|
IFlipflopContext flipflopContext = (await flipflopTask) ?? throw new FlipflopException("没有返回上下文");
|
||||||
NextOrientation = flipflopContext.State.ToContentType();
|
NextOrientation = flipflopContext.State.ToContentType();
|
||||||
|
|||||||
@@ -15,16 +15,16 @@ public static class MethodDetailsHelperTmp
|
|||||||
/// <param name="serviceContainer"></param>
|
/// <param name="serviceContainer"></param>
|
||||||
/// <param name="type"></param>
|
/// <param name="type"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static List<MethodDetails> GetList(Type type, bool isNetFramework)
|
public static List<MethodDetails> GetList(Type type)
|
||||||
{
|
{
|
||||||
var methodDetailsDictionary = new List<MethodDetails>();
|
var methodDetailsDictionary = new List<MethodDetails>();
|
||||||
var assemblyName = type.Assembly.GetName().Name;
|
var assemblyName = type.Assembly.GetName().Name;
|
||||||
var methods = GetMethodsToProcess(type, isNetFramework);
|
var methods = GetMethodsToProcess(type);
|
||||||
|
|
||||||
foreach (var method in methods)
|
foreach (var method in methods)
|
||||||
{
|
{
|
||||||
|
|
||||||
var methodDetails = CreateMethodDetails(type, method, assemblyName, isNetFramework);
|
var methodDetails = CreateMethodDetails(type, method, assemblyName);
|
||||||
methodDetailsDictionary.Add(methodDetails);
|
methodDetailsDictionary.Add(methodDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,26 +33,16 @@ public static class MethodDetailsHelperTmp
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取处理方法
|
/// 获取处理方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static IEnumerable<MethodInfo> GetMethodsToProcess(Type type, bool isNetFramework)
|
private static IEnumerable<MethodInfo> GetMethodsToProcess(Type type)
|
||||||
{
|
{
|
||||||
if (isNetFramework)
|
return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
{
|
|
||||||
|
|
||||||
return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
|
||||||
.Where(m => m.GetCustomAttribute<NodeActionAttribute>()?.Scan == true);
|
.Where(m => m.GetCustomAttribute<NodeActionAttribute>()?.Scan == true);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
|
||||||
.Where(m => m.GetCustomAttribute<NodeActionAttribute>()?.Scan == true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建方法信息
|
/// 创建方法信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static MethodDetails CreateMethodDetails(Type type, MethodInfo method, string assemblyName, bool isNetFramework)
|
private static MethodDetails CreateMethodDetails(Type type, MethodInfo method, string assemblyName)
|
||||||
{
|
{
|
||||||
|
|
||||||
var methodName = method.Name;
|
var methodName = method.Name;
|
||||||
@@ -98,18 +88,31 @@ public static class MethodDetailsHelperTmp
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取参数信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters"></param>
|
||||||
|
/// <returns></returns>
|
||||||
private static ExplicitData[] GetExplicitDataOfParameters(ParameterInfo[] parameters)
|
private static ExplicitData[] GetExplicitDataOfParameters(ParameterInfo[] parameters)
|
||||||
{
|
{
|
||||||
|
|
||||||
return parameters.Select((it, index) =>
|
return parameters.Select((it, index) =>
|
||||||
{
|
{
|
||||||
//Console.WriteLine($"{it.Name}-{it.HasDefaultValue}-{it.DefaultValue}");
|
Type paremType;
|
||||||
string explicitTypeName = GetExplicitTypeName(it.ParameterType);
|
//var attribute = it.ParameterType.GetCustomAttribute<EnumConvertorAttribute>();
|
||||||
var items = GetExplicitItems(it.ParameterType, explicitTypeName);
|
//if (attribute is not null && attribute.Enum.IsEnum)
|
||||||
|
//{
|
||||||
|
// // 存在选择器
|
||||||
|
// paremType = attribute.Enum;
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// paremType = it.ParameterType;
|
||||||
|
//}
|
||||||
|
paremType = it.ParameterType;
|
||||||
|
string explicitTypeName = GetExplicitTypeName(paremType);
|
||||||
|
var items = GetExplicitItems(paremType, explicitTypeName);
|
||||||
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
|
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return new ExplicitData
|
return new ExplicitData
|
||||||
{
|
{
|
||||||
IsExplicitData = it.HasDefaultValue,
|
IsExplicitData = it.HasDefaultValue,
|
||||||
@@ -127,6 +130,11 @@ public static class MethodDetailsHelperTmp
|
|||||||
}).ToArray();
|
}).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断使用输入器还是选择器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
private static string GetExplicitTypeName(Type type)
|
private static string GetExplicitTypeName(Type type)
|
||||||
{
|
{
|
||||||
return type switch
|
return type switch
|
||||||
@@ -140,15 +148,22 @@ public static class MethodDetailsHelperTmp
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取参数列表选项
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="explicitTypeName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
private static IEnumerable<string> GetExplicitItems(Type type, string explicitTypeName)
|
private static IEnumerable<string> GetExplicitItems(Type type, string explicitTypeName)
|
||||||
{
|
{
|
||||||
return explicitTypeName switch
|
IEnumerable<string> items = explicitTypeName switch
|
||||||
{
|
{
|
||||||
"Select" => Enum.GetNames(type),
|
"Select" => Enum.GetNames(type),
|
||||||
"Bool" => ["True", "False"],
|
"Bool" => ["True", "False"],
|
||||||
_ => []
|
_ => []
|
||||||
};
|
};
|
||||||
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Delegate GenerateMethodDelegate(Type type, MethodInfo methodInfo, ParameterInfo[] parameters, Type returnType)
|
private static Delegate GenerateMethodDelegate(Type type, MethodInfo methodInfo, ParameterInfo[] parameters, Type returnType)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
|||||||
foreach (var member in members)
|
foreach (var member in members)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (obj == null) return null;
|
if (obj is null) return null;
|
||||||
|
|
||||||
Type type = obj.GetType();
|
Type type = obj.GetType();
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
string[] members = memberPath[1..].Split('.');
|
string[] members = memberPath[1..].Split('.');
|
||||||
foreach (var member in members)
|
foreach (var member in members)
|
||||||
{
|
{
|
||||||
if (obj == null) return null;
|
if (obj is null) return null;
|
||||||
Type type = obj.GetType();
|
Type type = obj.GetType();
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
FieldInfo? fieldInfo = type.GetField(member);
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var method = target.GetType().GetMethod(methodName);
|
var method = target.GetType().GetMethod(methodName);
|
||||||
if (method == null)
|
if (method is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Method {methodName} not found on target.");
|
throw new ArgumentException($"Method {methodName} not found on target.");
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
|||||||
foreach (var member in members)
|
foreach (var member in members)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (target == null) return null;
|
if (target is null) return null;
|
||||||
|
|
||||||
|
|
||||||
var property = target.GetType().GetProperty(member);
|
var property = target.GetType().GetProperty(member);
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
https://space.bilibili.com/33526379
|
https://space.bilibili.com/33526379
|
||||||
|
|
||||||
# 计划任务 2024年9月17日更新
|
# 计划任务 2024年9月17日更新
|
||||||
* (重要+优先)正在计划实现断点功能,查看运行中节点的数据、ioc容器对象
|
* (重要+优先)正在实现断点功能,单步执行
|
||||||
* 正在计划新增基础节点“属性包装器”,用来收集各个节点的数据,包装成匿名对象/Json类型
|
* 计划实现节点树视图、IOC容器对象视图
|
||||||
* 正在计划实现网络方面的通讯,方便传出、传入数据
|
* 正在计划实现网络方面的通讯,方便传出、传入数据
|
||||||
|
* 正在计划新增基础节点“属性包装器”,用来收集各个节点的数据,包装成匿名对象/Json类型(暂时未想到如何设计)
|
||||||
* 正在计划实现对数据传递的包装, 尽可能避免拆箱、装箱,优化传递效率。(可能做不到)
|
* 正在计划实现对数据传递的包装, 尽可能避免拆箱、装箱,优化传递效率。(可能做不到)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.9.34728.123
|
VisualStudioVersion = 17.9.34728.123
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyDll", "MyDll\MyDll.csproj", "{69190D46-0B07-47CD-BC67-D542BA7F6C91}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.WorkBench", "WorkBench\Serein.WorkBench.csproj", "{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.WorkBench", "WorkBench\Serein.WorkBench.csproj", "{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library.Core", "Library.Core\Serein.Library.Core.csproj", "{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library.Core", "Library.Core\Serein.Library.Core.csproj", "{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}"
|
||||||
@@ -20,16 +18,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.Framework",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net461DllTest", "Net461DllTest\Net461DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{69190D46-0B07-47CD-BC67-D542BA7F6C91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{69190D46-0B07-47CD-BC67-D542BA7F6C91}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{69190D46-0B07-47CD-BC67-D542BA7F6C91}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{69190D46-0B07-47CD-BC67-D542BA7F6C91}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -50,6 +46,10 @@ Global
|
|||||||
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E40EE629-1A38-4011-88E3-9AD036869987}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E40EE629-1A38-4011-88E3-9AD036869987}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E40EE629-1A38-4011-88E3-9AD036869987}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E40EE629-1A38-4011-88E3-9AD036869987}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
<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" />
|
|
||||||
<ProjectReference Include="..\NodeFlow\Serein.NodeFlow.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,270 +0,0 @@
|
|||||||
using OpenQA.Selenium;
|
|
||||||
using OpenQA.Selenium.Chrome;
|
|
||||||
using OpenQA.Selenium.Edge;
|
|
||||||
using OpenQA.Selenium.Firefox;
|
|
||||||
using OpenQA.Selenium.IE;
|
|
||||||
using Serein.NodeFlow;
|
|
||||||
using Serein.NodeFlow.Tool;
|
|
||||||
using static System.Net.Mime.MediaTypeNames;
|
|
||||||
|
|
||||||
namespace Serein.Module
|
|
||||||
{
|
|
||||||
public enum DriverType
|
|
||||||
{
|
|
||||||
Chrome,
|
|
||||||
Edge,
|
|
||||||
IE,
|
|
||||||
Firefox,
|
|
||||||
}
|
|
||||||
public enum ByType
|
|
||||||
{
|
|
||||||
XPath,
|
|
||||||
Id,
|
|
||||||
Class,
|
|
||||||
Name,
|
|
||||||
CssSelector,
|
|
||||||
PartialLinkText,
|
|
||||||
}
|
|
||||||
public enum ScriptOp
|
|
||||||
{
|
|
||||||
Add,
|
|
||||||
Modify,
|
|
||||||
Delete,
|
|
||||||
}
|
|
||||||
public enum ActionType
|
|
||||||
{
|
|
||||||
Click,
|
|
||||||
DoubleClick,
|
|
||||||
RightClick,
|
|
||||||
SendKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <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 void Wait(int time = 1000)
|
|
||||||
{
|
|
||||||
Thread.Sleep(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action,"启动浏览器")]
|
|
||||||
public WebDriver OpenDriver( bool isVisible = true, 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 ToPage( 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,"定位元素")]
|
|
||||||
public IWebElement FindElement( string key = "", ByType byType = ByType.XPath, int index = 0)
|
|
||||||
{
|
|
||||||
By by = byType switch
|
|
||||||
{
|
|
||||||
ByType.Id => By.Id(key),
|
|
||||||
ByType.XPath => By.XPath(key),
|
|
||||||
ByType.Class => By.ClassName(key),
|
|
||||||
ByType.Name => By.Name(key),
|
|
||||||
ByType.CssSelector => By.CssSelector(key),
|
|
||||||
ByType.PartialLinkText => By.PartialLinkText(key),
|
|
||||||
};
|
|
||||||
if(index == -1)
|
|
||||||
{
|
|
||||||
return WebDriver.FindElements(by).First();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return WebDriver.FindElements(by)[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action, "定位并操作元素")]
|
|
||||||
public IWebElement FindAndUseElement( ByType byType = ByType.XPath,
|
|
||||||
string key = "",
|
|
||||||
ActionType actionType = ActionType.Click,
|
|
||||||
string text = "",
|
|
||||||
int index = 0,
|
|
||||||
int waitTime = 0)
|
|
||||||
{
|
|
||||||
Thread.Sleep(waitTime);
|
|
||||||
By by = byType switch
|
|
||||||
{
|
|
||||||
ByType.Id => By.Id(key),
|
|
||||||
ByType.XPath => By.XPath(key),
|
|
||||||
ByType.Class => By.ClassName(key),
|
|
||||||
ByType.Name => By.Name(key),
|
|
||||||
ByType.CssSelector => By.CssSelector(key),
|
|
||||||
ByType.PartialLinkText => By.PartialLinkText(key),
|
|
||||||
};
|
|
||||||
var element = WebDriver.FindElements(by)[index];
|
|
||||||
Thread.Sleep(waitTime);
|
|
||||||
var actions = new OpenQA.Selenium.Interactions.Actions(WebDriver);
|
|
||||||
switch (actionType)
|
|
||||||
{
|
|
||||||
case ActionType.Click:
|
|
||||||
actions.Click(element).Perform();
|
|
||||||
break;
|
|
||||||
case ActionType.DoubleClick:
|
|
||||||
actions.DoubleClick(element).Perform();
|
|
||||||
break;
|
|
||||||
case ActionType.RightClick:
|
|
||||||
actions.ContextClick(element).Perform();
|
|
||||||
break;
|
|
||||||
case ActionType.SendKeys:
|
|
||||||
element.Click();
|
|
||||||
element.Clear();
|
|
||||||
element.SendKeys(text);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action, "操作元素")]
|
|
||||||
public void PerformAction(IWebElement element, ActionType actionType = ActionType.Click, string text = "")
|
|
||||||
{
|
|
||||||
var actions = new OpenQA.Selenium.Interactions.Actions(WebDriver);
|
|
||||||
|
|
||||||
switch (actionType)
|
|
||||||
{
|
|
||||||
case ActionType.Click:
|
|
||||||
actions.Click(element).Perform();
|
|
||||||
break;
|
|
||||||
case ActionType.DoubleClick:
|
|
||||||
actions.DoubleClick(element).Perform();
|
|
||||||
break;
|
|
||||||
case ActionType.RightClick:
|
|
||||||
actions.ContextClick(element).Perform();
|
|
||||||
break;
|
|
||||||
case ActionType.SendKeys:
|
|
||||||
element.Click();
|
|
||||||
element.Clear();
|
|
||||||
element.SendKeys(text);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action,"Js操作元素属性")]
|
|
||||||
public void AddAttribute(IWebElement element, ScriptOp scriptOp = ScriptOp.Modify, string attributeName = "", string value = "")
|
|
||||||
{
|
|
||||||
if(scriptOp == ScriptOp.Add)
|
|
||||||
{
|
|
||||||
WebDriver.ExecuteScript($"arguments[0].{attributeName} = arguments[1];", element, value);
|
|
||||||
}
|
|
||||||
else if (scriptOp == ScriptOp.Modify)
|
|
||||||
{
|
|
||||||
element.SetAttribute(WebDriver, attributeName, value);
|
|
||||||
string newHref = element.GetAttribute("href");
|
|
||||||
Console.WriteLine("New href value: " + newHref);
|
|
||||||
|
|
||||||
WebDriver.ExecuteScript("arguments[0].setAttribute(arguments[1], arguments[2]);", element, attributeName, value);
|
|
||||||
}
|
|
||||||
else if (scriptOp == ScriptOp.Delete)
|
|
||||||
{
|
|
||||||
WebDriver.ExecuteScript("arguments[0].removeAttribute(arguments[1]);", element, attributeName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[MethodDetail(DynamicNodeType.Action, "Js获取元素属性")]
|
|
||||||
public string GetAttribute(IWebElement element, string attributeName = "")
|
|
||||||
{
|
|
||||||
return element.GetAttribute(attributeName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MyExtension
|
|
||||||
{
|
|
||||||
public static void SetAttribute(this IWebElement element, IWebDriver driver, string attributeName, string value)
|
|
||||||
{
|
|
||||||
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
|
|
||||||
js.ExecuteScript($"arguments[0].setAttribute('{attributeName}', '{value}');", element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -150,17 +150,23 @@ namespace Serein.WorkBench
|
|||||||
Shutdown(); // 关闭应用程序
|
Shutdown(); // 关闭应用程序
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
else if(1== 1)
|
||||||
|
{
|
||||||
//string filePath = @"F:\临时\project\new project.dnf";
|
//string filePath = @"F:\临时\project\new project.dnf";
|
||||||
string filePath = @"F:\临时\project\tmp\project.dnf";
|
|
||||||
|
string filePath;
|
||||||
|
//filePath = @"F:\临时\project\tmp\project.dnf";
|
||||||
|
//filePath = @"D:\Project\C#\TestNetFramework\Net45DllTest\Net45DllTest\bin\Debug\project.dnf";
|
||||||
|
filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net461DllTest\bin\Debug\project.dnf";
|
||||||
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
|
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
|
||||||
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
|
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
|
||||||
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
||||||
App.FileDataPath = filePath;//System.IO.Path.GetDirectoryName(filePath)!;
|
App.FileDataPath = filePath;//System.IO.Path.GetDirectoryName(filePath)!;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,10 @@ namespace Serein.WorkBench
|
|||||||
InitCanvasUI(); // 配置画布
|
InitCanvasUI(); // 配置画布
|
||||||
|
|
||||||
|
|
||||||
FlowEnvironment.LoadProject(App.FlowProjectData, App.FileDataPath); // 加载项目
|
if(App.FlowProjectData is not null)
|
||||||
|
{
|
||||||
|
FlowEnvironment.LoadProject(App.FlowProjectData, App.FileDataPath); // 加载项目
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitFlowEnvironmentEvent()
|
private void InitFlowEnvironmentEvent()
|
||||||
@@ -211,7 +214,7 @@ namespace Serein.WorkBench
|
|||||||
private void Window_ContentRendered(object sender, EventArgs e)
|
private void Window_ContentRendered(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var project = App.FlowProjectData;
|
var project = App.FlowProjectData;
|
||||||
if (project == null)
|
if (project is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -396,7 +399,7 @@ namespace Serein.WorkBench
|
|||||||
NodeControlType.ConditionRegion => CreateNodeControl<ConditionRegionControl, ConditionRegionNodeControlViewModel>(nodeModelBase),
|
NodeControlType.ConditionRegion => CreateNodeControl<ConditionRegionControl, ConditionRegionNodeControlViewModel>(nodeModelBase),
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
if (nodeControl == null)
|
if (nodeControl is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -609,7 +612,7 @@ namespace Serein.WorkBench
|
|||||||
if (FlowEnvironment.TryGetMethodDetails(childNode.MethodName, out MethodDetails md))
|
if (FlowEnvironment.TryGetMethodDetails(childNode.MethodName, out MethodDetails md))
|
||||||
{
|
{
|
||||||
var childNodeControl = CreateNodeControlOfNodeInfo(childNode, md);
|
var childNodeControl = CreateNodeControlOfNodeInfo(childNode, md);
|
||||||
if (childNodeControl == null)
|
if (childNodeControl is null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"无法为节点类型创建节点控件: {childNode.MethodName}\r\n");
|
Console.WriteLine($"无法为节点类型创建节点控件: {childNode.MethodName}\r\n");
|
||||||
continue;
|
continue;
|
||||||
@@ -801,7 +804,7 @@ namespace Serein.WorkBench
|
|||||||
private void DeleteConnection(Connection connection)
|
private void DeleteConnection(Connection connection)
|
||||||
{
|
{
|
||||||
var connectionToRemove = connection;
|
var connectionToRemove = connection;
|
||||||
if (connectionToRemove == null)
|
if (connectionToRemove is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -896,7 +899,7 @@ namespace Serein.WorkBench
|
|||||||
if (IsConnecting) // 正在连接节点
|
if (IsConnecting) // 正在连接节点
|
||||||
{
|
{
|
||||||
Point position = e.GetPosition(FlowChartCanvas);
|
Point position = e.GetPosition(FlowChartCanvas);
|
||||||
if (currentLine == null || startConnectNodeControl == null)
|
if (currentLine is null || startConnectNodeControl is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1785,7 +1788,7 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
public void AlignControlsWithGrouping(List<NodeControlBase> selectNodeControls, double proximityThreshold = 50, double spacing = 10)
|
public void AlignControlsWithGrouping(List<NodeControlBase> selectNodeControls, double proximityThreshold = 50, double spacing = 10)
|
||||||
{
|
{
|
||||||
if (selectNodeControls == null || selectNodeControls.Count < 2)
|
if (selectNodeControls is null || selectNodeControls.Count < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 按照控件的相对位置进行分组
|
// 按照控件的相对位置进行分组
|
||||||
@@ -1865,7 +1868,7 @@ namespace Serein.WorkBench
|
|||||||
#region Plan B 规划对齐
|
#region Plan B 规划对齐
|
||||||
public void AlignControlsWithDynamicProgramming(List<NodeControlBase> selectNodeControls, double spacing = 10)
|
public void AlignControlsWithDynamicProgramming(List<NodeControlBase> selectNodeControls, double spacing = 10)
|
||||||
{
|
{
|
||||||
if (selectNodeControls == null || selectNodeControls.Count < 2)
|
if (selectNodeControls is null || selectNodeControls.Count < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int n = selectNodeControls.Count;
|
int n = selectNodeControls.Count;
|
||||||
@@ -1973,7 +1976,7 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
public void AlignControlsWithGrouping(List<NodeControlBase> selectNodeControls, AlignMode alignMode, double proximityThreshold = 50, double spacing = 10)
|
public void AlignControlsWithGrouping(List<NodeControlBase> selectNodeControls, AlignMode alignMode, double proximityThreshold = 50, double spacing = 10)
|
||||||
{
|
{
|
||||||
if (selectNodeControls == null || selectNodeControls.Count < 2)
|
if (selectNodeControls is null || selectNodeControls.Count < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (alignMode)
|
switch (alignMode)
|
||||||
@@ -2064,7 +2067,7 @@ namespace Serein.WorkBench
|
|||||||
where TViewModel : NodeControlViewModelBase
|
where TViewModel : NodeControlViewModelBase
|
||||||
{
|
{
|
||||||
|
|
||||||
if (model == null)
|
if (model is null)
|
||||||
{
|
{
|
||||||
throw new Exception("无法创建节点控件");
|
throw new Exception("无法创建节点控件");
|
||||||
}
|
}
|
||||||
@@ -2089,7 +2092,7 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
var nodeObj = Activator.CreateInstance(typeof(TNode));
|
var nodeObj = Activator.CreateInstance(typeof(TNode));
|
||||||
var nodeBase = nodeObj as NodeModelBase;
|
var nodeBase = nodeObj as NodeModelBase;
|
||||||
if (nodeBase == null)
|
if (nodeBase is null)
|
||||||
{
|
{
|
||||||
throw new Exception("无法创建节点控件");
|
throw new Exception("无法创建节点控件");
|
||||||
}
|
}
|
||||||
@@ -2378,13 +2381,13 @@ namespace Serein.WorkBench
|
|||||||
//MakeDraggable(canvas, connection, connection.Start);
|
//MakeDraggable(canvas, connection, connection.Start);
|
||||||
//MakeDraggable(canvas, connection, connection.End);
|
//MakeDraggable(canvas, connection, connection.End);
|
||||||
|
|
||||||
if (connection.BezierPath == null)
|
if (connection.BezierPath is null)
|
||||||
{
|
{
|
||||||
connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
||||||
Canvas.SetZIndex(connection.BezierPath, -1);
|
Canvas.SetZIndex(connection.BezierPath, -1);
|
||||||
canvas.Children.Add(connection.BezierPath);
|
canvas.Children.Add(connection.BezierPath);
|
||||||
}
|
}
|
||||||
if (connection.ArrowPath == null)
|
if (connection.ArrowPath is null)
|
||||||
{
|
{
|
||||||
connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
||||||
Canvas.SetZIndex(connection.ArrowPath, -1);
|
Canvas.SetZIndex(connection.ArrowPath, -1);
|
||||||
@@ -2410,14 +2413,14 @@ namespace Serein.WorkBench
|
|||||||
|
|
||||||
canvas.Dispatcher.InvokeAsync(() =>
|
canvas.Dispatcher.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
if (connection != null && connection.BezierPath == null)
|
if (connection is not null && connection.BezierPath is null)
|
||||||
{
|
{
|
||||||
connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
||||||
//Canvas.SetZIndex(connection.BezierPath, -1);
|
//Canvas.SetZIndex(connection.BezierPath, -1);
|
||||||
canvas.Children.Add(connection.BezierPath);
|
canvas.Children.Add(connection.BezierPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connection != null && connection.ArrowPath == null)
|
if (connection is not null && connection.ArrowPath is null)
|
||||||
{
|
{
|
||||||
connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
||||||
//Canvas.SetZIndex(connection.ArrowPath, -1);
|
//Canvas.SetZIndex(connection.ArrowPath, -1);
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:Converters="clr-namespace:Serein.WorkBench.Tool.Converters"
|
||||||
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
||||||
xmlns:vm="clr-namespace:Serein.WorkBench.Node.ViewModel"
|
xmlns:vm="clr-namespace:Serein.WorkBench.Node.ViewModel"
|
||||||
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
|
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
|
||||||
@@ -11,40 +13,50 @@
|
|||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<vm:TypeToStringConverter x:Key="TypeToStringConverter"/>
|
<vm:TypeToStringConverter x:Key="TypeToStringConverter"/>
|
||||||
<!--<themes:ConditionControl x:Key="ConditionControl"/>-->
|
<!--<themes:ConditionControl x:Key="ConditionControl"/>-->
|
||||||
|
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
<Border BorderBrush="#FCB334" BorderThickness="1">
|
<Border BorderBrush="#FCB334" BorderThickness="1">
|
||||||
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.ToolTip>
|
|
||||||
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
|
||||||
</Grid.ToolTip>
|
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid>
|
||||||
<RowDefinition Height="*"/>
|
<Grid.ToolTip>
|
||||||
<RowDefinition Height="*"/>
|
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
<RowDefinition Height="*"/>
|
</Grid.ToolTip>
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Border Background="#FCB334" >
|
<Grid.RowDefinitions>
|
||||||
<TextBlock Grid.Row="0" Text="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<RowDefinition Height="*"/>
|
||||||
</Border>
|
<RowDefinition Height="*"/>
|
||||||
<!--<themes:ExplicitDataControl Grid.Row="1" ExplicitDatas="{Binding ExplicitDatas}" />-->
|
<RowDefinition Height="*"/>
|
||||||
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
|
</Grid.RowDefinitions>
|
||||||
<Grid Grid.Row="2" Background="#D5F0FC" >
|
|
||||||
<Grid.ColumnDefinitions>
|
<StackPanel Grid.Row="0" Orientation="Horizontal" Background="#FCB334">
|
||||||
<ColumnDefinition Width="50"/>
|
<CheckBox IsChecked="{Binding DebugSetting.IsEnable, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<CheckBox IsChecked="{Binding MethodDetails.IsProtectionParameter, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Border Grid.Column="0" BorderThickness="1">
|
<TextBlock Text="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
</StackPanel>
|
||||||
</Border>
|
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
|
||||||
<Border Grid.Column="1" BorderThickness="1">
|
|
||||||
<TextBlock Text="{Binding MethodDetails.ReturnType}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<Border Grid.Row="1" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
|
||||||
</Border>
|
Visibility="{Binding MethodDetails.IsProtectionParameter, Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" />
|
||||||
</Grid>
|
<!--<Border Grid.Row="0" Background="#FCB334" >
|
||||||
<!--<themes:ConditionControl Grid.Row="2" ></themes:ConditionControl>-->
|
|
||||||
|
</Border>-->
|
||||||
|
<!--<themes:ExplicitDataControl Grid.Row="1" ExplicitDatas="{Binding ExplicitDatas}" />-->
|
||||||
|
<Grid Grid.Row="2" Background="#D5F0FC" >
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="50"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Border Grid.Column="0" BorderThickness="1">
|
||||||
|
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<Border Grid.Column="1" BorderThickness="1">
|
||||||
|
<TextBlock Text="{Binding MethodDetails.ReturnType}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
<!--<themes:ConditionControl Grid.Row="2" ></themes:ConditionControl>-->
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</local:NodeControlBase>
|
</local:NodeControlBase>
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ namespace Serein.WorkBench.Themes
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上次刷新事件
|
/// 上次刷新时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
//private DateTime lastRefreshTime = DateTime.MinValue;
|
//private DateTime lastRefreshTime = DateTime.MinValue;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user