mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-02 15:50:47 +08:00
在Serein.Library添加了基础功能模块,暂时实现了键值对/数组数据的创建(可配合JSON库进行序列化)
This commit is contained in:
@@ -40,6 +40,11 @@ namespace Serein.Library.Core
|
||||
/// </summary>
|
||||
public ConnectionInvokeType NextOrientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 运行时异常信息
|
||||
/// </summary>
|
||||
public Exception ExceptionOfRuning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每个流程上下文分别存放节点的当前数据
|
||||
/// </summary>
|
||||
@@ -138,6 +143,10 @@ namespace Serein.Library.Core
|
||||
disposable?.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
foreach (var nodeObj in ContextShareData.Values)
|
||||
{
|
||||
@@ -158,5 +167,92 @@ namespace Serein.Library.Core
|
||||
RunState = RunState.Completion;
|
||||
}
|
||||
|
||||
|
||||
private void Dispose(ref IDictionary<string, object> keyValuePairs)
|
||||
{
|
||||
foreach (var nodeObj in keyValuePairs.Values)
|
||||
{
|
||||
if (nodeObj is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nodeObj is IDisposable disposable) /* typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) &&*/
|
||||
{
|
||||
disposable?.Dispose();
|
||||
}
|
||||
else if (nodeObj is IDictionary<string, object> tmpDict)
|
||||
{
|
||||
Dispose(ref tmpDict);
|
||||
}
|
||||
else if (nodeObj is ICollection<object> tmpList)
|
||||
{
|
||||
Dispose(ref tmpList);
|
||||
}
|
||||
else if (nodeObj is IList<object> tmpList2)
|
||||
{
|
||||
Dispose(ref tmpList2);
|
||||
}
|
||||
}
|
||||
keyValuePairs.Clear();
|
||||
}
|
||||
private void Dispose(ref ICollection<object> list)
|
||||
{
|
||||
foreach (var nodeObj in list)
|
||||
{
|
||||
if (nodeObj is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nodeObj is IDisposable disposable) /* typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) &&*/
|
||||
{
|
||||
disposable?.Dispose();
|
||||
}
|
||||
else if (nodeObj is IDictionary<string, object> tmpDict)
|
||||
{
|
||||
Dispose(ref tmpDict);
|
||||
}
|
||||
else if (nodeObj is ICollection<object> tmpList)
|
||||
{
|
||||
Dispose(ref tmpList);
|
||||
}
|
||||
else if (nodeObj is IList<object> tmpList2)
|
||||
{
|
||||
Dispose(ref tmpList2);
|
||||
}
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
private void Dispose(ref IList<object> list)
|
||||
{
|
||||
foreach (var nodeObj in list)
|
||||
{
|
||||
if (nodeObj is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nodeObj is IDisposable disposable) /* typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) &&*/
|
||||
{
|
||||
disposable?.Dispose();
|
||||
}
|
||||
else if (nodeObj is IDictionary<string, object> tmpDict)
|
||||
{
|
||||
Dispose(ref tmpDict);
|
||||
}
|
||||
else if (nodeObj is ICollection<object> tmpList)
|
||||
{
|
||||
Dispose(ref tmpList);
|
||||
}
|
||||
else if (nodeObj is IList<object> tmpList2)
|
||||
{
|
||||
Dispose(ref tmpList2);
|
||||
}
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ namespace Serein.Library.Framework.NodeFlow
|
||||
/// </summary>
|
||||
public ConnectionInvokeType NextOrientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 运行时异常信息
|
||||
/// </summary>
|
||||
public Exception ExceptionOfRuning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每个上下文分别存放节点的当前数据
|
||||
/// </summary>
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
ConnectionInvokeType NextOrientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 运行时异常信息
|
||||
/// </summary>
|
||||
Exception ExceptionOfRuning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置节点的运行时上一节点,用以多线程中隔开不同流程的数据
|
||||
/// </summary>
|
||||
|
||||
@@ -857,18 +857,20 @@ namespace Serein.Library.Api
|
||||
/// <summary>
|
||||
/// 获取方法描述信息
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">程序集名称</param>
|
||||
/// <param name="methodName">方法描述</param>
|
||||
/// <param name="mdInfo">方法信息</param>
|
||||
/// <returns></returns>
|
||||
bool TryGetMethodDetailsInfo(string libraryName, string methodName, out MethodDetailsInfo mdInfo);
|
||||
bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo mdInfo);
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定方法的Emit委托
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">程序集名称</param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="del"></param>
|
||||
/// <returns></returns>
|
||||
bool TryGetDelegateDetails(string libraryName, string methodName, out DelegateDetails del);
|
||||
bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del);
|
||||
|
||||
|
||||
#region 远程相关
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Serein.Library
|
||||
_emitDelegate = emitDelegate;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 记录Emit委托
|
||||
/// </summary>
|
||||
@@ -36,7 +37,9 @@ namespace Serein.Library
|
||||
_emitMethodType = EmitMethodType;
|
||||
_emitDelegate = EmitDelegate;
|
||||
}
|
||||
/// <summary>
|
||||
|
||||
|
||||
/*/// <summary>
|
||||
/// 更新委托方法
|
||||
/// </summary>
|
||||
/// <param name="EmitMethodType"></param>
|
||||
@@ -45,7 +48,9 @@ namespace Serein.Library
|
||||
{
|
||||
_emitMethodType = EmitMethodType;
|
||||
_emitDelegate = EmitDelegate;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
private Delegate _emitDelegate;
|
||||
private EmitMethodType _emitMethodType;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
@@ -36,7 +37,7 @@ namespace Serein.Library
|
||||
private bool _isProtectionParameter;
|
||||
|
||||
/// <summary>
|
||||
/// 作用实例的类型(多个相同的节点将拥有相同的类型)
|
||||
/// 调用节点方法时需要的实例(多个相同的节点将拥有相同的类型)
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private Type _actingInstanceType;
|
||||
@@ -77,6 +78,7 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private ParameterDetails[] _parameterDetailss;
|
||||
//private List<ParameterDetails> _parameterDetailss;
|
||||
|
||||
/// <summary>
|
||||
/// <para>描述该方法是否存在可选参数</para>
|
||||
@@ -137,6 +139,9 @@ namespace Serein.Library
|
||||
&& index < ParameterDetailss.Length) // 防止下标越界
|
||||
{
|
||||
ParameterDetailss[index] = null; // 释放对象引用
|
||||
|
||||
|
||||
|
||||
var tmp = ArrayHelper.RemoteToArray<ParameterDetails>(ParameterDetailss, index); // 新增;
|
||||
UpdateParamIndex(ref tmp);
|
||||
ParameterDetailss = tmp; // 新增
|
||||
|
||||
@@ -62,25 +62,6 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
[PropertyInfo(IsProtection = true)]
|
||||
private MethodDetails _methodDetails ;
|
||||
|
||||
/// <summary>
|
||||
/// 运行时的上一节点
|
||||
/// </summary>
|
||||
//[PropertyInfo]
|
||||
//private NodeModelBase _previousNode ;
|
||||
|
||||
/// <summary>
|
||||
/// 当前节点执行完毕后需要执行的下一个分支的类别
|
||||
/// </summary>
|
||||
//[PropertyInfo]
|
||||
//private ConnectionInvokeType _nextOrientation = ConnectionInvokeType.None;
|
||||
|
||||
/// <summary>
|
||||
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private Exception _runingException ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -109,7 +110,7 @@ namespace Serein.Library
|
||||
{
|
||||
// 保存的参数信息项数量大于方法本身的方法入参数量(可能存在可变入参)
|
||||
var length = nodeInfo.ParameterData.Length - pds.Length; // 需要扩容的长度
|
||||
this.MethodDetails.ParameterDetailss = ArrayHelper.ArrayExpansion(pds, length); // 扩容入参描述数组
|
||||
this.MethodDetails.ParameterDetailss = ArrayHelper.Expansion(pds, length); // 扩容入参描述数组
|
||||
pds = this.MethodDetails.ParameterDetailss;
|
||||
var startParmsPd = pds[md.ParamsArgIndex]; // 获取可变入参参数描述
|
||||
for(int i = md.ParamsArgIndex + 1; i <= md.ParamsArgIndex + length; i++)
|
||||
@@ -144,6 +145,10 @@ namespace Serein.Library
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 程序集更新,更新节点方法描述、以及所有入参描述的类型
|
||||
|
||||
#endregion
|
||||
|
||||
#region 调试中断
|
||||
|
||||
/// <summary>
|
||||
@@ -256,7 +261,6 @@ namespace Serein.Library
|
||||
newFlowData = null;
|
||||
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
currentNode.RuningException = ex;
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +321,7 @@ namespace Serein.Library
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
||||
}
|
||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行到某个节点
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在对应委托");
|
||||
}
|
||||
@@ -700,4 +704,68 @@ namespace Serein.Library
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if false
|
||||
public static class NodeModelExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集更新,更新节点方法描述、以及所有入参描述的类型
|
||||
/// </summary>
|
||||
/// <param name="nodeModel">节点Model</param>
|
||||
/// <param name="newMd">新的方法描述</param>
|
||||
public static void UploadMethod(this NodeModelBase nodeModel, MethodDetails newMd)
|
||||
{
|
||||
var thisMd = nodeModel.MethodDetails;
|
||||
|
||||
thisMd.ActingInstanceType = newMd.ActingInstanceType; // 更新方法需要的类型
|
||||
|
||||
var thisPds = thisMd.ParameterDetailss;
|
||||
var newPds = newMd.ParameterDetailss;
|
||||
// 当前存在可变参数,且新的方法也存在可变参数,需要把可变参数的数目与值传递过去
|
||||
if (thisMd.HasParamsArg && newMd.HasParamsArg)
|
||||
{
|
||||
int paramsLength = thisPds.Length - thisMd.ParamsArgIndex - 1; // 确定扩容长度
|
||||
newMd.ParameterDetailss = ArrayHelper.Expansion(newPds, paramsLength);// 为新方法的入参参数描述进行扩容
|
||||
newPds = newMd.ParameterDetailss;
|
||||
int index = newMd.ParamsArgIndex; // 记录
|
||||
var templatePd = newPds[newMd.ParamsArgIndex]; // 新的入参模板
|
||||
for (int i = thisMd.ParamsArgIndex; i < thisPds.Length; i++)
|
||||
{
|
||||
ParameterDetails thisPd = thisPds[i];
|
||||
var newPd = templatePd.CloneOfModel(nodeModel); // 复制参数描述
|
||||
newPd.Index = i + 1; // 更新索引
|
||||
newPd.IsParams = true;
|
||||
newPd.DataValue = thisPd.DataValue; // 保留参数值
|
||||
newPd.ArgDataSourceNodeGuid = thisPd.ArgDataSourceNodeGuid; // 保留参数来源信息
|
||||
newPd.ArgDataSourceType = thisPd.ArgDataSourceType; // 保留参数来源信息
|
||||
newPd.IsParams = thisPd.IsParams; // 保留显式参数设置
|
||||
newPds[index++] = newPd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var thidPdLength = thisMd.HasParamsArg ? thisMd.ParamsArgIndex : thisPds.Length;
|
||||
// 遍历当前的参数描述(不包含可变参数),找到匹配项,复制必要的数据进行保留
|
||||
for (int i = 0; i < thisPds.Length; i++)
|
||||
{
|
||||
ParameterDetails thisPd = thisPds[i];
|
||||
var newPd = newPds.FirstOrDefault(t_newPd => !t_newPd.IsParams // 不为可变参数
|
||||
&& t_newPd.Name.Equals(thisPd.Name, StringComparison.OrdinalIgnoreCase) // 存在相同名称
|
||||
&& t_newPd.DataType.Name.Equals(thisPd.DataType.Name) // 存在相同入参类型名称(以类型作为区分)
|
||||
);
|
||||
if (newPd != null) // 如果匹配上了
|
||||
{
|
||||
newPd.DataValue = thisPd.DataValue; // 保留参数值
|
||||
newPd.ArgDataSourceNodeGuid = thisPd.ArgDataSourceNodeGuid; // 保留参数来源信息
|
||||
newPd.ArgDataSourceType = thisPd.ArgDataSourceType; // 保留参数来源信息
|
||||
newPd.IsParams = thisPd.IsParams; // 保留显式参数设置
|
||||
}
|
||||
}
|
||||
thisMd.ReturnType = newMd.ReturnType;
|
||||
nodeModel.MethodDetails = newMd;
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
108
Library/SereinBaseFunction.cs
Normal file
108
Library/SereinBaseFunction.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础功能
|
||||
/// </summary>
|
||||
|
||||
[DynamicFlow(Name ="[基础功能]")]
|
||||
public class SereinBaseFunction
|
||||
{
|
||||
//[NodeAction(NodeType.Action,"条件节点")]
|
||||
//private bool SereinConditionNode(IDynamicContext context,
|
||||
// object targetObject,
|
||||
// string exp = "ISPASS")
|
||||
//{
|
||||
// var isPass = SereinConditionParser.To(targetObject, exp);
|
||||
// context.NextOrientation = isPass ? ConnectionInvokeType.IsSucceed : ConnectionInvokeType.IsFail;
|
||||
// return isPass;
|
||||
//}
|
||||
|
||||
//[NodeAction(NodeType.Action, "表达式节点")]
|
||||
//private object SereinExpNode(IDynamicContext context,
|
||||
// object targetObject,
|
||||
// string exp)
|
||||
//{
|
||||
|
||||
// exp = "@" + exp;
|
||||
// var newData = SerinExpressionEvaluator.Evaluate(exp, targetObject, out bool isChange);
|
||||
// object result;
|
||||
// if (isChange || exp.StartsWith("@GET",System.StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// result = newData;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// result = targetObject;
|
||||
// }
|
||||
// context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||
// return result;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "键值对组装")]
|
||||
private Dictionary<string, object> SereinKvDataCollectionNode(/*NodeModelBase nodeModel, */
|
||||
string argName,
|
||||
params object[] value)
|
||||
{
|
||||
//var paramsArgIndex = nodeModel.MethodDetails.ParamsArgIndex;
|
||||
//var pds = nodeModel.MethodDetails.ParameterDetailss;
|
||||
//var length = pds.Length - paramsArgIndex;
|
||||
//for(int i = paramsArgIndex; i < pds.Length; i++)
|
||||
//{
|
||||
// var pd = pds[i];
|
||||
//}
|
||||
|
||||
var names = argName.Split(';');
|
||||
var count = Math.Min(value.Length, names.Length);
|
||||
var dict = new Dictionary<string, object>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dict[names[i]] = value[i];
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Action, "数组组装")]
|
||||
private object[] SereinListDataCollectionNode(params object[] value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Action, "输出")]
|
||||
private object[] SereinConsoleNode(params object[] value)
|
||||
{
|
||||
foreach (var item in value)
|
||||
{
|
||||
Console.WriteLine(item);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/* if (!DynamicObjectHelper.TryResolve(dict, className, out var result))
|
||||
{
|
||||
Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
|
||||
}
|
||||
else
|
||||
{
|
||||
DynamicObjectHelper.PrintObjectProperties(result);
|
||||
}
|
||||
//if (!ObjDynamicCreateHelper.TryResolve(externalData, "RootType", out var result))
|
||||
//{
|
||||
// Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
|
||||
|
||||
//}
|
||||
//ObjDynamicCreateHelper.PrintObjectProperties(result!);
|
||||
return result;*/
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ namespace Serein.Library.Utils
|
||||
/// <param name="length">扩容长度</param>
|
||||
/// <returns>新的数组</returns>
|
||||
/// <exception cref="Exception"> length 传入负值</exception>
|
||||
public static T[] ArrayExpansion<T>(T[] original, int length)
|
||||
public static T[] Expansion<T>(T[] original, int length)
|
||||
{
|
||||
if(length == 0)
|
||||
{
|
||||
@@ -58,7 +58,10 @@ namespace Serein.Library.Utils
|
||||
public static T[] AddToArray<T>(T[] original, T newObject)
|
||||
{
|
||||
// 创建一个新数组,比原数组大1
|
||||
T[] newArray = ArrayHelper.ArrayExpansion(original, 1);
|
||||
T[] newArray = ArrayHelper.Expansion(original, 1);
|
||||
|
||||
original.CopyTo(newArray, 0);
|
||||
|
||||
// 将新对象放在最后一位
|
||||
newArray[newArray.Length - 1] = newObject;
|
||||
return newArray;
|
||||
|
||||
303
Library/Utils/DynamicObjectHelper.cs
Normal file
303
Library/Utils/DynamicObjectHelper.cs
Normal file
@@ -0,0 +1,303 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Utils
|
||||
{
|
||||
public class DynamicObjectHelper
|
||||
{
|
||||
// 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式)
|
||||
static Dictionary<string, Type> typeCache = new Dictionary<string, Type>();
|
||||
|
||||
public static object Resolve(IDictionary<string, object> properties, string typeName)
|
||||
{
|
||||
var obj = CreateObjectWithProperties(properties, typeName);
|
||||
//SetPropertyValues(obj, properties);
|
||||
return obj;
|
||||
}
|
||||
public static bool TryResolve(IDictionary<string, object> properties, string typeName, out object result)
|
||||
{
|
||||
result = CreateObjectWithProperties(properties, typeName);
|
||||
bool success = SetPropertyValuesWithValidation(result, properties);
|
||||
return success;
|
||||
// 打印赋值结果
|
||||
|
||||
}
|
||||
// 递归方法:打印对象属性及类型
|
||||
public static void PrintObjectProperties(object obj, string indent = "")
|
||||
{
|
||||
var objType = obj.GetType();
|
||||
foreach (var prop in objType.GetProperties())
|
||||
{
|
||||
var value = prop.GetValue(obj);
|
||||
Console.WriteLine($"{indent}{prop.Name} (Type: {prop.PropertyType.Name}): {value}");
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
if (prop.PropertyType.IsArray) // 处理数组类型
|
||||
{
|
||||
var array = (Array)value;
|
||||
Console.WriteLine($"{indent}{prop.Name} is an array with {array.Length} elements:");
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
var element = array.GetValue(i);
|
||||
if (element != null && element.GetType().IsClass && !(element is string))
|
||||
{
|
||||
Console.WriteLine($"{indent}\tArray[{i}] (Type: {element.GetType().Name}) contains a nested object:");
|
||||
PrintObjectProperties(element, indent + "\t\t");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{indent}\tArray[{i}] (Type: {element?.GetType().Name}): {element}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (value.GetType().IsClass && !(value is string)) // 处理嵌套对象
|
||||
{
|
||||
Console.WriteLine($"{indent}{prop.Name} contains a nested object:");
|
||||
PrintObjectProperties(value, indent + "\t");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 方法 1: 创建动态类型及其对象实例
|
||||
public static object CreateObjectWithProperties(IDictionary<string, object> properties, string typeName)
|
||||
{
|
||||
// 如果类型已经缓存,直接返回缓存的类型
|
||||
if (typeCache.ContainsKey(typeName))
|
||||
{
|
||||
return Activator.CreateInstance(typeCache[typeName]);
|
||||
}
|
||||
|
||||
// 定义动态程序集和模块
|
||||
var assemblyName = new AssemblyName("DynamicAssembly");
|
||||
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
|
||||
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
|
||||
|
||||
// 定义动态类型
|
||||
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
|
||||
|
||||
// 为每个属性名和值添加相应的属性到动态类型中
|
||||
foreach (var kvp in properties)
|
||||
{
|
||||
string propName = kvp.Key;
|
||||
object propValue = kvp.Value;
|
||||
Type propType;
|
||||
|
||||
if (propValue is IList<Dictionary<string, object>>) // 处理数组类型
|
||||
{
|
||||
var nestedPropValue = (propValue as IList<Dictionary<string, object>>)[0];
|
||||
var nestedType = CreateObjectWithProperties(nestedPropValue, $"{propName}Element");
|
||||
propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
|
||||
}
|
||||
else if (propValue is Dictionary<string, object> nestedProperties)
|
||||
{
|
||||
// 如果值是嵌套的字典,递归创建嵌套类型
|
||||
propType = CreateObjectWithProperties(nestedProperties, $"{typeName}_{propName}").GetType();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果是普通类型,使用值的类型
|
||||
propType = propValue?.GetType() ?? typeof(object);
|
||||
}
|
||||
|
||||
// 定义私有字段和公共属性
|
||||
var fieldBuilder = typeBuilder.DefineField("_" + propName, propType, FieldAttributes.Private);
|
||||
var propertyBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.HasDefault, propType, null);
|
||||
|
||||
// 定义 getter 方法
|
||||
var getMethodBuilder = typeBuilder.DefineMethod(
|
||||
"get_" + propName,
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
propType,
|
||||
Type.EmptyTypes);
|
||||
|
||||
var getIL = getMethodBuilder.GetILGenerator();
|
||||
getIL.Emit(OpCodes.Ldarg_0);
|
||||
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
|
||||
getIL.Emit(OpCodes.Ret);
|
||||
|
||||
// 定义 setter 方法
|
||||
var setMethodBuilder = typeBuilder.DefineMethod(
|
||||
"set_" + propName,
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
null,
|
||||
new Type[] { propType });
|
||||
|
||||
var setIL = setMethodBuilder.GetILGenerator();
|
||||
setIL.Emit(OpCodes.Ldarg_0);
|
||||
setIL.Emit(OpCodes.Ldarg_1);
|
||||
setIL.Emit(OpCodes.Stfld, fieldBuilder);
|
||||
setIL.Emit(OpCodes.Ret);
|
||||
|
||||
// 将 getter 和 setter 方法添加到属性
|
||||
propertyBuilder.SetGetMethod(getMethodBuilder);
|
||||
propertyBuilder.SetSetMethod(setMethodBuilder);
|
||||
}
|
||||
|
||||
// 创建类型并缓存
|
||||
var dynamicType = typeBuilder.CreateType();
|
||||
typeCache[typeName] = dynamicType;
|
||||
|
||||
// 创建对象实例
|
||||
return Activator.CreateInstance(dynamicType);
|
||||
}
|
||||
|
||||
// 方法 2: 递归设置对象的属性值
|
||||
public static void SetPropertyValues(object obj, Dictionary<string, object> properties)
|
||||
{
|
||||
var objType = obj.GetType();
|
||||
|
||||
foreach (var kvp in properties)
|
||||
{
|
||||
var propInfo = objType.GetProperty(kvp.Key);
|
||||
object value = kvp.Value;
|
||||
|
||||
// 如果值是嵌套的字典类型,递归处理嵌套对象
|
||||
if (value is Dictionary<string, object> nestedProperties)
|
||||
{
|
||||
// 创建嵌套对象
|
||||
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
|
||||
|
||||
// 递归设置嵌套对象的值
|
||||
SetPropertyValues(nestedObj, nestedProperties);
|
||||
|
||||
// 将嵌套对象赋值给属性
|
||||
propInfo.SetValue(obj, nestedObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 直接赋值给属性
|
||||
propInfo.SetValue(obj, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 方法 2: 递归设置对象的属性值(带验证)
|
||||
|
||||
public static bool SetPropertyValuesWithValidation(object obj, IDictionary<string, object> properties)
|
||||
{
|
||||
var objType = obj.GetType();
|
||||
bool allSuccessful = true; // 标记是否所有属性赋值成功
|
||||
|
||||
foreach (var kvp in properties)
|
||||
{
|
||||
var propName = kvp.Key;
|
||||
var propValue = kvp.Value;
|
||||
|
||||
var propInfo = objType.GetProperty(propName);
|
||||
|
||||
if (propInfo == null)
|
||||
{
|
||||
// 属性不存在,打印警告并标记失败
|
||||
Console.WriteLine($"Warning: 属性 '{propName}' 不存在于类型 '{objType.Name}' 中,跳过赋值。");
|
||||
allSuccessful = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查属性类型是否与要赋的值兼容
|
||||
var targetType = propInfo.PropertyType;
|
||||
if (!IsCompatibleType(targetType, propValue))
|
||||
{
|
||||
// 如果类型不兼容,打印错误并标记失败
|
||||
Console.WriteLine($"Error: 无法将类型 '{propValue?.GetType().Name}' 赋值给属性 '{propName}' (Type: {targetType.Name}),跳过赋值。");
|
||||
allSuccessful = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 如果值是一个嵌套对象,递归赋值
|
||||
if (propValue is Dictionary<string, object> nestedProperties)
|
||||
{
|
||||
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
|
||||
if (nestedObj != null && SetPropertyValuesWithValidation(nestedObj, nestedProperties))
|
||||
{
|
||||
propInfo.SetValue(obj, nestedObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
allSuccessful = false; // 嵌套赋值失败
|
||||
}
|
||||
}
|
||||
else if (propValue is IList<Dictionary<string, object>> list) // 处理列表
|
||||
{
|
||||
// 获取目标类型的数组元素类型
|
||||
var elementType = propInfo.PropertyType.GetElementType();
|
||||
if (elementType != null)
|
||||
{
|
||||
var array = Array.CreateInstance(elementType, list.Count);
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
var item = Activator.CreateInstance(elementType);
|
||||
if (item != null && SetPropertyValuesWithValidation(item, list[i]))
|
||||
{
|
||||
array.SetValue(item, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
allSuccessful = false; // 赋值失败
|
||||
}
|
||||
}
|
||||
propInfo.SetValue(obj, array);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 直接赋值
|
||||
propInfo.SetValue(obj, propValue);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: 为属性 '{propName}' 赋值时发生异常:{ex.Message}");
|
||||
allSuccessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
return allSuccessful;
|
||||
}
|
||||
|
||||
// 检查类型兼容性的方法(支持嵌套类型)
|
||||
static bool IsCompatibleType(Type targetType, object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
// 如果值为null,且目标类型是引用类型或者可空类型,则兼容
|
||||
return !targetType.IsValueType || Nullable.GetUnderlyingType(targetType) != null;
|
||||
}
|
||||
|
||||
// 检查值的类型是否与目标类型相同,或是否可以转换为目标类型
|
||||
if (targetType.IsAssignableFrom(value.GetType()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 处理数组类型
|
||||
if (targetType.IsArray)
|
||||
{
|
||||
// 检查数组的元素类型与值的类型兼容
|
||||
var elementType = targetType.GetElementType();
|
||||
return value is IList<Dictionary<string, object>>; // 假设值是一个列表,具体处理逻辑在赋值时
|
||||
}
|
||||
|
||||
// 处理嵌套类型的情况
|
||||
if (value is Dictionary<string, object> && targetType.IsClass && !targetType.IsPrimitive)
|
||||
{
|
||||
// 如果目标类型是一个复杂对象,并且值是一个字典,可能是嵌套对象
|
||||
return true; // 假设可以递归处理嵌套对象
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
|
||||
public static T Evaluate(string expression, T inputValue)
|
||||
{
|
||||
|
||||
|
||||
// 替换占位符@为输入值
|
||||
expression = expression.Replace("@", inputValue.ToString());
|
||||
try
|
||||
@@ -51,7 +51,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
public static object Evaluate(string expression, object targetObJ, out bool isChange)
|
||||
{
|
||||
if(expression is null || targetObJ is null)
|
||||
if (expression is null || targetObJ is null)
|
||||
{
|
||||
throw new Exception("表达式条件expression is null、 targetObJ is null");
|
||||
}
|
||||
@@ -88,13 +88,13 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
throw new NotSupportedException($"Operation {operation} is not supported.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static readonly char[] separator = new char[] { '(', ')' };
|
||||
private static readonly char[] separator = new char[] { '(', ')' };
|
||||
private static readonly char[] separatorArray = new char[] { ',' };
|
||||
|
||||
/// <summary>
|
||||
@@ -128,6 +128,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
return method.Invoke(target, parameterValues);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取值
|
||||
/// </summary>
|
||||
@@ -156,56 +157,53 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
}
|
||||
|
||||
var targetType = target?.GetType(); // 目标对象的类型
|
||||
if(targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
#region 处理键值对
|
||||
if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
{
|
||||
|
||||
var typetmp = target.GetType().FullName;
|
||||
// 目标是键值对
|
||||
var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
|
||||
var method = targetType.GetMethod("get_Item", BindingFlags.Public | BindingFlags.Instance);
|
||||
if(method != null)
|
||||
if (method != null)
|
||||
{
|
||||
var result = method.Invoke(target, new object[] { indexStr });
|
||||
if(result != null)
|
||||
var result = method.Invoke(target, new object[] { indexStr });
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
target = result;
|
||||
}
|
||||
}
|
||||
|
||||
//var dict = target as Dictionary<string, string>;
|
||||
////var dict = (Dictionary<dynamic, dynamic>)target;
|
||||
//var temp = dict[indexStr];
|
||||
////if (target is Dictionary<object, object> dict)
|
||||
////{
|
||||
//// var temp = dict[indexStr];
|
||||
////}
|
||||
//var TMP2= target.GetType().GetEnumValues();
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
else
|
||||
{
|
||||
|
||||
#region 表达式处理集合对象
|
||||
// 获取数组或集合对象
|
||||
var arrayProperty = target?.GetType().GetProperty(arrayName);
|
||||
if (arrayProperty is null)
|
||||
|
||||
// 如果arrayName为空,说明target可能是数组,而不需要再获取属性了
|
||||
if (!string.IsNullOrEmpty(arrayName))
|
||||
{
|
||||
var arrayField = target?.GetType().GetField(arrayName);
|
||||
if (arrayField is null)
|
||||
var arrayProperty = target?.GetType().GetProperty(arrayName);
|
||||
if (arrayProperty is null)
|
||||
{
|
||||
throw new ArgumentException($"Member {arrayName} not found on target.");
|
||||
var arrayField = target?.GetType().GetField(arrayName);
|
||||
if (arrayField is null)
|
||||
{
|
||||
throw new ArgumentException($"Member {arrayName} not found on target.");
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayField.GetValue(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayField.GetValue(target);
|
||||
target = arrayProperty.GetValue(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayProperty.GetValue(target);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 提取数组索引
|
||||
var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
|
||||
if (!int.TryParse(indexStr, out int index))
|
||||
@@ -236,7 +234,6 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -288,7 +285,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
|
||||
// 检查是否包含数组索引
|
||||
var arrayIndexStart = member.IndexOf('[');
|
||||
if (arrayIndexStart != -1)
|
||||
if (arrayIndexStart != -1)
|
||||
{
|
||||
// 解析数组名和索引
|
||||
var arrayName = member.Substring(0, arrayIndexStart);
|
||||
@@ -394,6 +391,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算数学简单表达式
|
||||
/// </summary>
|
||||
@@ -408,7 +406,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
private static T ComputedNumber<T>(object value, string expression) where T : struct, IComparable<T>
|
||||
{
|
||||
T result = value.ToConvert<T>();
|
||||
return SerinArithmeticExpressionEvaluator<T>.Evaluate(expression, result);
|
||||
return SerinArithmeticExpressionEvaluator<T>.Evaluate(expression, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -184,7 +184,7 @@ namespace Serein.NodeFlow
|
||||
//object?[]? args = [Context];
|
||||
foreach (var md in initMethods) // 初始化
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
@@ -205,7 +205,7 @@ namespace Serein.NodeFlow
|
||||
{
|
||||
//object?[]? data = [md.ActingInstance, args];
|
||||
//md.MethodDelegate.DynamicInvoke(data);
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行正在加载
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
@@ -228,7 +228,7 @@ namespace Serein.NodeFlow
|
||||
|
||||
foreach (MethodDetails? md in exitMethods)
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行退出执行
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
|
||||
@@ -53,40 +53,36 @@ namespace Serein.NodeFlow.Model
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
{
|
||||
// 条件区域中遍历每个条件节点
|
||||
foreach (SingleConditionNode? node in ConditionNodes)
|
||||
{
|
||||
var state = await JudgeAsync(context, node);
|
||||
context.NextOrientation = state; // 每次判读完成后,设置区域后继方向为判断结果
|
||||
if (state != ConnectionInvokeType.IsSucceed)
|
||||
{
|
||||
// 如果条件不通过,立刻推出循环
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//var previousNode = context.GetPreviousNode()
|
||||
return Task.FromResult(context.TransmissionData(this)); // 条件区域透传上一节点的数据
|
||||
}
|
||||
|
||||
|
||||
private async Task<ConnectionInvokeType> JudgeAsync(IDynamicContext context, SingleConditionNode node)
|
||||
{
|
||||
try
|
||||
{
|
||||
await node.ExecutingAsync(context);
|
||||
return context.NextOrientation;
|
||||
// 条件区域中遍历每个条件节点
|
||||
foreach (SingleConditionNode? node in ConditionNodes)
|
||||
{
|
||||
var state = await node.ExecutingAsync(context);
|
||||
if (context.NextOrientation != ConnectionInvokeType.IsSucceed)
|
||||
{
|
||||
// 如果条件不通过,立刻推出循环
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//var previousNode = context.GetPreviousNode()
|
||||
return context.TransmissionData(this); // 条件区域透传上一节点的数据
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
RuningException = ex;
|
||||
return ConnectionInvokeType.IsError;
|
||||
context.ExceptionOfRuning = ex;
|
||||
return context.TransmissionData(this); // 条件区域透传上一节点的数据
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override ParameterData[] GetParameterdatas()
|
||||
{
|
||||
return [];
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace Serein.NodeFlow.Model
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
RuningException = ex;
|
||||
context.ExceptionOfRuning = ex;
|
||||
}
|
||||
|
||||
Console.WriteLine($"{result} {Expression} -> " + context.NextOrientation);
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Serein.NodeFlow.Model
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
RuningException = ex;
|
||||
context.ExceptionOfRuning = ex;
|
||||
return parameter;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Serein.NodeFlow.Model
|
||||
#endregion
|
||||
|
||||
MethodDetails md = MethodDetails;
|
||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行到某个节点
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
@@ -72,14 +72,14 @@ namespace Serein.NodeFlow.Model
|
||||
}
|
||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
||||
context.NextOrientation = ConnectionInvokeType.None;
|
||||
RuningException = ex;
|
||||
context.ExceptionOfRuning = ex;
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
RuningException = ex;
|
||||
context.ExceptionOfRuning = ex;
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -13,36 +13,35 @@ namespace Serein.NodeFlow
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// 加载在流程中的程序集依赖
|
||||
/// </summary>
|
||||
public class FlowLibrary
|
||||
{
|
||||
private readonly Assembly assembly;
|
||||
private readonly Action actionOfUnloadAssmbly;
|
||||
private string _assemblyFilePath;
|
||||
|
||||
public FlowLibrary(string assemblyFilePath,
|
||||
Assembly assembly,
|
||||
public FlowLibrary(Assembly assembly,
|
||||
Action actionOfUnloadAssmbly)
|
||||
{
|
||||
this._assemblyFilePath = assemblyFilePath;
|
||||
this.assembly = assembly;
|
||||
this.actionOfUnloadAssmbly = actionOfUnloadAssmbly;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public string FullName => assembly.GetName().FullName;
|
||||
|
||||
public string Version => assembly.GetName().Version.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 加载程序集时创建的方法描述
|
||||
/// Key : 方法名称
|
||||
/// Value :方法详情
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, MethodDetails> MethodDetailss { get; } = new ConcurrentDictionary<string, MethodDetails>();
|
||||
|
||||
/// <summary>
|
||||
/// 管理通过Emit动态构建的委托
|
||||
/// Key :方法名称
|
||||
/// Value :方法详情
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, DelegateDetails> DelegateDetailss { get; } = new ConcurrentDictionary<string, DelegateDetails>();
|
||||
|
||||
@@ -57,56 +56,24 @@ namespace Serein.NodeFlow
|
||||
/// </summary>
|
||||
public void Upload()
|
||||
{
|
||||
DelegateDetailss.Clear();
|
||||
RegisterTypes.Clear();
|
||||
MethodDetailss.Clear();
|
||||
actionOfUnloadAssmbly?.Invoke();
|
||||
|
||||
}
|
||||
|
||||
public NodeLibraryInfo ToInfo()
|
||||
{
|
||||
return new NodeLibraryInfo
|
||||
{
|
||||
AssemblyName = FullName,
|
||||
FileName = Path.GetFileName(_assemblyFilePath),
|
||||
FilePath = _assemblyFilePath,
|
||||
AssemblyName = assembly.GetName().Name,
|
||||
FileName = Path.GetFileName(assembly.Location),
|
||||
FilePath = assembly.Location,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//public void LoadAssmely(Assembly assembly)
|
||||
//{
|
||||
// (var registerTypes, var mdlist) = LoadAssembly2(assembly);
|
||||
// if (mdlist.Count > 0)
|
||||
// {
|
||||
// var nodeLibraryInfo = new NodeLibraryInfo
|
||||
// {
|
||||
// //Assembly = assembly,
|
||||
// AssemblyName = assembly.FullName,
|
||||
// FileName = Path.GetFileName(_assemblyFilePath),
|
||||
// FilePath = _assemblyFilePath,
|
||||
// };
|
||||
|
||||
// //LibraryInfos.TryAdd(nodeLibraryInfo.AssemblyName, nodeLibraryInfo);
|
||||
|
||||
// MethodDetailss.TryAdd(nodeLibraryInfo.AssemblyName, mdlist);
|
||||
|
||||
// foreach (var md in mdlist)
|
||||
// {
|
||||
// MethodDetailss.TryAdd(md.MethodName, md);
|
||||
// }
|
||||
|
||||
// foreach (var kv in registerTypes)
|
||||
// {
|
||||
// if (!RegisterTypes.TryGetValue(kv.Key, out var types))
|
||||
// {
|
||||
// types = new List<Type>();
|
||||
// RegisterTypes.TryAdd(kv.Key, types);
|
||||
// }
|
||||
// types.AddRange(kv.Value);
|
||||
// }
|
||||
// var mdInfos = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 动态加载程序集
|
||||
/// </summary>
|
||||
@@ -158,18 +125,13 @@ namespace Serein.NodeFlow
|
||||
// (Type, string)
|
||||
// Type : 具有 DynamicFlowAttribute 标记的类型
|
||||
// string : 类型元数据 DynamicFlowAttribute 特性中的 Name 属性
|
||||
|
||||
types = types.Where(type => type.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute dynamicFlowAttribute
|
||||
&& dynamicFlowAttribute.Scan).ToList();
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (type.Name.Equals("YoloFlowControl"))
|
||||
{
|
||||
//var ab = type.GetCustomAttribute<DynamicFlowAttribute>();
|
||||
//var attributes = assembly.GetCustomAttributes();
|
||||
//foreach (var attribute in attributes)
|
||||
//{
|
||||
// Console.WriteLine(attribute.GetType().Name);
|
||||
//}
|
||||
}
|
||||
if (type.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute dynamicFlowAttribute && dynamicFlowAttribute.Scan == true)
|
||||
if (type.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute dynamicFlowAttribute)
|
||||
{
|
||||
scanTypes.Add((type, dynamicFlowAttribute.Name));
|
||||
}
|
||||
|
||||
@@ -31,25 +31,34 @@ namespace Serein.NodeFlow.Tool
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, FlowLibrary> _myFlowLibrarys = new ConcurrentDictionary<string, FlowLibrary>();
|
||||
|
||||
public (NodeLibraryInfo, List<MethodDetailsInfo>) LoadLibrary(string libraryfilePath)
|
||||
/// <summary>
|
||||
/// 加载类库
|
||||
/// </summary>
|
||||
/// <param name="libraryfilePath"></param>
|
||||
/// <returns></returns>
|
||||
public (NodeLibraryInfo, List<MethodDetailsInfo>) LoadLibraryOfPath(string libraryfilePath)
|
||||
{
|
||||
|
||||
return LoadDllNodeInfo(libraryfilePath);
|
||||
}
|
||||
|
||||
public bool UnloadLibrary(string libraryName)
|
||||
/// <summary>
|
||||
/// 卸载类库
|
||||
/// </summary>
|
||||
/// <param name="assemblyName"></param>
|
||||
/// <returns></returns>
|
||||
public bool UnloadLibrary(string assemblyName)
|
||||
{
|
||||
libraryName = libraryName.Split(',')[0];
|
||||
if (_myFlowLibrarys.Remove(libraryName, out var flowLibrary))
|
||||
if (_myFlowLibrarys.Remove(assemblyName, out var flowLibrary))
|
||||
{
|
||||
try
|
||||
{
|
||||
flowLibrary.Upload();
|
||||
flowLibrary.Upload(); // 尝试卸载
|
||||
flowLibrary = null;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"尝试卸载程序集[{libraryName}]发生错误:{ex}");
|
||||
Console.WriteLine($"尝试卸载程序集[{assemblyName}]发生错误:{ex}");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -144,6 +153,20 @@ namespace Serein.NodeFlow.Tool
|
||||
return rsTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取某个程序集下的所有方法信息,用于保存项目时调用
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<MethodDetails> GetLibraryMdsOfAssmbly(string assemblyName)
|
||||
{
|
||||
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary))
|
||||
{
|
||||
return flowLibrary.MethodDetailss.Values.ToList();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有方法信息,用于保存项目时调用
|
||||
@@ -165,9 +188,6 @@ namespace Serein.NodeFlow.Tool
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 序列化当前项目的依赖信息、节点信息,用于远程登录的场景,需要将依赖信息从本地(受控端)发送到远程(主控端)
|
||||
/// </summary>
|
||||
@@ -180,18 +200,38 @@ namespace Serein.NodeFlow.Tool
|
||||
|
||||
#region 功能性方法
|
||||
|
||||
/// <summary>
|
||||
/// 从文件路径中加载程序集,返回相应的信息
|
||||
/// </summary>
|
||||
/// <param name="dllFilePath"></param>
|
||||
/// <returns></returns>
|
||||
private readonly string SereinLibraryDll = $"{nameof(Serein)}.{nameof(Serein.Library)}.dll";
|
||||
|
||||
private (NodeLibraryInfo, List<MethodDetailsInfo>) LoadDllNodeInfo(string dllFilePath)
|
||||
{
|
||||
|
||||
#if true
|
||||
var fileName = Path.GetFileName(dllFilePath); // 获取文件名
|
||||
var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径
|
||||
var sereinFlowLibraryPath = Path.Combine(dir, $"{nameof(Serein)}.{nameof(Serein.Library)}.dll");
|
||||
|
||||
if (SereinLibraryDll.Equals(fileName))
|
||||
{
|
||||
|
||||
return LoadAssembly(typeof(IFlowEnvironment).Assembly, () => {
|
||||
//Console.WriteLine("基础模块不能卸载");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径
|
||||
var sereinFlowLibraryPath = Path.Combine(dir, SereinLibraryDll);
|
||||
// 每个类库下面至少需要有“Serein.Library.dll”类库依赖
|
||||
var flowAlc = new FlowLibraryAssemblyContext(sereinFlowLibraryPath, fileName);
|
||||
Action actionUnload = () =>
|
||||
{
|
||||
flowAlc?.Unload(); // 卸载程序集
|
||||
flowAlc = null;
|
||||
GC.Collect(); // 强制触发GC确保卸载成功
|
||||
GC.WaitForPendingFinalizers();
|
||||
};
|
||||
var assembly = flowAlc.LoadFromAssemblyPath(dllFilePath); // 加载指定路径的程序集
|
||||
return LoadAssembly(assembly, actionUnload);
|
||||
}
|
||||
|
||||
/* var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径
|
||||
var sereinFlowLibraryPath = Path.Combine(dir, SereinLibraryDll);
|
||||
// 每个类库下面至少需要有“Serein.Library.dll”类库依赖
|
||||
var flowAlc = new FlowLibraryAssemblyContext(sereinFlowLibraryPath, fileName);
|
||||
Action actionUnload = () =>
|
||||
@@ -207,8 +247,29 @@ namespace Serein.NodeFlow.Tool
|
||||
actionUnload.Invoke();
|
||||
throw new Exception($"程序集[{assembly.GetName().FullName}]已经加载过!");
|
||||
}
|
||||
FlowLibrary flowLibrary = new FlowLibrary(assembly, actionUnload);
|
||||
if (flowLibrary.LoadAssembly(assembly))
|
||||
{
|
||||
_myFlowLibrarys.TryAdd(assembly.GetName().Name, flowLibrary);
|
||||
(NodeLibraryInfo, List<MethodDetailsInfo>) result = (flowLibrary.ToInfo(),
|
||||
flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList());
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败");
|
||||
}*/
|
||||
}
|
||||
|
||||
FlowLibrary flowLibrary = new FlowLibrary(dllFilePath, assembly, actionUnload);
|
||||
private (NodeLibraryInfo, List<MethodDetailsInfo>) LoadAssembly(Assembly assembly,Action actionUnload)
|
||||
{
|
||||
if (_myFlowLibrarys.ContainsKey(assembly.GetName().Name))
|
||||
{
|
||||
actionUnload.Invoke();
|
||||
throw new Exception($"程序集[{assembly.GetName().FullName}]已经加载过!");
|
||||
}
|
||||
|
||||
FlowLibrary flowLibrary = new FlowLibrary(assembly, actionUnload);
|
||||
if (flowLibrary.LoadAssembly(assembly))
|
||||
{
|
||||
_myFlowLibrarys.TryAdd(assembly.GetName().Name, flowLibrary);
|
||||
@@ -220,29 +281,6 @@ namespace Serein.NodeFlow.Tool
|
||||
{
|
||||
throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败");
|
||||
}
|
||||
|
||||
#else
|
||||
var fileName = Path.GetFileName(dllFilePath); // 获取文件名
|
||||
Assembly assembly = Assembly.LoadFrom(dllFilePath); // 加载程序集
|
||||
FlowLibrary flowLibrary = new FlowLibrary(dllFilePath, assembly, () =>
|
||||
{
|
||||
Console.WriteLine("暂未实现卸载程序集");
|
||||
//flowAlc.Unload(); // 卸载程序集
|
||||
//flowAlc = null;
|
||||
//GC.Collect(); // 强制触发GC确保卸载成功
|
||||
//GC.WaitForPendingFinalizers();
|
||||
});
|
||||
|
||||
_myFlowLibrarys.TryAdd(assembly.GetName().Name, flowLibrary);
|
||||
|
||||
(NodeLibraryInfo, List<MethodDetailsInfo>) result = (flowLibrary.ToInfo(),
|
||||
flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList());
|
||||
return result;
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -281,6 +319,7 @@ namespace Serein.NodeFlow.Tool
|
||||
return Default.Assemblies.FirstOrDefault(x => x.FullName == assemblyName.FullName);
|
||||
}
|
||||
|
||||
// return null;
|
||||
|
||||
// 构建依赖项的路径
|
||||
//string assemblyPath = Path.Combine(AppContext.BaseDirectory, assemblyName.Name + ".dll");
|
||||
|
||||
@@ -60,8 +60,7 @@ public static class NodeMethodDetailsHelper
|
||||
// method.GetParameters(),// 方法参数
|
||||
// method.ReturnType);// 返回值
|
||||
|
||||
//// 通过表达式树生成委托
|
||||
var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var methodDelegate);// 返回值
|
||||
|
||||
|
||||
Type? returnType;
|
||||
bool isTask = IsGenericTask(methodInfo.ReturnType, out var taskResult);
|
||||
@@ -137,7 +136,11 @@ public static class NodeMethodDetailsHelper
|
||||
// 如果存在可变参数,取最后一个元素的下标,否则为-1;
|
||||
ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length - 1 : -1,
|
||||
};
|
||||
var dd = new DelegateDetails(emitMethodType, methodDelegate) ;
|
||||
|
||||
//var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var methodDelegate);// 返回值
|
||||
|
||||
|
||||
var dd = new DelegateDetails(methodInfo) ; // 构造委托
|
||||
|
||||
methodDetails = md;
|
||||
delegateDetails = dd;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Serein.BaseNode
|
||||
{
|
||||
@@ -10,7 +15,7 @@ namespace Serein.BaseNode
|
||||
Get,
|
||||
Set
|
||||
}
|
||||
[DynamicFlow(Name ="基础节点")]
|
||||
[DynamicFlow(Name ="[基础节点]")]
|
||||
internal class SereinBaseNodes
|
||||
{
|
||||
[NodeAction(NodeType.Action,"条件节点")]
|
||||
@@ -48,5 +53,39 @@ namespace Serein.BaseNode
|
||||
}
|
||||
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "KV数据收集节点")]
|
||||
private Dictionary<string, object> SereinKvDataCollectionNode(string argName, params object[] value)
|
||||
{
|
||||
var names = argName.Split(';');
|
||||
var count = Math.Min(value.Length, names.Length);
|
||||
var dict = new Dictionary<string, object>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dict[names[i]] = value[i];
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
[NodeAction(NodeType.Action, "List数据收集节点")]
|
||||
private object[] SereinListDataCollectionNode(params object[] value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
/* if (!DynamicObjectHelper.TryResolve(dict, className, out var result))
|
||||
{
|
||||
Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
|
||||
}
|
||||
else
|
||||
{
|
||||
DynamicObjectHelper.PrintObjectProperties(result);
|
||||
}
|
||||
//if (!ObjDynamicCreateHelper.TryResolve(externalData, "RootType", out var result))
|
||||
//{
|
||||
// Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
|
||||
|
||||
//}
|
||||
//ObjDynamicCreateHelper.PrintObjectProperties(result!);
|
||||
return result;*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,7 @@ namespace Serein.Library.NodeGenerator
|
||||
sb.AppendLine($"using System.Threading;");
|
||||
sb.AppendLine($"using System.Threading.Tasks;");
|
||||
sb.AppendLine($"using System.Collections.Concurrent;");
|
||||
sb.AppendLine($"using System.Collections.Generic;");
|
||||
sb.AppendLine($"using Serein.Library;");
|
||||
sb.AppendLine($"using Serein.Library.Api;");
|
||||
sb.AppendLine($"using static Serein.Library.Utils.ChannelFlowInterrupt;");
|
||||
|
||||
@@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.FlowStartTool", "Flo
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library.NodeGenerator", "Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj", "{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.BaseNode", "Serein.BaseNode\Serein.BaseNode.csproj", "{E6C9C6F1-1BA5-4220-A7A4-ED905BB8B54F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -62,6 +64,10 @@ Global
|
||||
{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E6C9C6F1-1BA5-4220-A7A4-ED905BB8B54F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E6C9C6F1-1BA5-4220-A7A4-ED905BB8B54F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E6C9C6F1-1BA5-4220-A7A4-ED905BB8B54F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E6C9C6F1-1BA5-4220-A7A4-ED905BB8B54F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Serein.Workbench
|
||||
filePath = @"F:\临时\project\linux\project.dnf";
|
||||
filePath = @"F:\临时\project\linux\http\project.dnf";
|
||||
filePath = @"F:\临时\project\yolo flow\project.dnf";
|
||||
filePath = @"F:\临时\project\data\project.dnf";
|
||||
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
|
||||
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
||||
App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
|
||||
|
||||
@@ -78,17 +78,6 @@
|
||||
<!--<RowDefinition Height="3*"></RowDefinition>-->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!--<Grid Margin="2,2,1,5" Grid.Row="0" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Column="0" Content="保存项目" Click="ButtonSaveFile_Click" HorizontalAlignment="Left" Margin="5,5,5,5"/>
|
||||
--><!--<Button Grid.Column="1" Content="加载项目" Click="ButtonLoadProjectFile_Click" HorizontalAlignment="Left" Margin="5,5,5,5"/>-->
|
||||
<!--<Button Grid.Row="0" Content="卸载清空" Click="UnloadAllButton_Click" HorizontalAlignment="Right" Margin="5,5,5,5"/>--><!--
|
||||
</Grid>-->
|
||||
|
||||
<!--暂时隐藏基础面板 Visibility="Collapsed" -->
|
||||
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
@@ -361,7 +361,7 @@ namespace Serein.Workbench
|
||||
/// 节点连接关系变更
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs)
|
||||
private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs)
|
||||
{
|
||||
string fromNodeGuid = eventArgs.FromNodeGuid;
|
||||
string toNodeGuid = eventArgs.ToNodeGuid;
|
||||
@@ -395,16 +395,17 @@ namespace Serein.Workbench
|
||||
FlowChartCanvas,
|
||||
connectionType,
|
||||
startJunction,
|
||||
endJunction,
|
||||
() => EnvDecorator.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, connectionType)
|
||||
endJunction
|
||||
);
|
||||
|
||||
//() => EnvDecorator.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, connectionType)
|
||||
|
||||
if (toNodeControl is FlipflopNodeControl flipflopControl
|
||||
&& flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
|
||||
{
|
||||
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
|
||||
}
|
||||
connection.RefreshLine(); // 添加贝塞尔曲线显示
|
||||
//connection.RefreshLine(); // 添加贝塞尔曲线显示
|
||||
Connections.Add(connection);
|
||||
fromNodeControl.AddCnnection(connection);
|
||||
toNodeControl.AddCnnection(connection);
|
||||
@@ -427,10 +428,9 @@ namespace Serein.Workbench
|
||||
|
||||
foreach (var connection in removeConnections)
|
||||
{
|
||||
connection.DeleteConnection();
|
||||
Connections.Remove(connection);
|
||||
fromNodeControl.RemoveCnnection(connection);
|
||||
toNodeControl.RemoveCnnection(connection);
|
||||
fromNodeControl.RemoveConnection(connection); // 移除连接
|
||||
toNodeControl.RemoveConnection(connection); // 移除连接
|
||||
if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control))
|
||||
{
|
||||
JudgmentFlipFlopNode(control); // 连接关系变更时判断
|
||||
@@ -461,6 +461,10 @@ namespace Serein.Workbench
|
||||
_ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发
|
||||
};
|
||||
|
||||
if(IToJunction.ArgDataJunction.Length == 0)
|
||||
{
|
||||
|
||||
}
|
||||
JunctionControlBase endJunction = IToJunction.ArgDataJunction[eventArgs.ArgIndex];
|
||||
LineType lineType = LineType.Bezier;
|
||||
// 添加连接
|
||||
@@ -470,16 +474,8 @@ namespace Serein.Workbench
|
||||
eventArgs.ArgIndex,
|
||||
eventArgs.ConnectionArgSourceType,
|
||||
startJunction,
|
||||
endJunction,
|
||||
() => EnvDecorator.RemoveConnectArgSourceAsync(fromNodeGuid, toNodeGuid, eventArgs.ArgIndex)
|
||||
endJunction
|
||||
);
|
||||
|
||||
//if (toNodeControl is FlipflopNodeControl flipflopControl
|
||||
// && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
|
||||
//{
|
||||
// NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
|
||||
//}
|
||||
connection.RefreshLine(); // 添加贝塞尔曲线显示
|
||||
Connections.Add(connection);
|
||||
fromNodeControl.AddCnnection(connection);
|
||||
toNodeControl.AddCnnection(connection);
|
||||
@@ -503,10 +499,9 @@ namespace Serein.Workbench
|
||||
if (connection.End is ArgJunctionControl junctionControl && junctionControl.ArgIndex == eventArgs.ArgIndex)
|
||||
{
|
||||
// 找到符合删除条件的连接线
|
||||
connection.DeleteConnection(); // 从UI层面上移除
|
||||
Connections.Remove(connection); // 从本地记录中移除
|
||||
fromNodeControl.RemoveCnnection(connection); // 从节点持有的记录移除
|
||||
toNodeControl.RemoveCnnection(connection); // 从节点持有的记录移除
|
||||
fromNodeControl.RemoveConnection(connection); // 从节点持有的记录移除
|
||||
toNodeControl.RemoveConnection(connection); // 从节点持有的记录移除
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,42 +37,41 @@ namespace Serein.Workbench.Node.View
|
||||
/// <summary>
|
||||
/// 方法入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
private JunctionControlBase[] argDataJunction;
|
||||
/// <summary>
|
||||
/// 方法入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
JunctionControlBase[] INodeJunction.ArgDataJunction { get {
|
||||
if(argDataJunction == null)
|
||||
JunctionControlBase[] INodeJunction.ArgDataJunction
|
||||
{
|
||||
get
|
||||
{
|
||||
// 获取 MethodDetailsControl 实例
|
||||
var methodDetailsControl = this.MethodDetailsControl;
|
||||
var itemsControl = FindVisualChild<ItemsControl>(methodDetailsControl); // 查找 ItemsControl
|
||||
if (itemsControl != null)
|
||||
{
|
||||
// 获取 MethodDetailsControl 实例
|
||||
var methodDetailsControl = this.MethodDetailsControl;
|
||||
argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
|
||||
var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
|
||||
var controls = new List<JunctionControlBase>();
|
||||
|
||||
var itemsControl = FindVisualChild<ItemsControl>(methodDetailsControl); // 查找 ItemsControl
|
||||
if (itemsControl != null)
|
||||
for (int i = 0; i < itemsControl.Items.Count; i++)
|
||||
{
|
||||
var controls = new List<JunctionControlBase>();
|
||||
|
||||
for (int i = 0; i < itemsControl.Items.Count; i++)
|
||||
var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
|
||||
if (container != null)
|
||||
{
|
||||
var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
|
||||
if (container != null)
|
||||
var argControl = FindVisualChild<ArgJunctionControl>(container);
|
||||
if (argControl != null)
|
||||
{
|
||||
var argControl = FindVisualChild<ArgJunctionControl>(container);
|
||||
if (argControl != null)
|
||||
{
|
||||
controls.Add(argControl); // 收集 ArgJunctionControl 实例
|
||||
}
|
||||
controls.Add(argControl); // 收集 ArgJunctionControl 实例
|
||||
}
|
||||
}
|
||||
argDataJunction = controls.ToArray();
|
||||
}
|
||||
return argDataJunction = controls.ToArray();
|
||||
}
|
||||
return argDataJunction;
|
||||
} }
|
||||
else
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.Workbench.Node.View"
|
||||
MaxWidth="300"
|
||||
>
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Top" >
|
||||
|
||||
@@ -32,10 +32,6 @@ namespace Serein.Workbench.Node.View
|
||||
/// </summary>
|
||||
JunctionControlBase INodeJunction.ReturnDataJunction => this.ResultJunctionControl;
|
||||
|
||||
/// <summary>
|
||||
/// 方法入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
private JunctionControlBase[] argDataJunction;
|
||||
/// <summary>
|
||||
/// 方法入参控制点(可能有,可能没)
|
||||
/// </summary>
|
||||
@@ -43,34 +39,34 @@ namespace Serein.Workbench.Node.View
|
||||
{
|
||||
get
|
||||
{
|
||||
if (argDataJunction == null)
|
||||
// 获取 MethodDetailsControl 实例
|
||||
var methodDetailsControl = this.MethodDetailsControl;
|
||||
var itemsControl = FindVisualChild<ItemsControl>(methodDetailsControl); // 查找 ItemsControl
|
||||
if (itemsControl != null)
|
||||
{
|
||||
// 获取 MethodDetailsControl 实例
|
||||
var methodDetailsControl = this.MethodDetailsControl;
|
||||
argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
|
||||
var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
|
||||
var controls = new List<JunctionControlBase>();
|
||||
|
||||
var itemsControl = FindVisualChild<ItemsControl>(methodDetailsControl); // 查找 ItemsControl
|
||||
if (itemsControl != null)
|
||||
for (int i = 0; i < itemsControl.Items.Count; i++)
|
||||
{
|
||||
var controls = new List<JunctionControlBase>();
|
||||
|
||||
for (int i = 0; i < itemsControl.Items.Count; i++)
|
||||
var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
|
||||
if (container != null)
|
||||
{
|
||||
var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
|
||||
if (container != null)
|
||||
var argControl = FindVisualChild<ArgJunctionControl>(container);
|
||||
if (argControl != null)
|
||||
{
|
||||
var argControl = FindVisualChild<ArgJunctionControl>(container);
|
||||
if (argControl != null)
|
||||
{
|
||||
controls.Add(argControl); // 收集 ArgJunctionControl 实例
|
||||
}
|
||||
controls.Add(argControl); // 收集 ArgJunctionControl 实例
|
||||
}
|
||||
}
|
||||
argDataJunction = controls.ToArray();
|
||||
}
|
||||
return argDataJunction = controls.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return [];
|
||||
}
|
||||
return argDataJunction;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Serein.Workbench.Node.View
|
||||
this.MouseDown += ParamsArg_OnMouseDown; // 增加或删除
|
||||
this.MouseMove += ParamsArgControl_MouseMove;
|
||||
this.MouseLeave += ParamsArgControl_MouseLeave;
|
||||
AddOrRemoveParamsAction = Add;
|
||||
AddOrRemoveParamsTask = AddAsync;
|
||||
|
||||
|
||||
}
|
||||
@@ -90,11 +90,11 @@ namespace Serein.Workbench.Node.View
|
||||
|
||||
private bool isMouseOver; // 鼠标悬停状态
|
||||
|
||||
private Action AddOrRemoveParamsAction; // 增加或删除参数
|
||||
private Func<Task> AddOrRemoveParamsTask; // 增加或删除参数
|
||||
|
||||
public void ParamsArg_OnMouseDown(object sender, MouseButtonEventArgs e)
|
||||
public async void ParamsArg_OnMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
AddOrRemoveParamsAction?.Invoke();
|
||||
await AddOrRemoveParamsTask.Invoke();
|
||||
}
|
||||
|
||||
private void ParamsArgControl_MouseMove(object sender, MouseEventArgs e)
|
||||
@@ -111,7 +111,7 @@ namespace Serein.Workbench.Node.View
|
||||
// 如果焦点仍在控件上时,则改变点击事件
|
||||
if (isMouseOver)
|
||||
{
|
||||
AddOrRemoveParamsAction = Remove;
|
||||
AddOrRemoveParamsTask = RemoveAsync;
|
||||
this.Dispatcher.Invoke(InvalidateVisual);// 触发一次重绘
|
||||
|
||||
}
|
||||
@@ -125,20 +125,20 @@ namespace Serein.Workbench.Node.View
|
||||
private void ParamsArgControl_MouseLeave(object sender, MouseEventArgs e)
|
||||
{
|
||||
isMouseOver = false;
|
||||
AddOrRemoveParamsAction = Add; // 鼠标焦点离开时恢复点击事件
|
||||
AddOrRemoveParamsTask = AddAsync; // 鼠标焦点离开时恢复点击事件
|
||||
cancellationTokenSource?.Cancel();
|
||||
this.Dispatcher.Invoke(InvalidateVisual);// 触发一次重绘
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void Add()
|
||||
private async Task AddAsync()
|
||||
{
|
||||
this.MyNode.Env.ChangeParameter(MyNode.Guid, true, ArgIndex);
|
||||
await this.MyNode.Env.ChangeParameter(MyNode.Guid, true, ArgIndex);
|
||||
}
|
||||
private void Remove()
|
||||
private async Task RemoveAsync()
|
||||
{
|
||||
this.MyNode.Env.ChangeParameter(MyNode.Guid, false, ArgIndex);
|
||||
await this.MyNode.Env.ChangeParameter(MyNode.Guid, false, ArgIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,22 +48,23 @@ namespace Serein.Workbench.Node.View
|
||||
/// 删除了连接之后,还需要从节点中的记录移除
|
||||
/// </summary>
|
||||
/// <param name="connection"></param>
|
||||
public void RemoveCnnection(ConnectionControl connection)
|
||||
public void RemoveConnection(ConnectionControl connection)
|
||||
{
|
||||
connectionControls.Remove(connection);
|
||||
connection.Remote();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除了连接之后,还需要从节点中的记录移除
|
||||
/// 删除所有连接
|
||||
/// </summary>
|
||||
public void RemoveAllConection()
|
||||
{
|
||||
foreach (var connection in this.connectionControls)
|
||||
{
|
||||
connection.Remote(); // 主动更新连线位置
|
||||
connection.Remote();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新与该节点有关的数据
|
||||
/// </summary>
|
||||
|
||||
@@ -104,7 +104,6 @@ namespace Serein.Workbench.Node.View
|
||||
public class ConnectionControl
|
||||
{
|
||||
|
||||
private readonly Action RemoteCallback;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -152,11 +151,9 @@ namespace Serein.Workbench.Node.View
|
||||
public ConnectionControl(Canvas Canvas,
|
||||
ConnectionInvokeType invokeType,
|
||||
JunctionControlBase Start,
|
||||
JunctionControlBase End,
|
||||
Action remoteCallback)
|
||||
JunctionControlBase End)
|
||||
{
|
||||
this.LineType = LineType.Bezier;
|
||||
this.RemoteCallback = remoteCallback;
|
||||
this.Canvas = Canvas;
|
||||
this.InvokeType = invokeType;
|
||||
this.Start = Start;
|
||||
@@ -174,13 +171,11 @@ namespace Serein.Workbench.Node.View
|
||||
int argIndex,
|
||||
ConnectionArgSourceType argSourceType,
|
||||
JunctionControlBase Start,
|
||||
JunctionControlBase End,
|
||||
Action remoteCallback)
|
||||
JunctionControlBase End)
|
||||
{
|
||||
this.LineType = LineType;
|
||||
this.RemoteCallback = remoteCallback;
|
||||
this.Canvas = Canvas;
|
||||
this.ArgIndex = ArgIndex;
|
||||
this.ArgIndex = argIndex;
|
||||
this.ArgSourceType = argSourceType;
|
||||
this.Start = Start;
|
||||
this.End = End;
|
||||
@@ -223,24 +218,26 @@ namespace Serein.Workbench.Node.View
|
||||
private void ConfigureLineContextMenu()
|
||||
{
|
||||
var contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => this.DeleteConnection()));
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => this.Remote()));
|
||||
BezierLine.ContextMenu = contextMenu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除该连线
|
||||
/// </summary>
|
||||
/// <param name="line"></param>
|
||||
public void DeleteConnection()
|
||||
{
|
||||
RemoteCallback?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除该连线
|
||||
/// </summary>
|
||||
public void Remote()
|
||||
{
|
||||
Canvas.Children.Remove(BezierLine);
|
||||
var env = Start.MyNode.Env;
|
||||
if (Start.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Invoke)
|
||||
{
|
||||
env.RemoveConnectInvokeAsync(Start.MyNode.Guid, End.MyNode.Guid, InvokeType);
|
||||
}
|
||||
else if (Start.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Arg)
|
||||
{
|
||||
env.RemoveConnectArgSourceAsync(Start.MyNode.Guid, End.MyNode.Guid, ArgIndex) ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -252,280 +249,26 @@ namespace Serein.Workbench.Node.View
|
||||
BezierLine.UpdatePoints(startPoint, endPoint);
|
||||
}
|
||||
|
||||
|
||||
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
|
||||
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
|
||||
/// <summary>
|
||||
/// 刷新坐标
|
||||
/// </summary>
|
||||
|
||||
#region 工具方法
|
||||
private (Point startPoint,Point endPoint) RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
|
||||
private (Point startPoint, Point endPoint) RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
|
||||
{
|
||||
|
||||
var startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置
|
||||
var endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置
|
||||
return (startPoint, endPoint);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// 连接控件,表示控件的连接关系
|
||||
/// </summary>
|
||||
public class ConnectionControl : Shape
|
||||
{
|
||||
private readonly Action RemoteCallback;
|
||||
|
||||
/// <summary>
|
||||
/// 关于调用
|
||||
/// </summary>
|
||||
/// <param name="Canvas"></param>
|
||||
/// <param name="Type"></param>
|
||||
public ConnectionControl(Canvas Canvas,
|
||||
ConnectionInvokeType Type,
|
||||
JunctionControlBase Start,
|
||||
JunctionControlBase End,
|
||||
Action remoteCallback)
|
||||
{
|
||||
this.RemoteCallback = remoteCallback;
|
||||
this.Canvas = Canvas;
|
||||
this.Type = Type;
|
||||
this.Start = Start;
|
||||
this.End = End;
|
||||
this.Start.Background = GetLineColor(Type); // 线条颜色
|
||||
this.End.Background = GetLineColor(Type); // 线条颜色
|
||||
InitElementPoint();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关于入参
|
||||
/// </summary>
|
||||
/// <param name="Canvas"></param>
|
||||
/// <param name="Type"></param>
|
||||
public ConnectionControl(Canvas Canvas,
|
||||
int argIndex,
|
||||
ConnectionArgSourceType connectionArgSourceType,
|
||||
JunctionControlBase Start,
|
||||
JunctionControlBase End,
|
||||
Action remoteCallback)
|
||||
{
|
||||
this.RemoteCallback = remoteCallback;
|
||||
this.Canvas = Canvas;
|
||||
this.ArgIndex = ArgIndex;
|
||||
this.ConnectionArgSourceType = connectionArgSourceType;
|
||||
this.Start = Start;
|
||||
this.End = End;
|
||||
this.Start.Background = GetLineColor(Type); // 线条颜色
|
||||
this.End.Background = GetLineColor(Type); // 线条颜色
|
||||
InitElementPoint();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 所在的画布
|
||||
/// </summary>
|
||||
public Canvas Canvas { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 调用方法类型,连接类型
|
||||
/// </summary>
|
||||
public ConnectionInvokeType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取参数类型,第几个参数
|
||||
/// </summary>
|
||||
public int ArgIndex { get; set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 参数来源(决定了连接线的样式)
|
||||
/// </summary>
|
||||
public ConnectionArgSourceType ConnectionArgSourceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始控制点
|
||||
/// </summary>
|
||||
public JunctionControlBase Start { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目标控制点
|
||||
/// </summary>
|
||||
public JunctionControlBase End { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 配置连接曲线的右键菜单
|
||||
/// </summary>
|
||||
/// <param name="line"></param>
|
||||
private void ConfigureLineContextMenu(ConnectionControl connection)
|
||||
{
|
||||
var contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
|
||||
connection.ContextMenu = contextMenu;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除该连线
|
||||
/// </summary>
|
||||
/// <param name="line"></param>
|
||||
private void DeleteConnection(ConnectionControl connection)
|
||||
{
|
||||
var connectionToRemove = connection;
|
||||
if (connectionToRemove is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(this.Start is JunctionControlBase startJunctionControlBase)
|
||||
{
|
||||
startJunctionControlBase.Background = Brushes.Transparent;
|
||||
}
|
||||
if (this.End is JunctionControlBase endJunctionControlBase)
|
||||
{
|
||||
endJunctionControlBase.Background = Brushes.Transparent;
|
||||
}
|
||||
this.Canvas.g
|
||||
RemoteCallback?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除
|
||||
/// </summary>
|
||||
public void RemoveFromCanvas()
|
||||
{
|
||||
Canvas.Children.Remove(this); // 移除线
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新绘制
|
||||
/// </summary>
|
||||
public void AddOrRefreshLine()
|
||||
{
|
||||
this.InvalidateVisual();
|
||||
}
|
||||
|
||||
public void InitElementPoint()
|
||||
{
|
||||
leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心
|
||||
rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心
|
||||
brush = GetLineColor(Type); // 线条颜色
|
||||
hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
|
||||
hitVisiblePen.Freeze(); // Freeze以提高性能
|
||||
visualPen = new Pen(brush, 2.0); // 默认可视化Pen
|
||||
visualPen.Freeze(); // Freeze以提高性能
|
||||
ConfigureLineContextMenu(this); // 设置连接右键事件
|
||||
linkSize = 4; // 整线条粗细
|
||||
Canvas.Children.Add(this); // 添加线
|
||||
Grid.SetZIndex(this, -9999999); // 置底
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控件重绘事件
|
||||
/// </summary>
|
||||
/// <param name="drawingContext"></param>
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
RefreshPoint(Canvas, this.Start, this.End); // 刷新坐标
|
||||
DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize, brush); // 刷新线条显示位置
|
||||
}
|
||||
|
||||
private readonly StreamGeometry streamGeometry = new StreamGeometry();
|
||||
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
|
||||
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
|
||||
private Pen hitVisiblePen; // 初始化碰撞检测线
|
||||
private Pen visualPen; // 默认可视化Pen
|
||||
private Point startPoint; // 连接线的起始节点
|
||||
private Point endPoint; // 连接线的终点
|
||||
private Brush brush; // 线条颜色
|
||||
double linkSize; // 根据缩放比例调整线条粗细
|
||||
protected override Geometry DefiningGeometry => streamGeometry;
|
||||
|
||||
#region 工具方法
|
||||
|
||||
public void RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
|
||||
{
|
||||
endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置
|
||||
startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据连接类型指定颜色
|
||||
/// </summary>
|
||||
/// <param name="currentConnectionType"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static SolidColorBrush GetLineColor(ConnectionInvokeType currentConnectionType)
|
||||
{
|
||||
return currentConnectionType switch
|
||||
{
|
||||
ConnectionInvokeType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")),
|
||||
ConnectionInvokeType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
|
||||
ConnectionInvokeType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
|
||||
ConnectionInvokeType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
|
||||
_ => throw new Exception(),
|
||||
};
|
||||
}
|
||||
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
|
||||
private Vector axis = new Vector(1, 0);
|
||||
private Vector startToEnd;
|
||||
private void DrawBezierCurve(DrawingContext drawingContext,
|
||||
Point start,
|
||||
Point end,
|
||||
double linkSize,
|
||||
Brush brush,
|
||||
bool isHitTestVisible = false,
|
||||
double strokeThickness = 1.0,
|
||||
bool isMouseOver = false,
|
||||
double dashOffset = 0.0)
|
||||
{
|
||||
// 控制点的计算逻辑
|
||||
double power = 8 * 8; // 控制贝塞尔曲线的“拉伸”强度
|
||||
|
||||
// 计算轴向向量与起点到终点的向量
|
||||
//var axis = new Vector(1, 0);
|
||||
startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
|
||||
|
||||
// 计算拉伸程度k,拉伸与水平夹角正相关
|
||||
var k = 1 - Math.Pow(Math.Max(0, axis.DotProduct(startToEnd)), 10.0);
|
||||
|
||||
// 如果起点x大于终点x,增加额外的偏移量,避免重叠
|
||||
var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
|
||||
|
||||
// 控制点的实际计算
|
||||
c0 = new Point(+(power + bias) * k + start.X, start.Y);
|
||||
c1 = new Point(-(power + bias) * k + end.X, end.Y);
|
||||
|
||||
// 准备StreamGeometry以用于绘制曲线
|
||||
streamGeometry.Clear();
|
||||
using (var context = streamGeometry.Open())
|
||||
{
|
||||
context.BeginFigure(start, true, false); // 曲线起点
|
||||
context.BezierTo(c0, c1, end, true, false); // 画贝塞尔曲线
|
||||
}
|
||||
|
||||
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
|
||||
|
||||
// 绘制碰撞检测线
|
||||
//if (true)
|
||||
//{
|
||||
// //hitVisiblePen = new Pen(Brushes.Transparent, linkSize + strokeThickness);
|
||||
// //hitVisiblePen.Freeze();
|
||||
// drawingContext.DrawGeometry(null, hitVisiblePen, streamGeometry);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user