修改了很多

This commit is contained in:
fengjiayi
2024-10-27 00:54:10 +08:00
parent 6f26d303e4
commit cb2553ac69
66 changed files with 2215 additions and 1402 deletions

View File

@@ -1,13 +1,10 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Attributes;
using Serein.Library.Enums;
using Serein.Library.Network.WebSocketCommunication;
using System.Security.Cryptography.X509Certificates;
using Serein.NodeFlow;
using Serein.Library.Core.NodeFlow;
using Serein.Library.NodeFlow.Tool;
using Serein.Library.Utils;
using Serein.FlowRemoteManagement.Model;
using System.Reflection;

View File

@@ -13,6 +13,11 @@ namespace Serein.FlowStartTool
{
public static void Main(string[] args)
{
#if true
args = [@"F:\临时\project\linux\project.dnf"];
#endif
Console.WriteLine("Hello :) ");
Console.WriteLine($"args : {string.Join(" , ", args)}");
string filePath;
@@ -57,10 +62,10 @@ namespace Serein.FlowStartTool
}
IsRuning = true;
StartFlow(flowProjectData, fileDataPath).GetAwaiter().GetResult();
_ = StartFlow(flowProjectData, fileDataPath);
while (IsRuning)
{
Console.ReadKey();
}
}
@@ -88,13 +93,12 @@ namespace Serein.FlowStartTool
// this.window = window;
//}
Env = new FlowEnvironmentDecorator(uIContextOperation); // Linux 环境下没有线程上下文(暂时没有写)
Env = new FlowEnvironmentDecorator(uIContextOperation);
Env.LoadProject(new FlowEnvInfo { Project = flowProjectData }, fileDataPath); // 加载项目
await Env.StartRemoteServerAsync(7525); // 启动 web socket 监听远程请求
//await Env.StartAsync();
IsRuning = false;
}

View File

@@ -1,10 +1,10 @@
{
"profiles": {
"Serein.FlowStartTool": {
"commandName": "Project"
},
"配置文件 1": {
"commandName": "DebugRoslynComponent"
},
"Serein.FlowStartTool": {
"commandName": "Project"
}
}
}

View File

@@ -9,34 +9,34 @@
<Nullable>enable</Nullable>
<ErrorReport>prompt</ErrorReport>
<AssemblyName>starter</AssemblyName>
<PlatformTarget>AnyCPU</PlatformTarget>
<!--<IsRoslynComponent>true</IsRoslynComponent>-->
<!--<GenerateDocumentationFile>true</GenerateDocumentationFile>-->
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
<Optimize>False</Optimize>
<GenerateAssemblyInfo>True</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0|AnyCPU'">
<Optimize>False</Optimize>
<GenerateAssemblyInfo>True</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Library.Core\Serein.Library.Core.csproj" />
<ProjectReference Include="..\Library\Serein.Library.csproj" />
<ProjectReference Include="..\NodeFlow\Serein.NodeFlow.csproj" />
</ItemGroup>
<!--<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0|AnyCPU'">
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup><!--<ProjectReference Include="..\Library\Serein.Library.csproj" />
<ProjectReference Include="..\NodeFlow\Serein.NodeFlow.csproj" />-->
<PackageReference Include="Serein.Library" Version="1.0.17" />
<PackageReference Include="Serein.Library.NodeGenerator" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<!--<ProjectReference Include="..\Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj" OutputItemType="Analyzer" />-->
<ProjectReference Include="..\NodeFlow\Serein.NodeFlow.csproj" />
</ItemGroup>
</PropertyGroup>-->
</Project>

View File

@@ -47,6 +47,10 @@ namespace Serein.Library.Core.NodeFlow
/// <returns></returns>
public object? GetFlowData(string nodeGuid)
{
if (string.IsNullOrEmpty(nodeGuid))
{
return null;
}
if(dictNodeFlowData.TryGetValue(nodeGuid,out var data))
{
return data;
@@ -70,8 +74,18 @@ namespace Serein.Library.Core.NodeFlow
/// <summary>
/// 结束流程
/// </summary>
public void EndCurrentBranch()
public void Exit()
{
foreach (var nodeObj in dictNodeFlowData.Values)
{
if (nodeObj is not null)
{
if (typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) && nodeObj is IDisposable disposable)
{
disposable?.Dispose();
}
}
}
this.dictNodeFlowData?.Clear();
RunState = RunState.Completion;
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.14</Version>
<Version>1.0.15</Version>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

View File

@@ -1,4 +1,5 @@
using Serein.Library.Api;
using System;
using System.Collections.Concurrent;
namespace Serein.Library.Framework.NodeFlow
@@ -46,8 +47,6 @@ namespace Serein.Library.Framework.NodeFlow
/// <returns></returns>
public object GetFlowData(string nodeGuid)
{
if (dictNodeFlowData.TryGetValue(nodeGuid, out var data))
{
return data;
@@ -71,8 +70,18 @@ namespace Serein.Library.Framework.NodeFlow
/// <summary>
/// 结束流程
/// </summary>
public void EndCurrentBranch()
public void Exit()
{
foreach (var nodeObj in dictNodeFlowData.Values)
{
if (nodeObj != null)
{
if (typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) && nodeObj is IDisposable disposable)
{
disposable?.Dispose();
}
}
}
this.dictNodeFlowData?.Clear();
RunState = RunState.Completion;
}

View File

@@ -34,5 +34,5 @@ using System.Runtime.InteropServices;
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.1.4")]
[assembly: AssemblyFileVersion("1.0.1.4")]
[assembly: AssemblyFileVersion("1.0.1.5")]
[assembly: NeutralResourcesLanguage("")]

View File

@@ -42,7 +42,8 @@ namespace Serein.Library.Api
/// <summary>
/// 用以提前结束分支运行
/// </summary>
void EndCurrentBranch();
void Exit();
/*/// <summary>
/// 定时循环触发

View File

@@ -1,5 +1,6 @@
using Serein.Library.FlowNode;
using Serein.Library.Utils;
using System;
using System.Collections.Generic;
@@ -176,6 +177,15 @@ namespace Serein.Library.Api
/// </summary>
Remote,
}
/// <summary>
/// 更改方法调用关系
/// </summary>
/// <param name="fromNodeGuid"></param>
/// <param name="toNodeGuid"></param>
/// <param name="junctionOfConnectionType"></param>
/// <param name="connectionInvokeType"></param>
/// <param name="changeType"></param>
public NodeConnectChangeEventArgs(string fromNodeGuid,
string toNodeGuid,
JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线
@@ -189,6 +199,15 @@ namespace Serein.Library.Api
this.JunctionOfConnectionType = junctionOfConnectionType;
}
/// <summary>
/// 更改参数传递关系
/// </summary>
/// <param name="fromNodeGuid"></param>
/// <param name="toNodeGuid"></param>
/// <param name="junctionOfConnectionType"></param>
/// <param name="argIndex"></param>
/// <param name="connectionArgSourceType"></param>
/// <param name="changeType"></param>
public NodeConnectChangeEventArgs(string fromNodeGuid,
string toNodeGuid,
JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线
@@ -228,6 +247,9 @@ namespace Serein.Library.Api
/// 节点对应的方法入参所需参数来源
/// </summary>
public ConnectionArgSourceType ConnectionArgSourceType { get; protected set; }
/// <summary>
/// 第几个参数
/// </summary>
public int ArgIndex { get; protected set; }
@@ -350,17 +372,19 @@ namespace Serein.Library.Api
/// </summary>
public class NodeInterruptStateChangeEventArgs : FlowEventArgs
{
public NodeInterruptStateChangeEventArgs(string nodeGuid, InterruptClass @class)
public NodeInterruptStateChangeEventArgs(string nodeGuid,bool isInterrupt)
{
NodeGuid = nodeGuid;
Class = @class;
// Class = @class;
IsInterrupt = isInterrupt;
}
/// <summary>
/// 中断的节点Guid
/// </summary>
public string NodeGuid { get; protected set; }
public InterruptClass Class { get; protected set; }
public bool IsInterrupt { get; protected set; }
// public InterruptClass Class { get; protected set; }
}
/// <summary>
/// 节点触发了中断事件参数
@@ -638,7 +662,7 @@ namespace Serein.Library.Api
/// <param name="addres">远程环境地址</param>
/// <param name="port">远程环境端口</param>
/// <param name="token">密码</param>
Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres,int port, string token);
Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres,int port, string token);
/// <summary>
/// 退出远程环境
@@ -705,30 +729,53 @@ namespace Serein.Library.Api
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="fromNodeJunctionType">起始节点控制点</param>
/// <param name="toNodeJunctionType">目标节点控制点</param>
/// <param name="connectionType">决定了方法执行后的后继行为</param>
/// <param name="argIndex">决定了方法入参来源</param>
Task<bool> ConnectNodeAsync(string fromNodeGuid,
/// <param name="invokeType">决定了方法执行后的后继行为</param>
Task<bool> ConnectInvokeNodeAsync(string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionInvokeType connectionType,
int argIndex);
ConnectionInvokeType invokeType);
/// <summary>
/// 在两个节点之间创建连接关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="fromNodeJunctionType">起始节点控制点</param>
/// <param name="toNodeJunctionType">目标节点控制点</param>
/// <param name="argSourceType">决定了方法参数来源</param>
/// <param name="argIndex">设置第几个参数</param>
Task<bool> ConnectArgSourceNodeAsync(string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionArgSourceType argSourceType,
int argIndex);
/// <summary>
/// 创建节点/区域/基础控件
/// </summary>
/// <param name="nodeType">节点/区域/基础控件类型</param>
/// <param name="position">节点在画布上的位置(</param>
/// <param name="methodDetailsInfo">节点绑定的方法说明</param>
/// <param name="methodDetailsInfo">节点绑定的方法说明</param>
Task<NodeInfo> CreateNodeAsync(NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null);
/// <summary>
/// 移除两个节点之间的连接关系
/// 移除两个节点之间的方法调用关系
/// </summary>
/// <param name="fromNodeGuid">起始节点</param>
/// <param name="toNodeGuid">目标节点</param>
/// <param name="connectionType">连接类型</param>
Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType);
Task<bool> RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType);
/// <summary>
/// 移除连接节点之间参数传递的关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="argIndex">连接到第几个参数</param>
/// <param name="connectionArgSourceType">参数来源类型</param>
Task<bool> RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex);
/// <summary>
/// 移除节点/区域/基础控件
@@ -750,12 +797,12 @@ namespace Serein.Library.Api
/// <summary>
/// 设置节点中断级别
/// 设置节点中断
/// </summary>
/// <param name="nodeGuid">被中断的节点Guid</param>
/// <param name="interruptClass">新的中断级别</param>
/// <param name="nodeGuid">更改中断状态的节点Guid</param>
/// <param name="isInterrup">是否中断</param>
/// <returns></returns>
Task<bool> SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass);
Task<bool> SetNodeInterruptAsync(string nodeGuid,bool isInterrup);
/// <summary>
/// 添加作用于某个对象的中断表达式

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library
{
/// <summary>
/// 拓展方法
/// </summary>
public static partial class SereinExtension
{
/// <summary>
/// 判断连接类型
/// </summary>
/// <param name="start"></param>
/// <returns></returns>
public static JunctionOfConnectionType ToConnectyionType(this JunctionType start)
{
if (start == JunctionType.Execute
|| start == JunctionType.NextStep)
{
return JunctionOfConnectionType.Invoke;
}
else
{
return JunctionOfConnectionType.Arg;
}
}
/// <summary>
/// 判断是否运行连接
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
public static bool IsCanConnection(this JunctionType start,JunctionType end)
{
if(start == end)
{
return false;
}
var startType = start.ToConnectyionType();
if (startType == JunctionOfConnectionType.Invoke)
{
return (end == JunctionType.Execute && start == JunctionType.NextStep)
|| (start == JunctionType.Execute && end == JunctionType.NextStep);
}
else // if (startType == JunctionOfConnectionType.Arg)
{
return (end == JunctionType.ArgData && start == JunctionType.ReturnData)
|| (start == JunctionType.ArgData && end == JunctionType.ReturnData);
}
//var endType = end.ToConnectyionType();
//if (startType != endType
// || startType == JunctionOfConnectionType.None
// || endType == JunctionOfConnectionType.None)
//{
// return false;
//}
//else
//{
// if (startType == JunctionOfConnectionType.Invoke)
// {
// return end == JunctionType.NextStep;
// }
// else // if (startType == JunctionOfConnectionType.Arg)
// {
// return end == JunctionType.ReturnData;
// }
//}
}
}
}

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using static Serein.Library.Utils.EmitHelper;
@@ -14,6 +15,17 @@ namespace Serein.Library
/// </summary>
public class DelegateDetails
{
/// <summary>
/// 根据方法信息构建Emit委托
/// </summary>
/// <param name="methodInfo"></param>
public DelegateDetails(MethodInfo methodInfo)
{
var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var emitDelegate);
_emitMethodType = emitMethodType;
_emitDelegate = emitDelegate;
}
/// <summary>
/// 记录Emit委托
/// </summary>
@@ -21,8 +33,8 @@ namespace Serein.Library
/// <param name="EmitDelegate"></param>
public DelegateDetails(EmitMethodType EmitMethodType, Delegate EmitDelegate)
{
this._emitMethodType = EmitMethodType;
this._emitDelegate = EmitDelegate;
_emitMethodType = EmitMethodType;
_emitDelegate = EmitDelegate;
}
/// <summary>
/// 更新委托方法
@@ -56,7 +68,7 @@ namespace Serein.Library
/// <returns>void方法自动返回null</returns>
public async Task<object> InvokeAsync(object instance, object[] args)
{
if(args is null)
if (args is null)
{
args = Array.Empty<object>();
}

View File

@@ -35,15 +35,16 @@ namespace Serein.Library
/// <summary>
/// 中断级别,暂时停止继续执行后继分支。
/// </summary>
[PropertyInfo]
private InterruptClass _interruptClass = InterruptClass.None;
//[PropertyInfo]
//private InterruptClass _interruptClass = InterruptClass.None;
/// <summary>
/// 中断级别,暂时停止继续执行后继分支。
/// </summary>
[PropertyInfo(IsNotification = true)]
[PropertyInfo(IsNotification = true, CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);")]
private bool _isInterrupt = false;
//private const string MyInteruptCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);"; // 添加到中断的自定义代码
/// <summary>
/// 取消中断的回调函数
@@ -56,29 +57,30 @@ namespace Serein.Library
/// </summary>
[PropertyInfo]
private Func<Task<CancelType>> _getInterruptTask;
}
/// <summary>
/// 中断级别,暂时停止继续执行后继分支。
/// </summary>
//public enum InterruptClass
//{
// /// <summary>
// /// 不中断
// /// </summary>
// None,
// /// <summary>
// /// 分支中断,中断进入当前节点的分支。
// /// </summary>
// Branch,
// /// <summary>
// /// 全局中断,中断全局所有节点的运行。(暂未实现相关)
// /// </summary>
// Global,
//}
}
/// <summary>
/// 中断级别,暂时停止继续执行后继分支。
/// </summary>
public enum InterruptClass
{
/// <summary>
/// 不中断
/// </summary>
None,
/// <summary>
/// 分支中断,中断进入当前节点的分支。
/// </summary>
Branch,
/// <summary>
/// 全局中断,中断全局所有节点的运行。(暂未实现相关)
/// </summary>
Global,
}
}

View File

@@ -85,9 +85,6 @@ namespace Serein.Library
}
public abstract partial class NodeModelBase : IDynamicFlowNode
{
public NodeModelBase(IFlowEnvironment environment)
@@ -119,39 +116,40 @@ namespace Serein.Library
/// <summary>
/// 控制FlowData在同一时间只会被同一个线程更改。
/// </summary>
private readonly ReaderWriterLockSlim _flowDataLock = new ReaderWriterLockSlim();
private object _flowData;
/// <summary>
/// 当前传递数据(执行了节点对应的方法,才会存在值)。
/// </summary>
protected object FlowData
{
get
{
_flowDataLock.EnterReadLock();
try
{
return _flowData;
}
finally
{
_flowDataLock.ExitReadLock();
}
}
set
{
_flowDataLock.EnterWriteLock();
try
{
_flowData = value;
}
finally
{
_flowDataLock.ExitWriteLock();
}
}
}
//private readonly ReaderWriterLockSlim _flowDataLock = new ReaderWriterLockSlim();
//private object _flowData;
///// <summary>
///// 当前传递数据(执行了节点对应的方法,才会存在值)。
///// </summary>
//protected object FlowData
//{
// get
// {
// _flowDataLock.EnterReadLock();
// try
// {
// return _flowData;
// }
// finally
// {
// _flowDataLock.ExitReadLock();
// }
// }
// set
// {
// _flowDataLock.EnterWriteLock();
// try
// {
// _flowData = value;
// }
// finally
// {
// _flowDataLock.ExitWriteLock();
// }
// }
//}
}
}
@@ -319,5 +317,5 @@ namespace Serein.Library
//}
}

View File

@@ -31,7 +31,7 @@ namespace Serein.Library
/// 获取节点参数
/// </summary>
/// <returns></returns>
public abstract Parameterdata[] GetParameterdatas();
public abstract ParameterData[] GetParameterdatas();
/// <summary>
/// 导出为节点信息
@@ -47,7 +47,7 @@ namespace Serein.Library
var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支
// 生成参数列表
Parameterdata[] parameterData = GetParameterdatas();
ParameterData[] parameterData = GetParameterdatas();
return new NodeInfo
{
@@ -82,9 +82,13 @@ namespace Serein.Library
{
for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
{
Parameterdata pd = nodeInfo.ParameterData[i];
this.MethodDetails.ParameterDetailss[i].IsExplicitData = pd.State;
this.MethodDetails.ParameterDetailss[i].DataValue = pd.Value;
var mdPd = this.MethodDetails.ParameterDetailss[i];
ParameterData pd = nodeInfo.ParameterData[i];
mdPd.IsExplicitData = pd.State;
mdPd.DataValue = pd.Value;
mdPd.ArgDataSourceType = EnumHelper.ConvertEnum<ConnectionArgSourceType>(pd.SourceType);
mdPd.ArgDataSourceNodeGuid = pd.SourceNodeGuid;
}
}
return this;
@@ -93,13 +97,12 @@ namespace Serein.Library
#region
/// <summary>
/// 不再中断
/// </summary>
public void CancelInterrupt()
{
this.DebugSetting.InterruptClass = InterruptClass.None;
this.DebugSetting.IsInterrupt = false;
DebugSetting.CancelInterruptCallback?.Invoke();
}
@@ -165,7 +168,7 @@ namespace Serein.Library
NodeModelBase upstreamNode = upstreamNodes[index];
if (!(upstreamNode is null) && upstreamNode.DebugSetting.IsEnable)
{
if (upstreamNode.DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
if (upstreamNode.DebugSetting.IsInterrupt) // 执行触发前
{
var cancelType = await upstreamNode.DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{upstreamNode.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
@@ -220,7 +223,7 @@ namespace Serein.Library
{
#region
if (DebugSetting.InterruptClass != InterruptClass.None) // 执行触发检查是否需要中断
if (DebugSetting.IsInterrupt) // 执行触发检查是否需要中断
{
var cancelType = await this.DebugSetting.GetInterruptTask(); // 等待中断结束
await Console.Out.WriteLineAsync($"[{this.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
@@ -312,8 +315,9 @@ namespace Serein.Library
}
object[] parameters = new object[md.ParameterDetailss.Length];
var previousFlowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据
var previousDataType = previousFlowData?.GetType(); // 当前传递数据的类型
//var previousFlowData = nodeModel.PreviousNode?.FlowData; // 当前传递数据
for (int i = 0; i < parameters.Length; i++)
{
@@ -336,41 +340,48 @@ namespace Serein.Library
object inputParameter; // 存放解析的临时参数
if (ed.IsExplicitData) // 判断是否使用显示的输入参数
{
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && !(previousFlowData is null))
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase))
{
var previousFlowData = context.GetFlowData(nodeModel?.PreviousNode?.Guid); // 当前传递的数据
// 执行表达式从上一节点获取对象
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, previousFlowData, out _);
}
else
{
// 使用输入的固定值
inputParameter = ed.DataValue;
inputParameter = ed.DataValue;
}
}
else
{
if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
{
inputParameter = previousFlowData; // 使用运行时上一节点的返回值
inputParameter = context.GetFlowData(nodeModel?.PreviousNode?.Guid); // 当前传递的数据
}
else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
{
// 获取指定节点的数据
// 如果指定节点没有被执行会返回null
// 如果执行过,会获取上一次执行结果作为预入参数据
inputParameter = ed.ArgDataSourceNodeMoels[i].FlowData;
inputParameter = context.GetFlowData(ed.ArgDataSourceNodeGuid);
}
else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
{
// 立刻调用对应节点获取数据。
var result = await ed.ArgDataSourceNodeMoels[i].InvokeAsync(nodeModel.Env);
var result = await context.Env.InvokeNodeAsync(ed.ArgDataSourceNodeGuid);
inputParameter = result;
}
else
{
throw new Exception("节点执行方法获取入参参数时ConnectionArgSourceType枚举是意外的枚举值");
}
}
}
if (inputParameter is null)
{
throw new Exception($"[arg{ed.Index}][{ed.Name}][{ed.DataType}]参数不能为null");
}
#endregion
#region
@@ -418,7 +429,7 @@ namespace Serein.Library
#endregion
#region
if (inputParameter.GetType() == ed.DataType)
{
parameters[i] = inputParameter; // 类型一致无需转换,直接装入入参数组
@@ -506,8 +517,8 @@ namespace Serein.Library
{
await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象
await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点
nodeModel.FlowData = newData; // 替换数据
context.AddOrUpdate(guid, nodeModel); // 上下文中更新数据
//nodeModel.FlowData = newData; // 替换数据
context.AddOrUpdate(guid, newData); // 上下文中更新数据
}
}
@@ -548,12 +559,13 @@ namespace Serein.Library
if (isExpInterrupt) // 触发中断
{
InterruptClass interruptClass = InterruptClass.Branch; // 分支中断
if (await context.Env.SetNodeInterruptAsync(nodeModel.Guid, interruptClass))
nodeModel.DebugSetting.IsInterrupt = true;
if (await context.Env.SetNodeInterruptAsync(nodeModel.Guid,true))
{
context.Env.TriggerInterrupt(nodeModel.Guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp);
var cancelType = await nodeModel.DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{data}]中断已{cancelType},开始执行后继分支");
nodeModel.DebugSetting.IsInterrupt = false;
}
}
}
@@ -561,26 +573,26 @@ namespace Serein.Library
}
}
/// <summary>
/// 释放对象
/// </summary>
public void ReleaseFlowData()
{
if (typeof(IDisposable).IsAssignableFrom(FlowData?.GetType()) && FlowData is IDisposable disposable)
{
disposable?.Dispose();
}
this.FlowData = null;
}
///// <summary>
///// 释放对象
///// </summary>
//public void ReleaseFlowData()
//{
// if (typeof(IDisposable).IsAssignableFrom(FlowData?.GetType()) && FlowData is IDisposable disposable)
// {
// disposable?.Dispose();
// }
// this.FlowData = null;
//}
/// <summary>
/// 获取节点数据
/// </summary>
/// <returns></returns>
public object GetFlowData()
{
return this.FlowData;
}
///// <summary>
///// 获取节点数据
///// </summary>
///// <returns></returns>
//public object GetFlowData()
//{
// return this.FlowData;
//}
#endregion
}

View File

@@ -1,4 +1,5 @@
using Serein.Library.Api;
using Serein.Library.Utils;
using System;
using System.Linq;
@@ -62,11 +63,10 @@ namespace Serein.Library
/// <summary>
/// 当 ArgDataSourceType 不为 GetPreviousNodeData 时(从运行时上一节点获取数据)。
/// 则通过该集合对应的节点,获取其 FlowData 作为预处理的入参参数。
/// 则通过当前上下文获取该Guid对应的数据作为预处理的入参参数。
/// </summary>
[PropertyInfo(IsProtection = true)]
public NodeModelBase[] _argDataSourceNodeMoels;
[PropertyInfo]
private string _argDataSourceNodeGuid;
/// <summary>
@@ -97,6 +97,16 @@ namespace Serein.Library
public partial class ParameterDetails
{
/// <summary>
/// 用于创建元数据
/// </summary>
public ParameterDetails()
{
}
/// <summary>
/// 为节点实例化新的入参描述
/// </summary>
@@ -112,27 +122,15 @@ namespace Serein.Library
/// <param name="info">参数信息</param>
public ParameterDetails(ParameterDetailsInfo info)
{
//this.env = env;
Index = info.Index;
Name = info.Name;
DataType = Type.GetType(info.DataTypeFullName);
ExplicitType = Type.GetType(info.ExplicitTypeFullName);
ExplicitTypeName = info.ExplicitTypeName;
Items = info.Items;
}
/// <summary>
/// 用于创建元数据
/// </summary>
public ParameterDetails()
{
}
/// <summary>
/// 转为描述
/// </summary>
@@ -141,12 +139,12 @@ namespace Serein.Library
{
return new ParameterDetailsInfo
{
Index = Index,
DataTypeFullName = DataType.FullName,
Name = Name,
ExplicitTypeFullName = ExplicitType.FullName,
ExplicitTypeName = ExplicitTypeName,
Items = Items,
Index = this.Index,
DataTypeFullName = this.DataType.FullName,
Name = this.Name,
ExplicitTypeFullName = this.ExplicitType.FullName,
ExplicitTypeName = this.ExplicitTypeName,
Items = this.Items.Select(it => it).ToArray(),
};
}
@@ -154,7 +152,7 @@ namespace Serein.Library
/// 为某个节点拷贝方法描述的入参描述
/// </summary>
/// <param name="env">运行环境</param>
/// <param name="nodeGuid">运行环境</param>
/// <param name="nodeModel">对应的节点</param>
/// <returns></returns>
public ParameterDetails CloneOfClone(IFlowEnvironment env, NodeModelBase nodeModel)
{

View File

@@ -8,7 +8,7 @@ namespace Serein.Library
{
/// <summary>
/// 方法入参描述
/// 方法入参描述(远程用)
/// </summary>
public class ParameterDetailsInfo
{
@@ -26,6 +26,7 @@ namespace Serein.Library
/// 方法入参参数名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 显式类型
/// </summary>

View File

@@ -202,14 +202,13 @@ namespace Serein.Library
/// <summary>
/// 参数
/// </summary>
public Parameterdata[] ParameterData { get; set; }
public ParameterData[] ParameterData { get; set; }
/// <summary>
/// 如果是区域控件,则会存在子项。
/// </summary>
public string[] ChildNodeGuids { get; set; }
/// <summary>
/// 于画布中的位置
/// </summary>
@@ -223,14 +222,26 @@ namespace Serein.Library
}
/// <summary>
/// 显示参数,项目文件相关
/// 参数信息,项目文件相关
/// </summary>
public class Parameterdata
public class ParameterData
{
/// <summary>
/// 参数类型true时使用自定义的入参false时由运行环境自动传参
/// </summary>
public bool State { get; set; }
/// <summary>
/// 参数来源节点
/// </summary>
public string SourceNodeGuid { get; set; }
/// <summary>
/// 来源类型
/// </summary>
public string SourceType { get; set; }
/// <summary>
/// 自定义入参
/// </summary>

View File

@@ -17,28 +17,58 @@ using Type = System.Type;
namespace Serein.Library.Web
{
/// <summary>
/// 路由接口
/// </summary>
public interface IRouter
{
/// <summary>
/// 添加处理模块
/// </summary>
/// <param name="controllerType"></param>
void AddHandle(Type controllerType);
/// <summary>
/// 路由解析开始处理
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task<bool> ProcessingAsync(HttpListenerContext context);
}
/// <summary>
/// api请求处理模块
/// </summary>
public class ApiHandleConfig
{
private readonly Delegate EmitDelegate;
private readonly EmitHelper.EmitMethodType EmitMethodType;
private readonly DelegateDetails delegateDetails;
/// <summary>
/// Post请求处理方法中入参参数类型
/// </summary>
public enum PostArgType
{
/// <summary>
/// 不做处理
/// </summary>
None,
/// <summary>
/// 使用Url参数
/// </summary>
IsUrlData,
/// <summary>
/// 使用整体的Boby参数
/// </summary>
IsBobyData,
}
/// <summary>
/// 添加处理配置
/// </summary>
/// <param name="methodInfo"></param>
public ApiHandleConfig(MethodInfo methodInfo)
{
EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out EmitDelegate);
delegateDetails = new DelegateDetails(methodInfo);
var parameterInfos = methodInfo.GetParameters();
ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray();
ParameterName = parameterInfos.Select(t => t.Name.ToLower()).ToArray();
@@ -68,7 +98,12 @@ namespace Serein.Library.Web
private readonly string[] ParameterName;
private readonly Type[] ParameterType;
/// <summary>
/// 处理Get请求
/// </summary>
/// <param name="instance"></param>
/// <param name="routeData"></param>
/// <returns></returns>
public async Task<object> HandleGet(object instance, Dictionary<string, string> routeData)
{
object[] args = new object[ParameterType.Length];
@@ -93,40 +128,20 @@ namespace Serein.Library.Web
}
}
object result;
object result = null;
try
{
if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func<object, object[], Task<object>> hasResultTask)
{
result = await hasResultTask(instance, args);
}
else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
{
await task.Invoke(instance, args);
result = null;
}
else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
{
result = func.Invoke(instance, args);
}
else
{
result = null;
}
result = await delegateDetails.InvokeAsync(instance, args);
}
catch (Exception ex)
{
result = null;
await Console.Out.WriteLineAsync(ex.Message);
}
return result;
}
/// <returns></returns>
public async Task<object> HandlePost(object instance, JObject jsonObject, Dictionary<string, string> routeData)
{
object[] args = new object[ParameterType.Length];
@@ -173,26 +188,10 @@ namespace Serein.Library.Web
}
object result;
object result = null;
try
{
if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func<object, object[], Task<object>> hasResultTask)
{
result = await hasResultTask(instance, args);
}
else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
{
await task.Invoke(instance, args);
result = null;
}
else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
{
result = func.Invoke(instance, args);
}
else
{
result = null;
}
result = await delegateDetails.InvokeAsync(instance, args);
}
catch (Exception ex)
{
@@ -567,107 +566,6 @@ namespace Serein.Library.Web
}
internal static class WebFunc
{
public static bool ToBool(this JToken token, bool defult = false)
{
var value = token?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return defult;
}
if (!bool.TryParse(value, out bool result))
{
return defult;
}
else
{
return result;
}
}
public static int ToInt(this JToken token, int defult = 0)
{
var value = token?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return defult;
}
if (!int.TryParse(value, out int result))
{
return defult;
}
else
{
return result;
}
}
public static double ToDouble(this JToken token, double defult = 0)
{
var value = token?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return defult;
}
if (!int.TryParse(value, out int result))
{
return defult;
}
else
{
return result;
}
}
}
#region
// private readonly ConcurrentDictionary<string, bool> _controllerAutoHosting; // 存储是否实例化
// private readonly ConcurrentDictionary<string, object> _controllerInstances;
//public void CollectRoutes(Type controllerType)
//{
// string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
// foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
// {
// var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
// if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
// {
// var customUrl = routeAttribute.Url; // 获取自定义 URL
// string url;
// if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
// {
// url = $"/api/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
// }
// else
// {
// customUrl = CleanUrl(customUrl);
// url = $"/api/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL并构建新的 URL
// }
// var httpMethod = routeAttribute.Http; // 获取 HTTP 方法
// _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
// }
// }
//}
//public void RegisterRoute<T>(T controllerInstance) // 方法声明,用于动态注册路由
//{
// Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型
// var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
// foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
// {
// var webAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
// if (webAttribute != null) // 如果存在 WebAPIAttribute 属性
// {
// var url = AddRoutesUrl(autoHostingAttribute, webAttribute, controllerType, method);
// if (url == null) continue;
// _controllerInstances[url] = controllerInstance;
// _controllerAutoHosting[url] = false;
// }
// }
//}
#endregion
}

View File

@@ -0,0 +1,64 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Network.Http
{
internal static partial class SereinExtension
{
#region JSON相关
public static bool ToBool(this JToken token, bool defult = false)
{
var value = token?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return defult;
}
if (!bool.TryParse(value, out bool result))
{
return defult;
}
else
{
return result;
}
}
public static int ToInt(this JToken token, int defult = 0)
{
var value = token?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return defult;
}
if (!int.TryParse(value, out int result))
{
return defult;
}
else
{
return result;
}
}
public static double ToDouble(this JToken token, double defult = 0)
{
var value = token?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return defult;
}
if (!int.TryParse(value, out int result))
{
return defult;
}
else
{
return result;
}
}
#endregion
}
}

View File

@@ -31,7 +31,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
Action<Exception, Action<object>> onExceptionTracking,
bool ArgNotNull)
{
EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo,out EmitDelegate);
DelegateDetails = new DelegateDetails(methodInfo);
this.Module = model;
Instance = instance;
var parameterInfos = methodInfo.GetParameters();
@@ -72,15 +72,10 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
/// 参数不能为空
/// </summary>
private bool ArgNotNull;
/// <summary>
/// Emit委托
/// </summary>
private readonly Delegate EmitDelegate;
/// <summary>
/// Emit委托类型
/// </summary>
private readonly EmitHelper.EmitMethodType EmitMethodType;
private readonly DelegateDetails DelegateDetails;
/// <summary>
/// 未捕获的异常跟踪
/// </summary>
@@ -114,29 +109,26 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
/// </summary>
private readonly bool[] IsCheckArgNotNull;
//private object ConvertArg(Type type, string argName )
//{
//}
public async void Handle(Func<object, Task> SendAsync,string msgId, JObject jsonObject)
{
object[] args = new object[ParameterType.Length];
bool isCanInvoke = true;; // 表示是否可以调用方法
for (int i = 0; i < ParameterType.Length; i++)
{
var type = ParameterType[i];
var argName = ParameterName[i];
#region DATA JSON数据
if (useData[i])
{
args[i] = jsonObject.ToObject(type);
}
#endregion
else if (useMsgId[i])
var type = ParameterType[i]; // 入参变量类型
var argName = ParameterName[i]; // 入参参数名称
#region ID
if (useMsgId[i])
{
args[i] = msgId;
}
#endregion
#region DATA JSON数据
else if (useData[i])
{
args[i] = jsonObject.ToObject(type);
}
#endregion
#region
else if (type.IsValueType)
{
@@ -229,23 +221,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
object result;
try
{
if (EmitMethodType == EmitHelper.EmitMethodType.HasResultTask && EmitDelegate is Func<object, object[], Task<object>> hasResultTask)
{
result = await hasResultTask(Instance, args);
}
else if (EmitMethodType == EmitHelper.EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
{
await task.Invoke(Instance, args);
result = null;
}
else if (EmitMethodType == EmitHelper.EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
{
result = func.Invoke(Instance, args);
}
else
{
result = null;
}
result = await DelegateDetails.InvokeAsync(Instance, args);
}
catch (Exception ex)
{
@@ -256,25 +232,11 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
await SendAsync.Invoke(exData);
}));
}
//sw.Stop();
//Console.WriteLine($"Emit Invoke{sw.ElapsedTicks * 1000000F / Stopwatch.Frequency:n3}μs");
if (Module.IsReturnValue)
{
if (result is null)
{
result = "null";
}
_ = SendAsync.Invoke(result);
}
//if( && result != null && result.GetType().IsClass)
//{
// //var reusltJsonText = JsonConvert.SerializeObject(result);
// //_ = SendAsync.Invoke($"{reusltJsonText}");
//}
}

View File

@@ -91,6 +91,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
MyHandleConfigs.Clear();
}
private HashSet<string> _myMsgIdHash = new HashSet<string>();
/// <summary>
/// 处理JSON数据
/// </summary>
@@ -106,11 +108,15 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
return;
}
string msgId = jsonObject.GetValue(MsgIdJsonKey)?.ToString();
if (_myMsgIdHash.Contains(msgId))
{
Console.WriteLine($"[{msgId}]{theme} 消息重复");
return;
}
_myMsgIdHash.Add(msgId);
try
{
JObject dataObj = jsonObject.GetValue(DataJsonKey)?.ToObject<JObject>();
handldConfig.Handle(async (data) =>
{
@@ -170,9 +176,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
}
var msg = jsonData.ToString();
//Console.WriteLine(msg);
//Console.WriteLine();
await sendAsync.Invoke(msg);
}

View File

@@ -155,21 +155,16 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
/// <summary>
/// 异步处理消息
/// </summary>
/// <param name="SendAsync"></param>
/// <param name="sendAsync"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task HandleMsgAsync(Func<string, Task> SendAsync, string message)
public void HandleMsg(Func<string, Task> sendAsync, string message)
{
//Console.WriteLine(message);
JObject json = JObject.Parse(message);
await Task.Run(() =>
foreach (var module in MyHandleModuleDict.Values)
{
foreach (var module in MyHandleModuleDict.Values)
{
module.HandleSocketMsg(SendAsync, json);
}
});
module.HandleSocketMsg(sendAsync, json);
}
}

View File

@@ -8,28 +8,4 @@ namespace Serein.Library.Network.WebSocketCommunication
}
//[AutoRegister(RegisterSequence.FlowLoading)]
//[AutoSocketModule(JsonThemeField = "theme", JsonDataField = "data")]
//public class UserService : ISocketControlBase
//{
// public Guid HandleGuid { get; } = new Guid();
// // Action<string> 类型是特殊的,会用一个委托代替,这个委托可以将文本信息发送到客户端
// // Action<object> 类型是特殊的会用一个委托代替这个委托可以将对象转成json发送到客户端
// [AutoSocketHandle]
// public void AddUser(User user,Action<string> Recover)
// {
// Console.WriteLine(user.ToString());
// Recover("ok");
// }
// [AutoSocketHandle(ThemeValue = "Remote")]
// public void DeleteUser(User user, Action<string> Recover)
// {
// Console.WriteLine(user.ToString());
// }
//}
}

View File

@@ -0,0 +1,73 @@
using Serein.Library.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication
{
public class MsgQueueUtil
{
public ConcurrentQueue<string> Msgs = new ConcurrentQueue<string>();
private readonly Channel<string> _msgChannel;
public MsgQueueUtil()
{
_msgChannel = CreateChannel();
}
private Channel<string> CreateChannel()
{
return Channel.CreateBounded<string>(new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.Wait
});
}
/// <summary>
/// 等待消息
/// </summary>
/// <returns></returns>
public async Task<string> WaitMsgAsync()
{
var state = await _msgChannel.Reader.ReadAsync();
return state;
}
public void WriteMsg(string msg)
{
//Msgs.Enqueue(msg);
Console.WriteLine($"{DateTime.Now}{msg}{Environment.NewLine}");
_ = _msgChannel.Writer.WriteAsync(msg);
}
public bool TryGetMsg(out string msg)
{
return Msgs.TryDequeue(out msg);
}
}
public class SocketExtension
{
/// <summary>
/// 发送消息
/// </summary>
/// <param name="webSocket"></param>
/// <param name="message"></param>
/// <returns></returns>
public static async Task SendAsync(WebSocket webSocket, string message)
{
var buffer = Encoding.UTF8.GetBytes(message);
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}

View File

@@ -1,10 +1,13 @@
using Serein.Library.Network.WebSocketCommunication.Handle;
using System;
using System.Diagnostics;
using System.IO.Compression;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;
namespace Serein.Library.Network.WebSocketCommunication
{
@@ -38,7 +41,6 @@ namespace Serein.Library.Network.WebSocketCommunication
{
try
{
await _client.ConnectAsync(new Uri(uri), CancellationToken.None);
_ = ReceiveAsync();
return true;
@@ -58,8 +60,12 @@ namespace Serein.Library.Network.WebSocketCommunication
/// <returns></returns>
public async Task SendAsync(string message)
{
var buffer = Encoding.UTF8.GetBytes(message);
await _client.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
Console.WriteLine("发送消息");
await Task.Delay(2000);
await SocketExtension.SendAsync(this._client, message); // 回复客户端
Console.WriteLine();
//var buffer = Encoding.UTF8.GetBytes(message);
//await _client.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
}
/// <summary>
@@ -68,13 +74,21 @@ namespace Serein.Library.Network.WebSocketCommunication
/// <returns></returns>
private async Task ReceiveAsync()
{
var buffer = new byte[1024];
var msgQueueUtil = new MsgQueueUtil();
_ = Task.Run(async () =>
{
await HandleMsgAsync(_client, msgQueueUtil);
});
var receivedMessage = new StringBuilder(); // 用于拼接长消息
while (_client.State == WebSocketState.Open)
{
try
{
var buffer = new byte[1024];
WebSocketReceiveResult result;
do
@@ -86,21 +100,22 @@ namespace Serein.Library.Network.WebSocketCommunication
receivedMessage.Append(partialMessage);
} while (!result.EndOfMessage); // 判断是否已经收到完整消息
var message = receivedMessage.ToString();
msgQueueUtil.WriteMsg(message);
receivedMessage.Clear(); // 清空 StringBuilder 为下一条消息做准备
// 处理收到的完整消息
if (result.MessageType == WebSocketMessageType.Close)
{
await _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
else
{
var completeMessage = receivedMessage.ToString();
_ = MsgHandleHelper.HandleMsgAsync(SendAsync, completeMessage); // 处理消息
//Debug.WriteLine($"Received: {completeMessage}");
}
// 清空 StringBuilder 为下一条消息做准备
receivedMessage.Clear();
//else
//{
// var completeMessage = receivedMessage.ToString();
// MsgHandleHelper.HandleMsg(SendAsync, completeMessage); // 处理消息,如果方法入参是需要发送消息委托时,将 SendAsync 作为委托参数提供
// //Debug.WriteLine($"Received: {completeMessage}");
//}
}
catch (Exception ex)
{
@@ -110,65 +125,86 @@ namespace Serein.Library.Network.WebSocketCommunication
}
/* #region 消息处理
private readonly string ThemeField;
private readonly ConcurrentDictionary<string, HandldConfig> ThemeConfigs = new ConcurrentDictionary<string, HandldConfig>();
public async Task HandleSocketMsg(string jsonStr)
public async Task HandleMsgAsync(WebSocket webSocket,
MsgQueueUtil msgQueueUtil)
{
JObject json;
try
{
json = JObject.Parse(jsonStr);
}
catch (Exception ex)
{
await SendAsync(_client, ex.Message);
return;
}
// 获取到消息
string themeName = json[ThemeField]?.ToString();
if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
return;
}
object dataValue;
if (string.IsNullOrEmpty(handldConfig.DataField))
while (true)
{
dataValue = json.ToObject(handldConfig.DataType);
}
else
{
dataValue = json[handldConfig.DataField].ToObject(handldConfig.DataType);
}
await handldConfig.Invoke(dataValue, SendAsync);
}
public void AddConfig(string themeName, Type dataType, MsgHandler msgHandler)
{
if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
handldConfig = new HandldConfig
var message = await msgQueueUtil.WaitMsgAsync(); // 有消息时通知
//if (!msgQueueUtil.TryGetMsg(out var message)) // 获取消息
//{
// return;
//}
// 消息处理
MsgHandleHelper.HandleMsg(async (text) =>
{
DataField = themeName,
DataType = dataType
};
ThemeConfigs.TryAdd(themeName, handldConfig);
await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入
}, message); // 处理消息
}
handldConfig.HandldAsync += msgHandler;
}
public void RemoteConfig(string themeName, MsgHandler msgHandler)
{
if (ThemeConfigs.TryGetValue(themeName, out var handldConfig))
/* #region 消息处理
private readonly string ThemeField;
private readonly ConcurrentDictionary<string, HandldConfig> ThemeConfigs = new ConcurrentDictionary<string, HandldConfig>();
public async Task HandleSocketMsg(string jsonStr)
{
handldConfig.HandldAsync -= msgHandler;
if (!handldConfig.HasSubscribers)
JObject json;
try
{
ThemeConfigs.TryRemove(themeName, out _);
json = JObject.Parse(jsonStr);
}
catch (Exception ex)
{
await SendAsync(_client, ex.Message);
return;
}
// 获取到消息
string themeName = json[ThemeField]?.ToString();
if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
return;
}
object dataValue;
if (string.IsNullOrEmpty(handldConfig.DataField))
{
dataValue = json.ToObject(handldConfig.DataType);
}
else
{
dataValue = json[handldConfig.DataField].ToObject(handldConfig.DataType);
}
await handldConfig.Invoke(dataValue, SendAsync);
}
public void AddConfig(string themeName, Type dataType, MsgHandler msgHandler)
{
if (!ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
handldConfig = new HandldConfig
{
DataField = themeName,
DataType = dataType
};
ThemeConfigs.TryAdd(themeName, handldConfig);
}
handldConfig.HandldAsync += msgHandler;
}
public void RemoteConfig(string themeName, MsgHandler msgHandler)
{
if (ThemeConfigs.TryGetValue(themeName, out var handldConfig))
{
handldConfig.HandldAsync -= msgHandler;
if (!handldConfig.HasSubscribers)
{
ThemeConfigs.TryRemove(themeName, out _);
}
}
}
#endregion*/
}
#endregion*/
}
}

View File

@@ -180,67 +180,46 @@ namespace Serein.Library.Network.WebSocketCommunication
return;
}
var msgQueueUtil = new MsgQueueUtil();
_ = Task.Run(async () =>
{
await HandleMsgAsync(webSocket,msgQueueUtil, authorizedHelper);
});
//Func<string, Task> SendAsync = async (text) =>
//{
// await WebSocketServer.SendAsync(webSocket, text);
//};
var buffer = new byte[1024];
var receivedMessage = new StringBuilder(); // 用于拼接长消息
while (webSocket.State == WebSocketState.Open)
while ( webSocket.State == WebSocketState.Open)
{
WebSocketReceiveResult result;
try
{
WebSocketReceiveResult result;
var buffer = new byte[1024];
do
{
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
if (IsCheckToken)
{
AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _);
}
}
// 将接收到的部分消息解码并拼接
var partialMessage = Encoding.UTF8.GetString(buffer, 0, result.Count);
receivedMessage.Append(partialMessage);
} while (!result.EndOfMessage); // 循环直到接收到完整的消息
// 完整消息已经接收到,准备处理
var message = receivedMessage.ToString();
if (result.MessageType == WebSocketMessageType.Close)
{
//SendAsync = null;
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
if (IsCheckToken)
{
AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _);
}
}
else
{
if (IsCheckToken)
{
var authorizedResult = await authorizedHelper.HandleAuthorized(message); // 尝试检测授权
if (!authorizedResult) // 授权失败
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
if (IsCheckToken)
{
AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _);
}
continue;
}
}
// 消息处理
_ = MsgHandleHelper.HandleMsgAsync(async (text) =>
{
await WebSocketServer.SendAsync(webSocket, text);
}, message); // 处理消息
}
// 清空 StringBuilder 为下一条消息做准备
receivedMessage.Clear();
var message = receivedMessage.ToString(); // 获取消息文本
receivedMessage.Clear(); // 清空 StringBuilder 为下一条消息做准备
msgQueueUtil.WriteMsg(message); // 处理消息
}
catch (Exception ex)
{
@@ -249,17 +228,45 @@ namespace Serein.Library.Network.WebSocketCommunication
}
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="webSocket"></param>
/// <param name="message"></param>
/// <returns></returns>
public static async Task SendAsync(WebSocket webSocket, string message)
public async Task HandleMsgAsync(WebSocket webSocket,
MsgQueueUtil msgQueueUtil,
WebSocketAuthorizedHelper authorizedHelper)
{
var buffer = Encoding.UTF8.GetBytes(message);
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
while (true)
{
var message = await msgQueueUtil.WaitMsgAsync(); // 有消息时通知
//if (!msgQueueUtil.TryGetMsg(out var message)) // 获取消息
//{
// return;
//}
if (IsCheckToken)
{
var authorizedResult = await authorizedHelper.HandleAuthorized(message); // 尝试检测授权
if (!authorizedResult) // 授权失败
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
if (IsCheckToken)
{
AuthorizedClients.TryRemove(authorizedHelper.AddresPort, out var _);
}
return;
}
}
// 消息处理
MsgHandleHelper.HandleMsg(async (text) =>
{
await SocketExtension.SendAsync(webSocket, text); // 回复客户端,处理方法中入参如果需要发送消息委托,则将该回调方法作为委托参数传入
}, message); // 处理消息
}
}
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.17</Version>
<Version>1.0.18</Version>
<TargetFrameworks>net8.0;net462</TargetFrameworks>
<!--<TargetFrameworks>net8.0</TargetFrameworks>-->
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
@@ -37,7 +37,6 @@
<ItemGroup>
<ProjectReference Include="..\Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj" OutputItemType="Analyzer" />
<!--ReferenceOutputAssembly="false"-->
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Reactive" Version="6.0.1" />
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />

View File

@@ -100,4 +100,14 @@ namespace Serein.Library.Utils
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<(TriggerType, object)>());
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Serein.Library.Utils
/// <summary>
/// 管理远程环境,具备连接、发送消息、停止的功能
/// </summary>
public class RemoteEnvControl
public class RemoteMsgUtil
{
/// <summary>
/// 远程环境配置
@@ -52,7 +52,7 @@ namespace Serein.Library.Utils
/// <summary>
/// 配置远程连接IP端口
/// </summary>
public RemoteEnvControl(ControlConfiguration controlConfiguration)
public RemoteMsgUtil(ControlConfiguration controlConfiguration)
{
Config = controlConfiguration;
}
@@ -120,13 +120,6 @@ namespace Serein.Library.Utils
/// <returns></returns>
public async Task SendAsync(string msgId , string theme, object data)
{
//var sendMsg = new
//{
// theme = theme,
// token = this.Token,
// data = data,
//};
//var msg = JsonConvert.SerializeObject(sendMsg);
JObject jsonData;
if (data is null)
@@ -156,11 +149,8 @@ namespace Serein.Library.Utils
[Config.DataJsonKey] = dataToken
};
}
var msg = jsonData.ToString();
//Console.WriteLine(msg);
//Console.WriteLine();
Console.WriteLine($"[{msgId}] => {theme}");
await EnvClient.SendAsync(msg);
}

View File

@@ -30,21 +30,31 @@
/// </summary>
public const string SetStartNode = nameof(SetStartNode);
/// <summary>
/// 尝试连接两个节点
/// </summary>
public const string ConnectNode = nameof(ConnectNode);
/// <summary>
/// 尝试创建节点
/// </summary>
public const string CreateNode = nameof(CreateNode);
/// <summary>
/// 尝试移除节点之间的连接关系
/// </summary>
public const string RemoveConnect = nameof(RemoveConnect);
/// <summary>
/// 尝试移除节点
/// </summary>
public const string RemoveNode = nameof(RemoveNode);
/// <summary>
/// 尝试连接两个节点的方法调用关系
/// </summary>
public const string ConnectInvokeNode = nameof(ConnectInvokeNode);
/// <summary>
/// 尝试移除节点之间的方法调用关系
/// </summary>
public const string RemoveInvokeConnect = nameof(RemoveInvokeConnect);
/// <summary>
/// 尝试连接两个节点的参数传递关系
/// </summary>
public const string ConnectArgSourceNode = nameof(ConnectArgSourceNode);
/// <summary>
/// 尝试移除节点之间的参数传递关系
/// </summary>
public const string RemoveArgSourceConnect = nameof(RemoveArgSourceConnect);
/// <summary>
/// 激活一个触发器
/// </summary>

View File

@@ -2,6 +2,7 @@
using Newtonsoft.Json;
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.FlowNode;
using Serein.Library.Utils;
using Serein.Library.Utils.SereinExpression;
using Serein.NodeFlow.Model;
@@ -9,6 +10,7 @@ using Serein.NodeFlow.Tool;
using System.Collections.Concurrent;
using System.Numerics;
using System.Reflection;
using System.Reflection.Metadata.Ecma335;
using System.Xml.Linq;
using static Serein.Library.Utils.ChannelFlowInterrupt;
@@ -78,9 +80,8 @@ namespace Serein.NodeFlow.Env
if (clientMsgManage is null)
{
clientMsgManage = new MsgControllerOfServer(this);
//clientMsgManage = new MsgControllerOfServer(this, "token");
}
await clientMsgManage.StartRemoteServerAsync(port);
_ = clientMsgManage.StartRemoteServerAsync(port);
}
/// <summary>
@@ -346,18 +347,11 @@ namespace Serein.NodeFlow.Env
List<MethodDetails> initMethods = [];
List<MethodDetails> loadMethods = [];
List<MethodDetails> exitMethods = [];
//foreach(var mds in MethodDetailss.Values)
//{
// var initMds = mds.Where(it => it.MethodDynamicType == NodeType.Init);
// var loadMds = mds.Where(it => it.MethodDynamicType == NodeType.Loading);
// var exitMds = mds.Where(it => it.MethodDynamicType == NodeType.Exit);
// initMethods.AddRange(initMds);
// loadMethods.AddRange(loadMds);
// exitMethods.AddRange(exitMds);
//}
var initMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Init);
var loadMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Loading);
var exitMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Exit);
initMethods.AddRange(initMds);
loadMethods.AddRange(loadMds);
exitMethods.AddRange(exitMds);
@@ -418,9 +412,11 @@ namespace Serein.NodeFlow.Env
/// <returns></returns>
public async Task<object> InvokeNodeAsync(string nodeGuid)
{
if(this.NodeModels.TryGetValue(nodeGuid, out var model))
{
return await model.ExecutingAsync(null);
return await model.InvokeAsync(this);
}
else
{
@@ -435,18 +431,7 @@ namespace Serein.NodeFlow.Env
{
ChannelFlowInterrupt?.CancelAllTasks();
flowStarter?.Exit();
foreach (var node in NodeModels.Values)
{
if (node is not null)
{
node.ReleaseFlowData(); // 退出时释放对象计数
}
}
UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs()));
GC.Collect();
}
@@ -489,7 +474,6 @@ namespace Serein.NodeFlow.Env
/// 获取当前环境信息(远程连接)
/// </summary>
/// <returns></returns>
// [AutoSocketHandle]
public async Task<FlowEnvInfo> GetEnvInfoAsync()
{
Dictionary<NodeLibrary, List<MethodDetailsInfo>> LibraryMds = [];
@@ -574,12 +558,6 @@ namespace Serein.NodeFlow.Env
{
MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载项目时尝试获取方法信息
}
else
{
}
var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
nodeModel.LoadInfo(nodeInfo); // 创建节点model
if (nodeModel is null)
@@ -588,7 +566,6 @@ namespace Serein.NodeFlow.Env
continue;
}
TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
if (nodeInfo.ChildNodeGuids?.Length > 0)
{
@@ -643,6 +620,7 @@ namespace Serein.NodeFlow.Env
Task.Run(async () =>
{
await Task.Delay(777);
#region
foreach (var nodeInfo in projectData.Nodes)
{
if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
@@ -650,8 +628,6 @@ namespace Serein.NodeFlow.Env
// 不存在对应的起始节点
continue;
}
List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes),
(ConnectionInvokeType.IsFail, nodeInfo.FalseNodes),
(ConnectionInvokeType.IsError, nodeInfo.ErrorNodes),
@@ -670,8 +646,24 @@ namespace Serein.NodeFlow.Env
{
_ = ConnectInvokeOfNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
}
}
}
#endregion
#region
foreach (var toNode in NodeModels.Values)
{
for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++)
{
var pd = toNode.MethodDetails.ParameterDetailss[i];
if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid)
&& NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode))
{
ConnectGerResultOfNode(fromNode, toNode, pd.ArgDataSourceType, pd.Index);
}
}
}
#endregion
});
SetStartNode(projectData.StartNode);
@@ -686,7 +678,7 @@ namespace Serein.NodeFlow.Env
/// <param name="addres">远程环境地址</param>
/// <param name="port">远程环境端口</param>
/// <param name="token">密码</param>
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
{
if (IsLcR)
{
@@ -695,7 +687,7 @@ namespace Serein.NodeFlow.Env
}
// 没有连接远程环境,可以重新连接
var controlConfiguration = new RemoteEnvControl.ControlConfiguration
var controlConfiguration = new RemoteMsgUtil.ControlConfiguration
{
Addres = addres,
Port = port,
@@ -704,8 +696,8 @@ namespace Serein.NodeFlow.Env
MsgIdJsonKey = FlowEnvironment.MsgIdKey,
DataJsonKey = FlowEnvironment.DataKey,
};
var remoteEnvControl = new RemoteEnvControl(controlConfiguration);
var result = await remoteEnvControl.ConnectAsync();
var remoteMsgUtil = new RemoteMsgUtil(controlConfiguration);
var result = await remoteMsgUtil.ConnectAsync();
if (!result)
{
await Console.Out.WriteLineAsync("连接失败,请检查地址与端口是否正确");
@@ -713,7 +705,7 @@ namespace Serein.NodeFlow.Env
}
await Console.Out.WriteLineAsync("连接成功开始验证Token");
IsLcR = true;
return (true, remoteEnvControl);
return (true, remoteMsgUtil);
}
/// <summary>
@@ -908,19 +900,18 @@ namespace Serein.NodeFlow.Env
}
/// <summary>
/// 连接节点
/// 连接节点,创建方法调用关系
/// </summary>
/// <param name="fromNodeGuid">起始节点</param>
/// <param name="toNodeGuid">目标节点</param>
/// <param name="fromNodeJunctionType">起始节点控制点</param>
/// <param name="toNodeJunctionType">目标节点控制点</param>
/// <param name="connectionType">连接关系</param>
public async Task<bool> ConnectNodeAsync(string fromNodeGuid,
/// <param name="invokeType">连接关系</param>
public async Task<bool> ConnectInvokeNodeAsync(string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionInvokeType connectionType,
int argIndex)
ConnectionInvokeType invokeType)
{
// 获取起始节点与目标节点
@@ -943,22 +934,43 @@ namespace Serein.NodeFlow.Env
(fromNode, toNode) = (toNode, fromNode);
}
// 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点
state = ConnectInvokeOfNode(fromNode, toNode, connectionType); // 本地环境进行连接
state = ConnectInvokeOfNode(fromNode, toNode, invokeType); // 本地环境进行连接
}
else if (type == JunctionOfConnectionType.Arg)
return state;
}
/// <summary>
/// 创建节点之间的参数来源关系
/// </summary>
/// <param name="fromNodeGuid">起始节点</param>
/// <param name="toNodeGuid">目标节点</param>
/// <param name="fromNodeJunctionType">起始节点控制点result控制点</param>
/// <param name="toNodeJunctionType">目标节点控制点argData控制点</param>
/// <param name="argIndex">目标节点的第几个参数</param>
/// <param name="connectionArgSourceType">调用目标节点对应方法时,对应参数来源类型</param>
/// <returns></returns>
public async Task<bool> ConnectArgSourceNodeAsync(string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionArgSourceType connectionArgSourceType,
int argIndex)
{
// 获取起始节点与目标节点
var fromNode = GuidToModel(fromNodeGuid);
var toNode = GuidToModel(toNodeGuid);
if (fromNode is null || toNode is null) return false;
(var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType);
if (!state)
{
ConnectionArgSourceType connectionArgSourceType;
Console.WriteLine("出现非预期的连接行为");
return false; // 出现不符预期的连接行为,忽略此次连接行为
}
if (fromNode.Guid.Equals(toNode.Guid))
{
connectionArgSourceType = ConnectionArgSourceType.GetPreviousNodeData;
}
else
{
connectionArgSourceType = ConnectionArgSourceType.GetOtherNodeData;
}
// (连接自身的情况下)从上一个节点“返回值”控制点,连接到目标节点“方法入参”控制点
if (type == JunctionOfConnectionType.Arg)
{
// 从起始节点“返回值”控制点,连接到目标节点“方法入参”控制点
if (fromNodeJunctionType == JunctionType.ArgData)
{
@@ -966,32 +978,48 @@ namespace Serein.NodeFlow.Env
(fromNode, toNode) = (toNode, fromNode);
}
// 确定方法入参关系
state = ConnectGerResultOfNode(fromNode, toNode, connectionArgSourceType, argIndex); // 本地环境进行连接
}
return state;
}
/// <summary>
/// 移除连接关系
/// 移除连接节点之间方法调用的关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="connectionType">连接关系</param>
/// <exception cref="NotImplementedException"></exception>
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
public async Task<bool> RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
{
// 获取起始节点与目标节点
var fromNode = GuidToModel(fromNodeGuid);
var toNode = GuidToModel(toNodeGuid);
if (fromNode is null || toNode is null) return false;
var result = await RemoteConnectAsync(fromNode, toNode, connectionType);
return result;
}
/// <summary>
/// 移除连接节点之间参数传递的关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="argIndex">连接到第几个参数</param>
public async Task<bool> RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex)
{
// 获取起始节点与目标节点
var fromNode = GuidToModel(fromNodeGuid);
var toNode = GuidToModel(toNodeGuid);
if (fromNode is null || toNode is null) return false;
var result = await RemoteConnectAsync(fromNode, toNode, argIndex);
return result;
}
/// <summary>
/// 获取方法描述
/// </summary>
@@ -1077,41 +1105,39 @@ namespace Serein.NodeFlow.Env
/// <param name="nodeGuid">被中断的目标节点Guid</param>
/// <param name="interruptClass">中断级别</param>
/// <returns>操作是否成功</returns>
public Task<bool> SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass)
public Task<bool> SetNodeInterruptAsync(string nodeGuid, bool isInterrupt)
{
var nodeModel = GuidToModel(nodeGuid);
if (nodeModel is null)
return Task.FromResult(false);
if (interruptClass == InterruptClass.None)
if (!isInterrupt)
{
nodeModel.CancelInterrupt();
}
else if (interruptClass == InterruptClass.Branch)
else if (isInterrupt)
{
nodeModel.DebugSetting.CancelInterruptCallback?.Invoke();
nodeModel.DebugSetting.GetInterruptTask = async () =>
{
TriggerInterrupt(nodeGuid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor);
var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid);
var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid);
return result;
};
nodeModel.DebugSetting.CancelInterruptCallback = () =>
{
//nodeModel.DebugSetting.IsInterrupt = false;
ChannelFlowInterrupt.TriggerSignal(nodeGuid);
};
}
else if (interruptClass == InterruptClass.Global) // 全局……做不了omg
{
return Task.FromResult(false);
}
nodeModel.DebugSetting.InterruptClass = interruptClass;
//nodeModel.DebugSetting.IsInterrupt = true;
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass)));
UIContextOperation?.Invoke(() => OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, isInterrupt)));
}
return Task.FromResult(true);
@@ -1311,10 +1337,6 @@ namespace Serein.NodeFlow.Env
#region
/// <summary>
/// 加载指定路径的DLL文件
/// </summary>
@@ -1375,11 +1397,31 @@ namespace Serein.NodeFlow.Env
connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Remote)));
}
//else if (OperatingSystem.IsLinux())
//{
//}
return true;
}
/// <summary>
/// 移除连接关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Model</param>
/// <param name="toNodeGuid">目标节点Model</param>
/// <param name="connectionType">连接关系</param>
/// <exception cref="NotImplementedException"></exception>
private async Task<bool> RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, int argIndex)
{
toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = null;
toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值
if (OperatingSystem.IsWindows())
{
await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(
fromNode.Guid,
toNode.Guid,
JunctionOfConnectionType.Arg,
argIndex,
ConnectionArgSourceType.GetPreviousNodeData,
NodeConnectChangeEventArgs.ConnectChangeType.Remote)));
}
return true;
}
@@ -1479,13 +1521,10 @@ namespace Serein.NodeFlow.Env
}
}
/// <summary>
/// 创建节点
/// </summary>
/// <param name="nodeBase"></param>
private bool TryAddNode(NodeModelBase nodeModel)
{
nodeModel.Guid ??= Guid.NewGuid().ToString();
@@ -1503,10 +1542,6 @@ namespace Serein.NodeFlow.Env
return true;
}
/// <summary>
/// 检查连接
/// </summary>
@@ -1530,12 +1565,12 @@ namespace Serein.NodeFlow.Env
type = JunctionOfConnectionType.Invoke;
state = true;
}
else if (toNodeJunctionType == JunctionType.ArgData && fromNode.Guid.Equals(toNode.Guid))
{
// “方法执行”控制点拖拽到“方法入参”控制点且是同一个节点则添加获取参数关系表示生成入参参数时自动从该节点的上一节点获取flowdata
type = JunctionOfConnectionType.Arg;
state = true;
}
//else if (toNodeJunctionType == JunctionType.ArgData && fromNode.Guid.Equals(toNode.Guid))
//{
// // “方法执行”控制点拖拽到“方法入参”控制点且是同一个节点则添加获取参数关系表示生成入参参数时自动从该节点的上一节点获取flowdata
// type = JunctionOfConnectionType.Arg;
// state = true;
//}
}
else if (fromNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid))
{
@@ -1548,12 +1583,12 @@ namespace Serein.NodeFlow.Env
}
else if (fromNodeJunctionType == JunctionType.ArgData)
{
if (toNodeJunctionType == JunctionType.Execute && fromNode.Guid.Equals(toNode.Guid)) // 添加获取参数关系
{
// “方法入参”控制点拖拽到“方法执行”控制点且是同一个节点则添加获取参数关系生成入参参数时自动从该节点的上一节点获取flowdata
type = JunctionOfConnectionType.Arg;
state = true;
}
//if (toNodeJunctionType == JunctionType.Execute && fromNode.Guid.Equals(toNode.Guid)) // 添加获取参数关系
//{
// // “方法入参”控制点拖拽到“方法执行”控制点且是同一个节点则添加获取参数关系生成入参参数时自动从该节点的上一节点获取flowdata
// type = JunctionOfConnectionType.Arg;
// state = true;
//}
if(toNodeJunctionType == JunctionType.ReturnData && !fromNode.Guid.Equals(toNode.Guid))
{
// “”控制点拖拽到“方法返回值”控制点且不是同一个节点添加获取参数关系生成参数时从目标节点获取flowdata
@@ -1574,14 +1609,13 @@ namespace Serein.NodeFlow.Env
return (type,state);
}
/// <summary>
/// 连接节点
/// </summary>
/// <param name="fromNode">起始节点</param>
/// <param name="toNode">目标节点</param>
/// <param name="connectionType">连接关系</param>
private bool ConnectInvokeOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType connectionType)
/// <param name="invokeType">连接关系</param>
private bool ConnectInvokeOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType invokeType)
{
if (fromNode is null || toNode is null || fromNode == toNode)
{
@@ -1634,17 +1668,19 @@ namespace Serein.NodeFlow.Env
}
if (isPass)
{
fromNode.SuccessorNodes[connectionType].Add(toNode); // 添加到起始节点的子分支
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的分支
fromNode.SuccessorNodes[invokeType].Add(toNode); // 添加到起始节点的分支
toNode.PreviousNodes[invokeType].Add(fromNode); // 添加到目标节点的父分支
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() =>
OnNodeConnectChange?.Invoke(
new NodeConnectChangeEventArgs(
fromNode.Guid, // 从哪个节点开始
toNode.Guid, // 连接到那个节点
JunctionOfConnectionType.Invoke,
connectionType, // 连接线的样式类型
invokeType, // 连接线的样式类型
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
))); // 通知UI
}
@@ -1668,8 +1704,13 @@ namespace Serein.NodeFlow.Env
/// <param name="connectionArgSourceType"></param>
/// <param name="argIndex"></param>
/// <returns></returns>
private bool ConnectGerResultOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionArgSourceType connectionArgSourceType,int argIndex)
private bool ConnectGerResultOfNode(NodeModelBase fromNode,
NodeModelBase toNode,
ConnectionArgSourceType connectionArgSourceType,
int argIndex)
{
toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = fromNode.Guid;
toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = connectionArgSourceType;
UIContextOperation?.Invoke(() =>
OnNodeConnectChange?.Invoke(
new NodeConnectChangeEventArgs(
@@ -1680,7 +1721,7 @@ namespace Serein.NodeFlow.Env
connectionArgSourceType,
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
))); // 通知UI
return false;
return true;
}

View File

@@ -1,5 +1,6 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.FlowNode;
using Serein.Library.Utils;
namespace Serein.NodeFlow.Env
@@ -33,22 +34,22 @@ namespace Serein.NodeFlow.Env
private IFlowEnvironment currentFlowEnvironment;
private int _flag = 0; // 使用原子自增代替锁
private int _loadingProjectFlag = 0; // 使用原子自增代替锁
/// <summary>
/// 传入false时将停止数据通知。传入true时
/// </summary>
/// <param name="value"></param>
public void SetFlag(bool value)
public void SetProjectLoadingFlag(bool value)
{
Interlocked.Exchange(ref _flag, value ? 1 : 0);
Interlocked.Exchange(ref _loadingProjectFlag, value ? 1 : 0);
}
/// <summary>
///
/// 判断是否正在加载项目
/// </summary>
/// <returns></returns>
public bool IsFlagSet()
public bool IsLoadingProject()
{
return Interlocked.CompareExchange(ref _flag, 1, 1) == 1;
return Interlocked.CompareExchange(ref _loadingProjectFlag, 1, 1) == 1;
}
@@ -180,34 +181,69 @@ namespace Serein.NodeFlow.Env
currentFlowEnvironment.ClearAll();
}
public async Task<bool> ConnectNodeAsync(string fromNodeGuid,
/// <summary>
/// 在两个节点之间创建连接关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="fromNodeJunctionType">起始节点控制点</param>
/// <param name="toNodeJunctionType">目标节点控制点</param>
/// <param name="invokeType">决定了方法执行后的后继行为</param>
public async Task<bool> ConnectInvokeNodeAsync(string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionInvokeType connectionType,
int argIndex)
ConnectionInvokeType invokeType)
{
return await currentFlowEnvironment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, connectionType, argIndex);
return await currentFlowEnvironment.ConnectInvokeNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, invokeType);
}
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
/// <summary>
/// 在两个节点之间创建连接关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="fromNodeJunctionType">起始节点控制点</param>
/// <param name="toNodeJunctionType">目标节点控制点</param>
/// <param name="argSourceType">决定了方法参数来源</param>
/// <param name="argIndex">设置第几个参数</param>
public async Task<bool> ConnectArgSourceNodeAsync(string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionArgSourceType argSourceType,
int argIndex)
{
return await currentFlowEnvironment.ConnectArgSourceNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, argSourceType, argIndex);
}
/// <summary>
/// 连接远程环境并自动切换环境
/// </summary>
/// <param name="addres"></param>
/// <param name="port"></param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
{
// 连接成功,切换远程环境
(var isConnect, var remoteEnvControl) = await currentFlowEnvironment.ConnectRemoteEnv(addres, port, token);
(var isConnect, var remoteMsgUtil) = await currentFlowEnvironment.ConnectRemoteEnv(addres, port, token);
if (isConnect)
{
remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteEnvControl, this.UIContextOperation);
remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteMsgUtil, this.UIContextOperation);
currentFlowEnvironment = remoteFlowEnvironment;
}
return (isConnect, remoteEnvControl);
return (isConnect, remoteMsgUtil);
}
public async Task<NodeInfo> CreateNodeAsync(NodeControlType nodeBase, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null)
{
SetFlag(false);
SetProjectLoadingFlag(false);
var result = await currentFlowEnvironment.CreateNodeAsync(nodeBase, position, methodDetailsInfo); // 装饰器调用
SetFlag(true);
SetProjectLoadingFlag(true);
return result;
}
@@ -249,9 +285,9 @@ namespace Serein.NodeFlow.Env
public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath)
{
if (flowEnvInfo is null) return;
SetFlag(false);
SetProjectLoadingFlag(false);
currentFlowEnvironment.LoadProject(flowEnvInfo, filePath);
SetFlag(true);
SetProjectLoadingFlag(true);
}
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
@@ -275,9 +311,21 @@ namespace Serein.NodeFlow.Env
return currentFlowEnvironment.RemoteDll(assemblyFullName);
}
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
public async Task<bool> RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
{
return await currentFlowEnvironment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connectionType);
return await currentFlowEnvironment.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, connectionType);
}
/// <summary>
/// 移除连接节点之间参数传递的关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="argIndex">连接到第几个参数</param>
/// <param name="connectionArgSourceType">参数来源类型</param>
public async Task<bool> RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex)
{
return await currentFlowEnvironment.RemoveConnectArgSourceAsync(fromNodeGuid, toNodeGuid, argIndex);
}
public async Task<bool> RemoveNodeAsync(string nodeGuid)
@@ -296,9 +344,9 @@ namespace Serein.NodeFlow.Env
currentFlowEnvironment.SetMonitorObjState(key, isMonitor);
}
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass)
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, bool isInterrupt)
{
return await currentFlowEnvironment.SetNodeInterruptAsync(nodeGuid, interruptClass);
return await currentFlowEnvironment.SetNodeInterruptAsync(nodeGuid, isInterrupt);
}
public void SetStartNode(string nodeGuid)
@@ -359,11 +407,11 @@ namespace Serein.NodeFlow.Env
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
{
if (!IsFlagSet())
if (!IsLoadingProject())
{
return;
}
await currentFlowEnvironment.NotificationNodeValueChangeAsync(nodeGuid, path, value);
await currentFlowEnvironment.NotificationNodeValueChangeAsync(nodeGuid, path, value);
}

View File

@@ -1,13 +1,11 @@
using Serein.Library;
using Newtonsoft.Json;
using Serein.Library;
using Serein.Library.Network.WebSocketCommunication;
using Serein.Library.Network.WebSocketCommunication.Handle;
using Serein.Library.Utils;
namespace Serein.NodeFlow.Env
{
/// <summary>
/// 客户端的消息管理(用于处理服务端的响应)
/// </summary>
@@ -28,6 +26,14 @@ namespace Serein.NodeFlow.Env
this.remoteFlowEnvironment = remoteFlowEnvironment;
SendCommandFunc = func;
}
/// <summary>
/// 处理需要返回的消息
/// </summary>
/// <param name="msgId"></param>
/// <param name="theme"></param>
/// <param name="data"></param>
/// <returns></returns>
private async Task SendCommandAsync(string msgId, string theme, object? data)
{
await SendCommandFunc.Invoke(msgId, theme, data);
@@ -40,15 +46,12 @@ namespace Serein.NodeFlow.Env
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException">超时触发</exception>
public async Task SendAsync(string signal, object? data = null, int overtimeInMs = 100)
public async Task SendAsync(string theme, object? data = null, int overtimeInMs = 100)
{
//Console.WriteLine($"指令[{signal}]value{JsonConvert.SerializeObject(sendData)}");
if (!DebounceHelper.CanExecute(signal, overtimeInMs))
{
return;
}
var msgId = MsgIdHelper.GenerateId().ToString();
await SendCommandAsync(msgId, signal, data);
Console.WriteLine($"[{msgId}] => {theme}");
await SendCommandAsync(msgId, theme, data); // 客户端发送消息
}
/// <summary>
@@ -58,33 +61,13 @@ namespace Serein.NodeFlow.Env
/// <exception cref="NotImplementedException">超时触发</exception>
public async Task<TResult> SendAndWaitDataAsync<TResult>(string theme, object? data = null, int overtimeInMs = 50)
{
//Console.WriteLine($"指令[{signal}]value{JsonConvert.SerializeObject(sendData)}");
var msgId = MsgIdHelper.GenerateId().ToString();
_ = SendCommandAsync(msgId, theme, data);
//_ = Task.Run(async () =>
//{
// await Task.Delay(500);
//});
await SendCommandAsync(msgId, theme, data); // 客户端发送消息
return await remoteFlowEnvironment.WaitData<TResult>(msgId);
//if (DebounceHelper.CanExecute(signal, overtimeInMs))
//{
// _ = SendCommandAsync.Invoke(signal, sendData);
// return await remoteFlowEnvironment.WaitData<TResult>(signal);
// //(var type, var result) = await remoteFlowEnvironment.WaitDataWithTimeoutAsync<TResult>(signal, TimeSpan.FromSeconds(150));
// //if (type == TriggerType.Overtime)
// //{
// // throw new NotImplementedException("超时触发");
// //}
// //else
// //{
// // return result;
// //}
//}
//else
//{
// return default;
//}
}
@@ -139,15 +122,26 @@ namespace Serein.NodeFlow.Env
remoteFlowEnvironment.TriggerSignal(msgId, state);
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)]
public void ConnectNode([UseMsgId] string msgId, bool state)
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectInvokeNode)]
public void ConnectInvokeNode([UseMsgId] string msgId, bool state)
{
remoteFlowEnvironment.TriggerSignal(msgId, state);
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)]
public void RemoveConnect([UseMsgId] string msgId, bool state)
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveInvokeConnect)]
public void RemoveInvokeConnect([UseMsgId] string msgId, bool state)
{
remoteFlowEnvironment.TriggerSignal(msgId, state);
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectArgSourceNode)]
public void ConnectArgSourceNode([UseMsgId] string msgId, bool state)
{
remoteFlowEnvironment.TriggerSignal(msgId, state);
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveArgSourceConnect)]
public void RemoveArgSourceConnect([UseMsgId] string msgId, bool state)
{
remoteFlowEnvironment.TriggerSignal(msgId, state);
}

View File

@@ -227,7 +227,8 @@ namespace Serein.NodeFlow.Env
[AutoSocketHandle(ThemeValue = EnvMsgTheme.GetEnvInfo)]
private async Task<FlowEnvInfo> GetEnvInfoAsync()
{
return await environment.GetEnvInfoAsync();
var envInfo = await environment.GetEnvInfoAsync();
return envInfo;
}
/// <summary>
@@ -248,7 +249,7 @@ namespace Serein.NodeFlow.Env
/// <param name="port">远程环境端口</param>
/// <param name="token">密码</param>
// [AutoSocketHandle]
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
{
return await environment.ConnectRemoteEnv(addres, port, token);
}
@@ -314,7 +315,7 @@ namespace Serein.NodeFlow.Env
}
/// <summary>
/// 从远程环境移除节点
/// 远程从远程环境移除节点
/// </summary>
/// <param name="nodeGuid"></param>
/// <exception cref="NotImplementedException"></exception>
@@ -324,56 +325,178 @@ namespace Serein.NodeFlow.Env
//var result = environment.RemoveNodeAsync(nodeGuid).GetAwaiter().GetResult();
var result = await environment.RemoveNodeAsync(nodeGuid);
//return result;
return new
{
state = result
};
return new { state = result };
}
/// <summary>
/// 连接节点
/// 远程连接节点的方法调用关系
/// </summary>
/// <param name="fromNodeGuid">起始节点</param>
/// <param name="toNodeGuid">目标节点</param>
/// <param name="connectionType">连接关系</param>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)]
public async Task<object> ConnectNode(string fromNodeGuid, string toNodeGuid, string connectionType)
/// <param name="fromJunctionType">起始节点控制点</param>
/// <param name="toJunctionType">目标节点控制点</param>
/// <param name="invokeType">连接关系</param>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectInvokeNode)]
public async Task<object> ConnectInvokeNode(string fromNodeGuid,
string toNodeGuid,
string fromJunctionType,
string toJunctionType,
string invokeType)
{
if (!EnumHelper.TryConvertEnum<ConnectionInvokeType>(connectionType, out var tmpConnectionType))
if (!EnumHelper.TryConvertEnum<ConnectionInvokeType>(invokeType, out var tmpConnectionType))
{
return new
{
state = false
};
return new{ state = false};
}
//environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType);
var result = await environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid,0,0, tmpConnectionType,0);
return new
if (!EnumHelper.TryConvertEnum<JunctionType>(fromJunctionType, out var tmpFromJunctionType))
{
state = result
};
return new{ state = false};
}
if (!EnumHelper.TryConvertEnum<JunctionType>(toJunctionType, out var tmpToJunctionType))
{
return new{ state = false};
}
// 检查控制点类别,判断此次连接请求是否符合预期
if (tmpFromJunctionType == JunctionType.Execute)
{
if (tmpToJunctionType == JunctionType.NextStep)
{
(fromNodeGuid, toNodeGuid) = (toNodeGuid, fromNodeGuid); // 需要反转
}
else
{
return new { state = false }; // 非预期的控制点连接
}
}
else if (tmpFromJunctionType == JunctionType.NextStep)
{
if (tmpToJunctionType == JunctionType.Execute)
{
// 顺序正确无须反转
}
else
{
return new { state = false }; // 非预期的控制点连接
}
}
else // 其它类型的控制点,排除
{
return new { state = false }; // 非预期的控制点连接
}
Console.WriteLine();
Console.WriteLine($"起始节点:{fromNodeGuid}");
Console.WriteLine($"目标节点:{toNodeGuid}");
Console.WriteLine($"链接请求:{(tmpFromJunctionType, tmpToJunctionType)}");
var result = await environment.ConnectInvokeNodeAsync(fromNodeGuid, toNodeGuid, tmpFromJunctionType, tmpToJunctionType, tmpConnectionType);
return new { state = result };
}
/// <summary>
/// 移除连接关系
/// 远程移除节点的方法调用关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="connectionType">连接关系</param>
/// <exception cref="NotImplementedException"></exception>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)]
public async Task<object> RemoveConnect(string fromNodeGuid, string toNodeGuid, string connectionType)
/// <param name="invokeType">连接关系</param>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveInvokeConnect)]
public async Task<object> RemoveInvokeConnect(string fromNodeGuid, string toNodeGuid, string invokeType)
{
if (!EnumHelper.TryConvertEnum<ConnectionInvokeType>(connectionType, out var tmpConnectionType))
if (!EnumHelper.TryConvertEnum<ConnectionInvokeType>(invokeType, out var tmpConnectionType))
{
return new
{
state = false
};
}
var result = await environment.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType);
return new { state = result };
}
var result = await environment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, tmpConnectionType);
/// <summary>
/// 远程连接节点的参数传递关系
/// </summary>
/// <param name="fromNodeGuid">起始节点</param>
/// <param name="toNodeGuid">目标节点</param>
/// <param name="fromJunctionType">起始节点控制点</param>
/// <param name="toJunctionType">目标节点控制点</param>
/// <param name="argSourceType">入参参数来源类型</param>
/// <param name="argIndex">第几个参数</param>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectArgSourceNode)]
public async Task<object> ConnectArgSourceNode(string fromNodeGuid,
string toNodeGuid,
string fromJunctionType,
string toJunctionType,
string argSourceType,
int argIndex)
{
if (argIndex < 0 || argIndex > 65535) // 下标不合法
{
return new { state = false };
}
// 检查字面量是否可转换枚举类型
if (!EnumHelper.TryConvertEnum<ConnectionArgSourceType>(argSourceType, out var tmpArgSourceType))
{
return new { state = false };
}
if (!EnumHelper.TryConvertEnum<JunctionType>(fromJunctionType, out var tmpFromJunctionType))
{
return new { state = false };
}
if (!EnumHelper.TryConvertEnum<JunctionType>(toJunctionType, out var tmpToJunctionType))
{
return new { state = false };
}
// 检查控制点类别,判断此次连接请求是否符合预期
if (tmpFromJunctionType == JunctionType.ArgData)
{
if (tmpToJunctionType == JunctionType.ReturnData)
{
(fromNodeGuid, toNodeGuid) = (toNodeGuid, fromNodeGuid);// 需要反转
}
else
{
return new { state = false }; // 非预期的控制点连接
}
}
else if (tmpFromJunctionType == JunctionType.ReturnData)
{
if (tmpToJunctionType == JunctionType.ArgData)
{
// 顺序正确无须反转
}
else
{
return new { state = false }; // 非预期的控制点连接
}
}
else // 其它类型的控制点,排除
{
return new { state = false }; // 非预期的控制点连接
}
//Console.WriteLine();
//Console.WriteLine($"起始节点:{fromNodeGuid}");
//Console.WriteLine($"目标节点:{toNodeGuid}");
//Console.WriteLine($"链接请求:{(tmpFromJunctionType, tmpToJunctionType)}");
// 调用环境接口进行连接
var result = await environment.ConnectArgSourceNodeAsync(fromNodeGuid, toNodeGuid, tmpFromJunctionType, tmpToJunctionType, tmpArgSourceType, argIndex);
return new { state = result };
}
/// <summary>
/// 远程移除节点的参数传递关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="argIndex">目标节点的第几个参数</param>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveArgSourceConnect)]
public async Task<object> RemoveArgSourceConnect(string fromNodeGuid, string toNodeGuid, int argIndex)
{
var result = await environment.RemoveConnectArgSourceAsync(fromNodeGuid, toNodeGuid, argIndex);
return new
{
state = result
@@ -408,18 +531,14 @@ namespace Serein.NodeFlow.Env
/// 中断指定节点,并指定中断等级。
/// </summary>
/// <param name="nodeGuid">被中断的目标节点Guid</param>
/// <param name="interruptClass">中断级别</param>
/// <param name="isInterrupt">是否中断</param>
/// <returns>操作是否成功</returns>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.SetNodeInterrupt)]
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, string interruptClass)
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, bool isInterrupt)
{
if (!EnumHelper.TryConvertEnum<InterruptClass>(interruptClass, out var @class))
{
return false;
}
return await this.environment.SetNodeInterruptAsync(nodeGuid, @class);
return await this.environment.SetNodeInterruptAsync(nodeGuid, isInterrupt);
}

View File

@@ -1,8 +1,11 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.FlowNode;
using Serein.Library.Utils;
using Serein.NodeFlow.Tool;
using System.Collections.Concurrent;
using System.Security.AccessControl;
using System.Threading.Channels;
namespace Serein.NodeFlow.Env
{
@@ -16,21 +19,21 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 连接到远程环境后切换到的环境接口实现
/// </summary>
/// <param name="RemoteEnvControl">连接到远程环境后,本地环境自动切换到对应的环境实体</param>
/// <param name="remoteMsgUtil">连接到远程环境后,本地环境自动切换到对应的环境实体</param>
/// <param name="uIContextOperation">远程环境下需要操作UI线程时所提供的线程上下文封装工具</param>
public RemoteFlowEnvironment(RemoteEnvControl RemoteEnvControl, UIContextOperation uIContextOperation)
public RemoteFlowEnvironment(RemoteMsgUtil remoteMsgUtil, UIContextOperation uIContextOperation)
{
this.UIContextOperation = uIContextOperation;
remoteEnvControl = RemoteEnvControl;
msgClient = new MsgControllerOfClient(this, RemoteEnvControl.SendAsync);
RemoteEnvControl.EnvClient.MsgHandleHelper.AddModule(msgClient, (ex, send) =>
RemoteMsgUtil = remoteMsgUtil;
msgClient = new MsgControllerOfClient(this, remoteMsgUtil.SendAsync); // 这里提供的是主动发送消息的方法
remoteMsgUtil.EnvClient.MsgHandleHelper.AddModule(msgClient, (ex, send) =>
{
Console.WriteLine(ex);
});
}
//private readonly Func<string, object?, Task> SendCommandAsync;
private readonly RemoteEnvControl remoteEnvControl;
private readonly RemoteMsgUtil RemoteMsgUtil;
private readonly MsgControllerOfClient msgClient;
private readonly ConcurrentDictionary<string, MethodDetails> MethodDetailss = [];
@@ -70,6 +73,12 @@ namespace Serein.NodeFlow.Env
public IFlowEnvironment CurrentEnv => this;
public UIContextOperation UIContextOperation { get; }
/// <summary>
/// 标示是否正在加载项目
/// </summary>
private bool IsLoadingProject = false;
public void SetConsoleOut()
{
var logTextWriter = new LogTextWriter(msg =>
@@ -102,9 +111,9 @@ namespace Serein.NodeFlow.Env
public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath)
{
//Console.WriteLine("远程环境尚未实现的接口LoadProject");
// dll面板
Console.WriteLine("加载远程环境");
IsLoadingProject = true;
#region DLL功能区创建
var libmds = flowEnvInfo.LibraryMds;
foreach (var lib in libmds)
{
@@ -114,21 +123,18 @@ namespace Serein.NodeFlow.Env
FilePath = "Remote",
};
var mdInfos = lib.Mds.ToList();
//OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos)); // 通知UI创建dll面板显示
UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos))); // 通知UI创建dll面板显示
foreach (var mdInfo in mdInfos)
{
MethodDetailss.TryAdd(mdInfo.MethodName, new MethodDetails(mdInfo)); // 从DLL读取时生成元数据
}
}
//flowSemaphore.
#endregion
#region
var projectData = flowEnvInfo.Project;
List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>();
// 加载节点
foreach (var nodeInfo in projectData.Nodes)
{
@@ -139,7 +145,6 @@ namespace Serein.NodeFlow.Env
}
else
{
MethodDetails? methodDetails = null;
if (!string.IsNullOrEmpty(nodeInfo.MethodName))
{
@@ -167,7 +172,9 @@ namespace Serein.NodeFlow.Env
}
}
}
#endregion
#region
// 加载区域子项
foreach ((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes)
{
@@ -183,7 +190,9 @@ namespace Serein.NodeFlow.Env
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)));
}
}
#endregion
#region
// 加载节点
foreach ((NodeModelBase nodeModel, PositionOfUI position) item in ordinaryNodes)
{
@@ -202,60 +211,84 @@ namespace Serein.NodeFlow.Env
//OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position));
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)));
}
#endregion
// 确定节点之间的连接关系
#region
_ = Task.Run(async () =>
{
await Task.Delay(250);
foreach (var nodeInfo in projectData.Nodes)
{
if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
await Task.Delay(500);
#region
foreach (var nodeInfo in projectData.Nodes)
{
// 不存在对应的起始节点
continue;
}
if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
{
// 不存在对应的起始节点
continue;
}
List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes),
List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes),
(ConnectionInvokeType.IsFail, nodeInfo.FalseNodes),
(ConnectionInvokeType.IsError, nodeInfo.ErrorNodes),
(ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)];
List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
.Select(info => (info.connectionType,
info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid])
.ToArray()))
.ToList();
// 遍历每种类型的节点分支(四种)
foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in fromNodes)
{
// 遍历当前类型分支的节点(确认连接关系)
foreach (var toNode in item.toNodes)
List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
.Select(info => (info.connectionType,
info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid])
.ToArray()))
.ToList();
// 遍历每种类型的节点分支(四种)
foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in fromNodes)
{
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
toNode.Guid,
JunctionOfConnectionType.Invoke,
item.connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点
//OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
// toNode.Guid,
// item.connectionType,
// NodeConnectChangeEventArgs.ConnectChangeType.Create)); //
// 遍历当前类型分支的节点(确认连接关系)
foreach (var toNode in item.toNodes)
{
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
toNode.Guid,
JunctionOfConnectionType.Invoke,
item.connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点
}
}
}
}
});
#endregion
SetStartNode(projectData.StartNode);
#region
foreach (var toNode in NodeModels.Values)
{
if(toNode.MethodDetails.ParameterDetailss is null)
{
continue;
}
for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++)
{
var pd = toNode.MethodDetails.ParameterDetailss[i];
if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid)
&& NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode))
{
UIContextOperation?.Invoke(() =>
OnNodeConnectChange?.Invoke(
new NodeConnectChangeEventArgs(
fromNode.Guid, // 从哪个节点开始
toNode.Guid, // 连接到那个节点
JunctionOfConnectionType.Arg,
(int)pd.Index, // 连接线的样式类型
pd.ArgDataSourceType,
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
))); // 通知UI
}
}
}
#endregion
});
#endregion
SetStartNode(projectData.StartNode); // 设置流程起点
UIContextOperation?.Invoke(() =>
{
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs());
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); // 加载完成
});
IsLoadingProject = false;
}
private bool TryAddNode(NodeModelBase nodeModel)
{
@@ -339,15 +372,12 @@ namespace Serein.NodeFlow.Env
public async Task<FlowEnvInfo> GetEnvInfoAsync()
{
var envInfo = await msgClient.SendAndWaitDataAsync<FlowEnvInfo>(EnvMsgTheme.GetEnvInfo);
return envInfo;
}
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
{
await Console.Out.WriteLineAsync("远程环境尚未实现的接口ConnectRemoteEnv");
return (false, null);
@@ -436,32 +466,204 @@ namespace Serein.NodeFlow.Env
return null;
}
public async Task<bool> ConnectNodeAsync(string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionInvokeType connectionType,
int argIndex = 0)
/// <summary>
/// 在两个节点之间创建方法调用关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="fromNodeJunctionType">起始节点控制点</param>
/// <param name="toNodeJunctionType">目标节点控制点</param>
/// <param name="invokeType">决定了方法执行后的后继行为</param>
public async Task<bool> ConnectInvokeNodeAsync(string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionInvokeType invokeType)
{
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ConnectNode, new
if (fromNodeJunctionType == JunctionType.Execute)
{
fromNodeGuid,
toNodeGuid,
fromNodeJunctionType = fromNodeJunctionType.ToString(),
toNodeJunctionType = toNodeJunctionType.ToString(),
connectionType = connectionType.ToString(),
});
if (toNodeJunctionType == JunctionType.NextStep)
{
(fromNodeGuid, toNodeGuid) = (toNodeGuid, fromNodeGuid);// 需要反转
}
else
{
return false; // 非预期的控制点连接
}
}
else if (fromNodeJunctionType == JunctionType.NextStep)
{
if (toNodeJunctionType == JunctionType.Execute)
{
// 顺序正确无须反转
}
else
{
return false; // 非预期的控制点连接
}
}
else // 其它类型的控制点,排除
{
return false; // 非预期的控制点连接
}
var sendObj = new
{
fromNodeGuid = fromNodeGuid,
toNodeGuid = toNodeGuid,
fromJunctionType = fromNodeJunctionType.ToString(),
toJunctionType = toNodeJunctionType.ToString(),
invokeType = invokeType.ToString(),
};
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ConnectInvokeNode, sendObj);
if (result)
{
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
toNodeGuid,
JunctionOfConnectionType.Invoke,
connectionType,
invokeType,
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
}
return result;
}
/// <summary>
/// 在两个节点之间创建参数传递关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="fromNodeJunctionType">起始节点控制点</param>
/// <param name="toNodeJunctionType">目标节点控制点</param>
/// <param name="argSourceType">决定了方法参数来源</param>
/// <param name="argIndex">设置第几个参数</param>
public async Task<bool> ConnectArgSourceNodeAsync(string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionArgSourceType argSourceType,
int argIndex = 0)
{
// 正确的顺序:起始节点[返回值控制点] 向 目标节点[入参控制点] 发起连接
//Console.WriteLine();
//Console.WriteLine($"起始节点:{fromNodeGuid}");
//Console.WriteLine($"目标节点:{toNodeGuid}");
//Console.WriteLine($"链接请求:{(fromNodeJunctionType, toNodeJunctionType)}");
//Console.WriteLine((fromNodeJunctionType, toNodeJunctionType));
if (fromNodeJunctionType == JunctionType.ArgData)
{
if (toNodeJunctionType == JunctionType.ReturnData)
{
(fromNodeGuid, toNodeGuid) = (toNodeGuid, fromNodeGuid);// 需要反转
}
else
{
return false; // 非预期的控制点连接
}
}
else if (fromNodeJunctionType == JunctionType.ReturnData)
{
if (toNodeJunctionType == JunctionType.ArgData)
{
// 顺序正确无须反转
}
else
{
return false; // 非预期的控制点连接
}
}
else // 其它类型的控制点,排除
{
return false; // 非预期的控制点连接
}
var sendObj = new
{
fromNodeGuid = fromNodeGuid,
toNodeGuid = toNodeGuid,
fromJunctionType = fromNodeJunctionType.ToString(),
toJunctionType = toNodeJunctionType.ToString(),
argSourceType = argSourceType.ToString(),
argIndex = argIndex,
};
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ConnectArgSourceNode, sendObj);
if (result)
{
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
toNodeGuid,
JunctionOfConnectionType.Arg,
argIndex,
argSourceType,
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
}
return result;
}
/// <summary>
/// 移除两个节点之间的方法调用关系
/// </summary>
/// <param name="fromNodeGuid">起始节点</param>
/// <param name="toNodeGuid">目标节点</param>
/// <param name="invokeType">连接类型</param>
public async Task<bool> RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType invokeType)
{
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveInvokeConnect, new
{
fromNodeGuid = fromNodeGuid,
toNodeGuid = toNodeGuid,
invokeType = invokeType.ToString(),
});
if (result)
{
UIContextOperation.Invoke(() =>
{
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
toNodeGuid,
JunctionOfConnectionType.Invoke,
invokeType,
NodeConnectChangeEventArgs.ConnectChangeType.Remote));
});
}
return result;
}
/// <summary>
/// 移除连接节点之间参数传递的关系
/// </summary>
/// <param name="fromNodeGuid">起始节点Guid</param>
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="argIndex">连接到第几个参数</param>
public async Task<bool> RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex)
{
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveArgSourceConnect, new
{
fromNodeGuid = fromNodeGuid,
toNodeGuid = toNodeGuid,
argIndex = argIndex,
});
if (result)
{
UIContextOperation.Invoke(() =>
{
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
toNodeGuid,
JunctionOfConnectionType.Arg,
argIndex,
ConnectionArgSourceType.GetPreviousNodeData,
NodeConnectChangeEventArgs.ConnectChangeType.Remote)); // 通知UI
});
}
return result;
}
/// <summary>
/// 创建节点/区域/基础控件
/// </summary>
/// <param name="nodeType">节点/区域/基础控件类型</param>
/// <param name="position">节点在画布上的位置(</param>
/// <param name="methodDetailsInfo">节点绑定的方法说明</param>
public async Task<NodeInfo> CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null)
{
var nodeInfo = await msgClient.SendAndWaitDataAsync<NodeInfo>(EnvMsgTheme.CreateNode, new
@@ -489,29 +691,6 @@ namespace Serein.NodeFlow.Env
});
return nodeInfo;
}
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
{
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveConnect, new
{
fromNodeGuid,
toNodeGuid,
connectionType = connectionType.ToString(),
});
if (result)
{
UIContextOperation.Invoke(() =>
{
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
toNodeGuid,
JunctionOfConnectionType.Invoke,
connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Remote));
});
}
return result;
}
public async Task<bool> RemoveNodeAsync(string nodeGuid)
{
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveNode, new
@@ -548,13 +727,13 @@ namespace Serein.NodeFlow.Env
});
}
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass)
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, bool isInterrupt)
{
var state = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.SetNodeInterrupt, // 设置节点中断
new
{
nodeGuid,
interruptClass = interruptClass.ToString(),
isInterrupt,
});
return state;
}
@@ -638,12 +817,13 @@ namespace Serein.NodeFlow.Env
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
{
//Console.WriteLine($"通知远程环境修改节点数据:{nodeGuid},name:{path},value:{value}");
_ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new
{
nodeGuid = nodeGuid,
path = path,
value = value.ToString(),
});
//_ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new
//{
// nodeGuid = nodeGuid,
// path = path,
// value = value.ToString(),
//});
}
}

View File

@@ -46,13 +46,27 @@ namespace Serein.NodeFlow
/// <returns></returns>
public async Task StartFlowInSelectNodeAsync(IFlowEnvironment env, NodeModelBase startNode)
{
IDynamicContext Context;
IDynamicContext context;
#if NET6_0_OR_GREATER
Context = new Serein.Library.Core.NodeFlow.DynamicContext(env); // 从起始节点启动流程时创建上下文
context = new Serein.Library.Core.NodeFlow.DynamicContext(env); // 从起始节点启动流程时创建上下文
#else
Context = new Serein.Library.Framework.NodeFlow.DynamicContext(env);
#endif
await startNode.StartFlowAsync(Context); // 开始运行时从选定节点开始运行
await startNode.StartFlowAsync(context); // 开始运行时从选定节点开始运行
context.Exit();
/*
foreach (var node in NodeModels.Values)
{
if (node is not null)
{
node.ReleaseFlowData(); // 退出时释放对象
}
}
*/
}
@@ -360,7 +374,7 @@ namespace Serein.NodeFlow
if (nextNodes[i].DebugSetting.IsEnable)
{
nextNodes[i].PreviousNode = singleFlipFlopNode;
if (nextNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前
{
var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");

View File

@@ -1,61 +0,0 @@
using Serein.Library;
using Serein.Library.Api;
namespace Serein.NodeFlow.Model
{
/// <summary>
/// 组合动作节点(用于动作区域)
/// </summary>
public class CompositeActionNode : NodeModelBase
{
public List<SingleActionNode> ActionNodes;
/// <summary>
/// 组合动作节点(用于动作区域)
/// </summary>
public CompositeActionNode(IFlowEnvironment environment, List<SingleActionNode> actionNodes):base(environment)
{
ActionNodes = actionNodes;
}
//public override async Task<object?> Executing(IDynamicContext context)
public override Task<object?> ExecutingAsync(IDynamicContext context)
{
throw new NotImplementedException("动作区域暂未实现");
}
public override Parameterdata[] GetParameterdatas()
{
return [];
}
public override NodeInfo? ToInfo()
{
if (MethodDetails is null) return null;
var trueNodes = SuccessorNodes[ConnectionInvokeType.IsSucceed].Select(item => item.Guid); // 真分支
var falseNodes = SuccessorNodes[ConnectionInvokeType.IsFail].Select(item => item.Guid);// 假分支
var errorNodes = SuccessorNodes[ConnectionInvokeType.IsError].Select(item => item.Guid);// 异常分支
var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支
// 生成参数列表
Parameterdata[] parameterData = GetParameterdatas();
return new NodeInfo
{
Guid = Guid,
MethodName = MethodDetails?.MethodName,
Label = DisplayName ?? "",
Type = this.GetType().ToString(),
TrueNodes = trueNodes.ToArray(),
FalseNodes = falseNodes.ToArray(),
UpstreamNodes = upstreamNodes.ToArray(),
ParameterData = parameterData.ToArray(),
ErrorNodes = errorNodes.ToArray(),
ChildNodeGuids = ActionNodes.Select(node => node.Guid).ToArray(),
};
}
}
}

View File

@@ -58,7 +58,8 @@ namespace Serein.NodeFlow.Model
break;
}
}
return Task.FromResult( PreviousNode?.GetFlowData()); // 条件区域透传上一节点的数据
return Task.FromResult(context.GetFlowData(PreviousNode.Guid)); // 条件区域透传上一节点的数据
}
@@ -78,7 +79,7 @@ namespace Serein.NodeFlow.Model
}
}
public override Parameterdata[] GetParameterdatas()
public override ParameterData[] GetParameterdatas()
{
return [];
}
@@ -97,7 +98,7 @@ namespace Serein.NodeFlow.Model
var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支
// 生成参数列表
Parameterdata[] parameterData = GetParameterdatas();
ParameterData[] parameterData = GetParameterdatas();
return new NodeInfo
{

View File

@@ -1,6 +0,0 @@
namespace Serein.NodeFlow.Model
{
//public class CompositeLoopNode : NodeBase
//{
//}
}

View File

@@ -1,5 +1,6 @@
using Serein.Library.Api;
using Serein.Library;
using System.Security.AccessControl;
namespace Serein.NodeFlow.Model
{
@@ -12,13 +13,15 @@ namespace Serein.NodeFlow.Model
{
}
public override Parameterdata[] GetParameterdatas()
public override ParameterData[] GetParameterdatas()
{
if (base.MethodDetails.ParameterDetailss.Length > 0)
{
return MethodDetails.ParameterDetailss
.Select(it => new Parameterdata
.Select(it => new ParameterData
{
SourceNodeGuid = it.ArgDataSourceNodeGuid,
SourceType = it.ArgDataSourceType.ToString(),
State = it.IsExplicitData,
Value = it.DataValue,
})

View File

@@ -50,7 +50,7 @@ namespace Serein.NodeFlow.Model
{
// 接收上一节点参数or自定义参数内容
object? parameter;
object? result = PreviousNode?.GetFlowData(); // 条件节点透传上一节点的数据
object? result = context.GetFlowData(PreviousNode.Guid); // 条件节点透传上一节点的数据
if (IsCustomData) // 是否使用自定义参数
{
// 表达式获取上一节点数据
@@ -88,7 +88,7 @@ namespace Serein.NodeFlow.Model
return Task.FromResult(result);
}
public override Parameterdata[] GetParameterdatas()
public override ParameterData[] GetParameterdatas()
{
var value = CustomData switch
{
@@ -99,7 +99,7 @@ namespace Serein.NodeFlow.Model
Type when CustomData.GetType() == typeof(bool) => ((bool)CustomData).ToString(),
_ => CustomData?.ToString()!,
};
return [new Parameterdata
return [new ParameterData
{
State = IsCustomData,
Expression = Expression,
@@ -114,11 +114,10 @@ namespace Serein.NodeFlow.Model
this.Position = nodeInfo.Position;// 加载位置信息
for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
{
Parameterdata? pd = nodeInfo.ParameterData[i];
ParameterData? pd = nodeInfo.ParameterData[i];
node.IsCustomData = pd.State;
node.CustomData = pd.Value;
node.Expression = pd.Expression;
}
return this;
}

View File

@@ -31,7 +31,7 @@ namespace Serein.NodeFlow.Model
//public override async Task<object?> Executing(IDynamicContext context)
public override Task<object?> ExecutingAsync(IDynamicContext context)
{
var data = PreviousNode?.GetFlowData(); // 表达式节点使用上一节点数据
var data = context.GetFlowData(PreviousNode.Guid); // 表达式节点使用上一节点数据
try
{
@@ -59,9 +59,9 @@ namespace Serein.NodeFlow.Model
}
public override Parameterdata[] GetParameterdatas()
public override ParameterData[] GetParameterdatas()
{
return [new Parameterdata { Expression = Expression }];
return [new ParameterData { Expression = Expression }];
}

View File

@@ -26,7 +26,7 @@ namespace Serein.NodeFlow.Model
public override async Task<object?> ExecutingAsync(IDynamicContext context)
{
#region
if (DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
if (DebugSetting.IsInterrupt) // 执行触发前
{
string guid = this.Guid.ToString();
var cancelType = await this.DebugSetting.GetInterruptTask();
@@ -82,13 +82,15 @@ namespace Serein.NodeFlow.Model
/// 获取触发器参数
/// </summary>
/// <returns></returns>
public override Parameterdata[] GetParameterdatas()
public override ParameterData[] GetParameterdatas()
{
if (base.MethodDetails.ParameterDetailss.Length > 0)
{
return MethodDetails.ParameterDetailss
.Select(it => new Parameterdata
.Select(it => new ParameterData
{
SourceNodeGuid = it.ArgDataSourceNodeGuid,
SourceType = it.ArgDataSourceType.ToString(),
State = it.IsExplicitData,
Value = it.DataValue
})

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.17</Version>
<Version>1.0.18</Version>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@@ -60,11 +60,6 @@
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Collections.NonGeneric" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj" OutputItemType="Analyzer" />

View File

@@ -3,6 +3,7 @@ using Serein.Library.Utils;
using Serein.Library;
using System.Collections.Concurrent;
using System.Reflection;
using Serein.Library.FlowNode;
namespace Serein.NodeFlow.Tool;

View File

@@ -33,7 +33,6 @@ namespace Serein.Library
/// 节点的调试设置
/// </summary>
DebugSetting,
}
@@ -68,6 +67,12 @@ namespace Serein.Library
/// 是否禁止参数进行修改(初始化后不能再通过 Setter 修改)
/// </summary>
public bool IsProtection = false;
/// <summary>
/// 自定义代码
/// </summary>
public string CustomCode = null;
}
}

View File

@@ -163,7 +163,7 @@ namespace Serein.Library.NodeGenerator
var propertyName = field.ToPropertyName(); // 转为合适的属性名称
var attributeInfo = fieldKV.Value; // 缓存的特性信息
var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), "true"); // 是否为保护字段
var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), value => bool.Parse(value)); // 是否为保护字段
// 生成 getter / setter
sb.AppendLine(leadingTrivia);
@@ -174,11 +174,12 @@ namespace Serein.Library.NodeGenerator
sb.AppendLine(" {");
sb.AppendLine($" if ({fieldName} {(isProtection ? "== default" : "!= value")})"); // 非保护的Setter
sb.AppendLine(" {");
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsPrint), "true")) // 是否打印
sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); // 通知UI属性发生改变了");
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsPrint), value => bool.Parse(value))) // 是否打印
{
sb.AddCode(5, $"Console.WriteLine({fieldName});");
}
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsNotification), "true")) // 是否通知
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsNotification), value => bool.Parse(value))) // 是否通知
{
if (classInfo.ExitsPath(nameof(NodeValuePath.Node))) // 节点 or 自定义节点
{
@@ -197,7 +198,12 @@ namespace Serein.Library.NodeGenerator
sb.AddCode(5, $"NodeModel?.Env?.NotificationNodeValueChangeAsync(NodeModel.Guid, \"DebugSetting.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了");
}
}
sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); // 通知UI属性发生改变了");
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.CustomCode), value => !string.IsNullOrEmpty(value))) // 是否打印
{
var customCode = attributeInfo[nameof(PropertyInfo)][nameof(PropertyInfo.CustomCode)] as string;
customCode = customCode.Trim().Substring(1, customCode.Length - 2);
sb.AddCode(5, $"{customCode} // 添加的自定义代码");
}
//sb.AppendLine($" {fieldName} = value;");
//sb.AppendLine($" OnPropertyChanged(); // 通知UI属性发生改变了");
sb.AppendLine(" }");
@@ -510,7 +516,7 @@ namespace Serein.Library.NodeGenerator
public static bool Search(this Dictionary<string, Dictionary<string, string>> dict,
string attributeName = null,
string attributePropertyName = null,
string comparisonValue = null)
Func<string, bool> judgeFunc = null)
{
if (string.IsNullOrWhiteSpace(attributeName))
return false;
@@ -521,9 +527,11 @@ namespace Serein.Library.NodeGenerator
return true;
if (!abs.TryGetValue(attributePropertyName, out var absValue))
return false;
if (string.IsNullOrWhiteSpace(comparisonValue))
if (judgeFunc == null)
return true;
return absValue.Equals(comparisonValue);
return judgeFunc.Invoke(absValue); ;
//return absValue.Equals(comparisonValue);
}

View File

@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>1.0.1</Version>
<IsRoslynComponent>true</IsRoslynComponent>
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
@@ -14,6 +15,12 @@
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<Compile Remove="bin\**" />
<EmbeddedResource Remove="bin\**" />
<None Remove="bin\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" PrivateAssets="all" />

View File

@@ -13,7 +13,7 @@ namespace Serein.Workbench
void LoadLocalProject()
{
#if DEBUG
if (1 == 1)
if (1 == 11)
{
string filePath;
filePath = @"F:\临时\project\linux\project.dnf";

View File

@@ -1,18 +1,26 @@
<Window x:Class="Serein.Workbench.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.Workbench"
xmlns:tool="clr-namespace:Serein.Workbench.Tool.Converters"
xmlns:nodeView="clr-namespace:Serein.Workbench.Node.View"
xmlns:themes="clr-namespace:Serein.Workbench.Themes"
xmlns:converters="clr-namespace:Serein.Workbench.Tool.Converters"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:MainWindowViewModel}"
Title="Dynamic Node Flow" Height="900" Width="1400"
AllowDrop="True" Drop="Window_Drop" DragOver="Window_DragOver"
AllowDrop="True"
Drop="Window_Drop"
DragOver="Window_DragOver"
Loaded="Window_Loaded"
ContentRendered="Window_ContentRendered"
PreviewKeyDown="Window_PreviewKeyDown"
Closing="Window_Closing">
<Window.Resources>
<converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
<tool:RightThumbPositionConverter x:Key="RightThumbPositionConverter" />
<tool:BottomThumbPositionConverter x:Key="BottomThumbPositionConverter" />
<tool:VerticalCenterThumbPositionConverter x:Key="VerticalCenterThumbPositionConverter" />
@@ -35,7 +43,7 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Menu DockPanel.Dock="Top" Grid.Row="0" Height="20">
<Menu DockPanel.Dock="Top" Grid.Row="0" Grid.ColumnSpan="5" Height="20">
<MenuItem Header="项目">
<!--菜单项为MenuItem文字使用属性 Header-->
<MenuItem Header="保存项目" Click="ButtonSaveFile_Click" ></MenuItem>
@@ -99,7 +107,7 @@
<GridSplitter Grid.Row="1" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
<Grid Grid.Row="1" Grid.Column="2" x:Name="FlowChartStackGrid">
<StackPanel x:Name="FlowChartStackPanel"
ClipToBounds="True">
@@ -199,6 +207,25 @@ Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Con
</StackPanel>
<StackPanel>
<StackPanel x:Name="CreateNodeInvoke"
Margin="14" Width="auto" HorizontalAlignment="Left" Background="White" Opacity="0.7"
Visibility="{Binding IsConnectionInvokeNode,
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" >
<TextBlock Margin="8,2,8,0" Foreground="#FF2727" FontSize="14" Text="正在设置调用关系"/>
<TextBlock Margin="8,0,8,0" Foreground="#4A82E4" FontSize="14" Text=" 按 1 切换:上游分支"/>
<TextBlock Margin="8,0,8,0" Foreground="#04FC10" FontSize="14" Text=" 按 2 切换Succeed 分支"/>
<TextBlock Margin="8,0,8,0" Foreground="#F18905" FontSize="14" Text=" 按 3 切换Fail 分支"/>
<TextBlock Margin="8,0,8,2" Foreground="#FE1343" FontSize="14" Text=" 按 4 切换:异常分支"/>
</StackPanel>
<StackPanel Margin="14" Width="auto" HorizontalAlignment="Left" Background="White" Opacity="0.9"
Visibility="{Binding IsConnectionArgSourceNode,
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" >
<TextBlock Margin="8,2,8,0" Foreground="#FF2727" FontSize="14" Text="正在设置参数传递关系"/>
<TextBlock Margin="8,0,8,0" Foreground="#56CEF6" FontSize="14" Text=" 按 1 切换:调用时获取指定节点的返回值"/>
<TextBlock Margin="8,0,8,2" Foreground="#B06BBB" FontSize="14" Text=" 按 2 切换:调用时立刻调用指定节点,使用其返回值作为入参参数"/>
</StackPanel>
</StackPanel>
</Grid>
<GridSplitter Grid.Row="1" Grid.Column="3" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
<!--IOC容器属性-->

View File

@@ -5,6 +5,7 @@ using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.Library.Utils.SereinExpression;
using Serein.NodeFlow.Tool;
using Serein.Workbench.Extension;
using Serein.Workbench.Node;
using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel;
@@ -83,7 +84,7 @@ namespace Serein.Workbench
/// <summary>
/// 标记是否正在进行连接操作
/// </summary>
private bool IsConnecting;
//private bool IsConnecting;
/// <summary>
/// 标记是否正在拖动控件
/// </summary>
@@ -116,7 +117,7 @@ namespace Serein.Workbench
/// <summary>
/// 记录开始连接的文本块
/// </summary>
private NodeControlBase? startConnectNodeControl;
//private NodeControlBase? startConnectNodeControl;
/// <summary>
/// 当前正在绘制的连接线
/// </summary>
@@ -124,7 +125,7 @@ namespace Serein.Workbench
/// <summary>
/// 当前正在绘制的真假分支属性
/// </summary>
private ConnectionInvokeType currentConnectionType;
//private ConnectionInvokeType currentConnectionType;
/// <summary>
@@ -144,36 +145,38 @@ namespace Serein.Workbench
public MainWindow()
{
InitializeComponent();
ViewModel = new MainWindowViewModel(this);
this.DataContext = ViewModel;
InitializeComponent();
EnvDecorator = ViewModel.FlowEnvironment;
ViewObjectViewer.FlowEnvironment = EnvDecorator;
IOCObjectViewer.FlowEnvironment = EnvDecorator;
IOCObjectViewer.SelectObj += ViewObjectViewer.LoadObjectInformation;
//this.FlowEnvironment.SetConsoleOut((msg) => LogOutWindow.AppendText(msg), () => LogOutWindow.Clear()); // 设置输出
InitFlowEnvironmentEvent(); // 配置环境事件
#region
canvasTransformGroup = new TransformGroup();
scaleTransform = new ScaleTransform();
translateTransform = new TranslateTransform();
canvasTransformGroup.Children.Add(scaleTransform);
canvasTransformGroup.Children.Add(translateTransform);
FlowChartCanvas.RenderTransform = canvasTransformGroup;
#endregion
InitFlowEnvironmentEvent(); // 配置环境事件
if (App.FlowProjectData is not null)
{
EnvDecorator.LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); // 加载项目
}
IOCObjectViewer.SelectObj += ViewObjectViewer.LoadObjectInformation;
}
/// <summary>
/// 初始化环境事件
/// </summary>
@@ -229,8 +232,6 @@ namespace Serein.Workbench
}
#region
private void Window_Loaded(object sender, RoutedEventArgs e)
{
@@ -382,13 +383,14 @@ namespace Serein.Workbench
JunctionControlBase startJunction = IFormJunction.NextStepJunction;
JunctionControlBase endJunction = IToJunction.ExecuteJunction;
// 添加连接
var connection = new ConnectionControl(
FlowChartCanvas,
connectionType,
startJunction,
endJunction,
() => EnvDecorator.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connectionType)
() => EnvDecorator.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, connectionType)
);
if (toNodeControl is FlipflopNodeControl flipflopControl
@@ -400,7 +402,7 @@ namespace Serein.Workbench
Connections.Add(connection);
fromNodeControl.AddCnnection(connection);
toNodeControl.AddCnnection(connection);
EndConnection();
EndConnection(); // 环境触发了创建节点连接事件
}
@@ -440,7 +442,7 @@ namespace Serein.Workbench
JunctionControlBase startJunction = eventArgs.ConnectionArgSourceType switch
{
ConnectionArgSourceType.GetPreviousNodeData => IFormJunction.ExecuteJunction, // 自身节点
ConnectionArgSourceType.GetPreviousNodeData => IFormJunction.ReturnDataJunction, // 自身节点
ConnectionArgSourceType.GetOtherNodeData => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
ConnectionArgSourceType.GetOtherNodeDataOfInvoke => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
_ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发
@@ -448,11 +450,6 @@ namespace Serein.Workbench
JunctionControlBase endJunction = IToJunction.ArgDataJunction[eventArgs.ArgIndex];
LineType lineType = LineType.Bezier;
if(eventArgs.ConnectionArgSourceType == ConnectionArgSourceType.GetPreviousNodeData)
{
lineType = LineType.Semicircle;
}
// 添加连接
var connection = new ConnectionControl(
lineType,
@@ -461,19 +458,19 @@ namespace Serein.Workbench
eventArgs.ConnectionArgSourceType,
startJunction,
endJunction,
() => EnvDecorator.RemoveConnectAsync(fromNodeGuid, toNodeGuid, 0)
() => EnvDecorator.RemoveConnectArgSourceAsync(fromNodeGuid, toNodeGuid, eventArgs.ArgIndex)
);
if (toNodeControl is FlipflopNodeControl flipflopControl
&& flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
{
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
}
//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);
EndConnection();
EndConnection(); // 环境触发了创建节点连接事件
}
@@ -481,20 +478,27 @@ namespace Serein.Workbench
{
// 需要移除连接
var removeConnections = Connections.Where(c => c.Start.MyNode.Guid.Equals(fromNodeGuid)
&& c.End.MyNode.Guid.Equals(toNodeGuid))
.ToList();
&& c.End.MyNode.Guid.Equals(toNodeGuid))
.ToList(); // 获取这两个节点之间的所有连接关系
foreach (var connection in removeConnections)
{
connection.DeleteConnection();
Connections.Remove(connection);
fromNodeControl.RemoveCnnection(connection);
toNodeControl.RemoveCnnection(connection);
if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control))
if(connection.End is ArgJunctionControl junctionControl && junctionControl.ArgIndex == eventArgs.ArgIndex)
{
JudgmentFlipFlopNode(control); // 连接关系变更时判断
// 找到符合删除条件的连接线
connection.DeleteConnection(); // 从UI层面上移除
Connections.Remove(connection); // 从本地记录中移除
fromNodeControl.RemoveCnnection(connection); // 从节点持有的记录移除
toNodeControl.RemoveCnnection(connection); // 从节点持有的记录移除
}
//if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control))
//{
// JudgmentFlipFlopNode(control); // 连接关系变更时判断
//}
}
}
#endregion
@@ -1044,15 +1048,17 @@ namespace Serein.Workbench
{
if ((s is MenuItem menuItem) && menuItem is not null)
{
if (nodeControl?.ViewModel?.NodeModel?.DebugSetting?.InterruptClass == InterruptClass.None)
if (nodeControl?.ViewModel?.NodeModel?.DebugSetting?.IsInterrupt == true)
{
await EnvDecorator.SetNodeInterruptAsync(nodeGuid, InterruptClass.Branch);
await EnvDecorator.SetNodeInterruptAsync(nodeGuid,false);
nodeControl.ViewModel.IsInterrupt = false;
menuItem.Header = "取消中断";
}
else
{
await EnvDecorator.SetNodeInterruptAsync(nodeGuid, InterruptClass.None);
nodeControl!.ViewModel!.IsInterrupt = true;
await EnvDecorator.SetNodeInterruptAsync(nodeGuid, true);
menuItem.Header = "在此中断";
}
@@ -1166,7 +1172,7 @@ namespace Serein.Workbench
#endregion
#region
#region /
/// <summary>
/// 鼠标在画布移动。
@@ -1176,36 +1182,28 @@ namespace Serein.Workbench
/// </summary>
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
{
var myData = GlobalJunctionData.MyGlobalConnectingData;
if (myData.IsCreateing && e.LeftButton == MouseButtonState.Pressed)
{
if (myData.Type == JunctionOfConnectionType.Invoke)
{
ViewModel.IsConnectionInvokeNode = true; // 正在连接节点的调用关系
if (e.LeftButton == MouseButtonState.Pressed && GlobalJunctionData.MyGlobalConnectingData is not null)
{
// 正在连接节点
//var controlPointPosition = GlobalJunctionData.MyGlobalConnectingData.StartPoint;
}
else
{
ViewModel.IsConnectionArgSourceNode = true; // 正在连接节点的调用关系
}
var currentPoint = e.GetPosition(FlowChartCanvas);
GlobalJunctionData.MyGlobalConnectingData.UpdatePoint(currentPoint);
//virtualLine.VirtualLine.UpdatePoints(currentPoint);
//virtualLine.VirtualLine.X1 = controlPointPosition.X;
//virtualLine.VirtualLine.Y1 = controlPointPosition.Y;
//virtualLine.VirtualLine.X2 = currentPoint.X;
//virtualLine.VirtualLine.Y2 = currentPoint.Y;
currentPoint.X -= 2;
currentPoint.Y -= 2;
myData.UpdatePoint(currentPoint);
return;
}
//if (IsConnecting) // 正在连接节点
//{
// Point position = e.GetPosition(FlowChartCanvas);
// if (currentLine is null || startConnectNodeControl is null)
// {
// return;
// }
// currentLine.X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2;
// currentLine.Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2;
// currentLine.X2 = position.X;
// currentLine.Y2 = position.Y;
//}
if (IsCanvasDragging && e.MiddleButton == MouseButtonState.Pressed) // 正在移动画布(按住中键)
{
Point currentMousePosition = e.GetPosition(this);
@@ -1399,8 +1397,6 @@ namespace Serein.Workbench
/// </summary>
private void Block_MouseMove(object sender, MouseEventArgs e)
{
if (IsConnecting)
return;
if (IsCanvasDragging)
return;
if (IsSelectControl)
@@ -1465,8 +1461,8 @@ namespace Serein.Workbench
}
}
// 改变对象树?
private void ChangeViewerObjOfNode(NodeControlBase nodeControl)
{
@@ -1475,7 +1471,9 @@ namespace Serein.Workbench
if (node is not null && node.MethodDetails?.ReturnType != typeof(void))
{
var key = node.Guid;
var instance = node.GetFlowData(); // 对象预览树视图获取(后期更改)
object instance = null;
//Console.WriteLine("WindowXaml 后台代码中 ChangeViewerObjOfNode 需要重新设计");
//var instance = node.GetFlowData(); // 对象预览树视图获取(后期更改)
if(instance is not null)
{
ViewObjectViewer.LoadObjectInformation(key, instance);
@@ -1505,12 +1503,12 @@ namespace Serein.Workbench
EnvDecorator.SetMonitorObjState(key, true); // 通知环境该节点的数据更新后需要传到UI
}
}
#endregion
#region UI连接控件操作
/// <summary>
/// 控件的鼠标左键松开事件,结束拖动操作,创建连线
/// 控件的鼠标左键松开事件,结束拖动操作
/// </summary>
private void Block_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
@@ -1534,52 +1532,18 @@ namespace Serein.Workbench
//GlobalJunctionData.OK();
}
/// <summary>
/// 主窗口的KeyDown事件处理用于在连接操作中按下Esc键取消连接。
/// </summary>
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape && IsConnecting)
{
this.KeyDown -= MainWindow_KeyDown;
EndConnection();
}
}
/// <summary>
/// 结束连接操作,清理状态并移除虚线。
/// </summary>
private void EndConnection()
{
IsConnecting = false;
startConnectNodeControl = null;
// 移除虚线
//if (currentLine != null)
//{
// FlowChartCanvas.Children.Remove(currentLine);
// currentLine = null;
//}
Mouse.OverrideCursor = null; // 恢复视觉效果
ViewModel.IsConnectionArgSourceNode = false;
ViewModel.IsConnectionInvokeNode = false;
GlobalJunctionData.OK();
}
/// <summary>
/// 更新与指定控件相关的所有连接的位置。
/// </summary>
//private void UpdateConnections(NodeControlBase nodeControl)
//{
// nodeControl.UpdateLocationConnections();
// //foreach (var connection in Connections)
// //{
// // if (connection.Start.MyNode.Guid == nodeControl.ViewModel.NodeModel.Guid
// // || connection.End.MyNode.Guid == nodeControl.ViewModel.NodeModel.Guid)
// // {
// // connection.RefreshLine(); // 主动更新某个控件相关的所有连接线
// // //connection.RemoveFromCanvas();
// // //BezierLineDrawer.UpdateBezierLine(FlowChartCanvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
// // }
// //}
//}
#endregion
#region
private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
@@ -1607,7 +1571,7 @@ namespace Serein.Workbench
// if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
if (e.Delta < 0 && scaleTransform.ScaleX < 0.05) return;
if (e.Delta > 0 && scaleTransform.ScaleY > 1.5) return;
if (e.Delta > 0 && scaleTransform.ScaleY > 2.0) return;
// 获取鼠标在 Canvas 内的相对位置
var mousePosition = e.GetPosition(FlowChartCanvas);
@@ -1781,7 +1745,7 @@ namespace Serein.Workbench
/// <param name="e"></param>
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (GlobalJunctionData.MyGlobalConnectingData is not null)
if (GlobalJunctionData.MyGlobalConnectingData.IsCreateing)
{
return;
}
@@ -1835,36 +1799,52 @@ namespace Serein.Workbench
// 释放鼠标捕获
FlowChartCanvas.ReleaseMouseCapture();
}
// 创建连线
if (GlobalJunctionData.MyGlobalConnectingData is not null)
if (GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing)
{
var myData = GlobalJunctionData.MyGlobalConnectingData;
GlobalJunctionData.OK();
var canvas = this.FlowChartCanvas;
if (myData.IsCanConnected)
{
var canvas = this.FlowChartCanvas;
var currentendPoint = e.GetPosition(canvas); // 当前鼠标落点
var changingJunctionPosition = myData.CurrentJunction.TranslatePoint(new Point(0, 0), canvas);
var changingJunctionRect = new Rect(changingJunctionPosition, new Size(myData.CurrentJunction.Width, myData.CurrentJunction.Height));
if (changingJunctionRect.Contains(currentendPoint)) // 可以创建连接
{
var argIndex = 0;
if(myData.StartJunction is ArgJunctionControl argJunction1)
#region
if (myData.Type == JunctionOfConnectionType.Invoke)
{
argIndex = argJunction1.ArgIndex;
this.EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType,
myData.ConnectionInvokeType);
}
#endregion
#region
else if (myData.Type == JunctionOfConnectionType.Arg)
{
var argIndex = 0;
if (myData.StartJunction is ArgJunctionControl argJunction1)
{
argIndex = argJunction1.ArgIndex;
}
else if (myData.CurrentJunction is ArgJunctionControl argJunction2)
{
argIndex = argJunction2.ArgIndex;
}
this.EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType,
myData.ConnectionArgSourceType,
argIndex);
}
#endregion
}
else if (myData.CurrentJunction is ArgJunctionControl argJunction2)
{
argIndex = argJunction2.ArgIndex;
}
this.EnvDecorator.ConnectNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType,
ConnectionInvokeType.IsSucceed,argIndex);
}
EndConnection();
}
}
@@ -2605,6 +2585,7 @@ namespace Serein.Workbench
#region -
private async void ButtonStartRemoteServer_Click(object sender, RoutedEventArgs e)
{
await this.EnvDecorator.StartRemoteServerAsync();
}
@@ -2618,12 +2599,13 @@ namespace Serein.Workbench
var windowEnvRemoteLoginView = new WindowEnvRemoteLoginView(async (addres, port, token) =>
{
ResetFlowEnvironmentEvent();// 移除事件
(var isConnect, RemoteEnvControl remoteEnvControl) = await this.EnvDecorator.ConnectRemoteEnv(addres, port, token);
InitFlowEnvironmentEvent(); // 重新添加时间(如果没有连接成功,那么依然是原本的环境)
(var isConnect, var _) = await this.EnvDecorator.ConnectRemoteEnv(addres, port, token);
InitFlowEnvironmentEvent(); // 重新添加事件(如果没有连接成功,那么依然是原本的环境)
if (isConnect)
{
// 连接成功,加载远程项目
var flowEnvInfo = await EnvDecorator.GetEnvInfoAsync();
await Task.Delay(1000);
EnvDecorator.LoadProject(flowEnvInfo, string.Empty);// 加载远程环境的项目
}
});
@@ -2635,7 +2617,7 @@ namespace Serein.Workbench
/// <summary>
/// 按键监听。esc取消操作
/// 窗体按键监听。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
@@ -2647,16 +2629,53 @@ namespace Serein.Workbench
}
if (e.KeyStates == Keyboard.GetKeyStates(Key.Escape))
//if (Keyboard.Modifiers == ModifierKeys.Shift)
{
IsConnecting = false;
IsControlDragging = false;
IsCanvasDragging = false;
EndConnection();
SelectionRectangle.Visibility = Visibility.Collapsed;
CancelSelectNode();
EndConnection();
}
if(GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing)
{
if(myData.Type == JunctionOfConnectionType.Invoke)
{
ConnectionInvokeType connectionInvokeType = e.KeyStates switch
{
KeyStates k when k == Keyboard.GetKeyStates(Key.D1) => ConnectionInvokeType.Upstream,
KeyStates k when k == Keyboard.GetKeyStates(Key.D2) => ConnectionInvokeType.IsSucceed,
KeyStates k when k == Keyboard.GetKeyStates(Key.D3) => ConnectionInvokeType.IsFail,
KeyStates k when k == Keyboard.GetKeyStates(Key.D4) => ConnectionInvokeType.IsError,
_ => ConnectionInvokeType.None,
};
if (connectionInvokeType != ConnectionInvokeType.None)
{
myData.ConnectionInvokeType = connectionInvokeType;
myData.MyLine.Line.UpdateLineColor(connectionInvokeType.ToLineColor());
}
}
else if (myData.Type == JunctionOfConnectionType.Arg)
{
ConnectionArgSourceType connectionArgSourceType = e.KeyStates switch
{
KeyStates k when k == Keyboard.GetKeyStates(Key.D1) => ConnectionArgSourceType.GetOtherNodeData,
KeyStates k when k == Keyboard.GetKeyStates(Key.D2) => ConnectionArgSourceType.GetOtherNodeDataOfInvoke,
_ => ConnectionArgSourceType.GetPreviousNodeData,
};
if (connectionArgSourceType != ConnectionArgSourceType.GetPreviousNodeData)
{
myData.ConnectionArgSourceType = connectionArgSourceType;
myData.MyLine.Line.UpdateLineColor(connectionArgSourceType.ToLineColor());
}
}
myData.CurrentJunction.InvalidateVisual(); // 刷新目标节点控制点样式
}
}
/// <summary>
@@ -2705,12 +2724,13 @@ namespace Serein.Workbench
}
ObjDynamicCreateHelper.PrintObjectProperties(result!);
Console.WriteLine( );
var exp = "@set .Addresses[1].Street = qwq";
var exp = "@set .Addresses[1].Street = 233";
var data = SerinExpressionEvaluator.Evaluate(exp, result!, out bool isChange);
exp = "@get .Addresses[1].Street";
data = SerinExpressionEvaluator.Evaluate(exp,result!, out isChange);
Console.WriteLine($"{exp} => {data}");
}
/// <summary>
/// 卸载DLL文件清空当前项目
/// </summary>
@@ -2719,9 +2739,8 @@ namespace Serein.Workbench
private void UnloadAllButton_Click(object sender, RoutedEventArgs e)
{
EnvDecorator.ClearAll();
}
/// <summary>
/// 卸载DLL文件清空当前项目
/// </summary>
@@ -2732,23 +2751,10 @@ namespace Serein.Workbench
Connections.Clear();
NodeControls.Clear();
//currentLine = null;
startConnectNodeControl = null;
//startConnectNodeControl = null;
MessageBox.Show("所有DLL已卸载。", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
#region UI层面上显示为 线
#region Extension
#endregion
#endregion
}

View File

@@ -1,6 +1,7 @@
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Env;
using System.ComponentModel;
using System.Windows;
namespace Serein.Workbench
@@ -9,9 +10,10 @@ namespace Serein.Workbench
/// 工作台数据视图
/// </summary>
/// <param name="window"></param>
public class MainWindowViewModel
public class MainWindowViewModel: INotifyPropertyChanged
{
private readonly MainWindow window ;
/// <summary>
/// 运行环境
/// </summary>
@@ -46,5 +48,54 @@ namespace Serein.Workbench
}
private bool _isConnectionInvokeNode = false;
/// <summary>
/// 是否正在连接节点的方法调用关系
/// </summary>
public bool IsConnectionInvokeNode { get => _isConnectionInvokeNode; set
{
if (_isConnectionInvokeNode != value)
{
SetProperty<bool>(ref _isConnectionInvokeNode, value);
}
}
}
private bool _isConnectionArgSouceNode = false;
/// <summary>
/// 是否正在连接节点的参数传递关系
/// </summary>
public bool IsConnectionArgSourceNode { get => _isConnectionArgSouceNode; set
{
if (_isConnectionArgSouceNode != value)
{
SetProperty<bool>(ref _isConnectionArgSouceNode, value);
}
}
}
/// <summary>
/// 略
/// <para>此事件为自动生成</para>
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// 通知属性变更
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="storage">绑定的变量</param>
/// <param name="value">新的数据</param>
/// <param name="propertyName"></param>
protected void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -54,26 +54,26 @@ namespace Serein.Workbench.Node.ViewModel
//private bool isInterrupt;
/////// <summary>
/////// 控制中断状态的视觉效果
/////// </summary>
//public bool IsInterrupt
//{
// get => NodeModel.DebugSetting.IsInterrupt;
// set
// {
// NodeModel.DebugSetting.IsInterrupt = value;
// OnPropertyChanged();
// }
//}
private bool isInterrupt;
///// <summary>
///// 控制中断状态的视觉效果
///// </summary>
public bool IsInterrupt
{
get => NodeModel.DebugSetting.IsInterrupt;
set
{
NodeModel.DebugSetting.IsInterrupt = value;
OnPropertyChanged();
}
}
//public event PropertyChangedEventHandler? PropertyChanged;
//protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
//{
// //Console.WriteLine(propertyName);
// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
//}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
//Console.WriteLine(propertyName);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>

View File

@@ -5,7 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.Workbench.Node.View"
xmlns:vm="clr-namespace:Serein.Workbench.Node.ViewModel"
xmlns:Converters="clr-namespace:Serein.Workbench.Tool.Converters"
xmlns:converters="clr-namespace:Serein.Workbench.Tool.Converters"
xmlns:themes="clr-namespace:Serein.Workbench.Themes"
d:DataContext="{d:DesignInstance vm:ActionNodeControlViewModel}"
mc:Ignorable="d"
@@ -13,7 +13,7 @@
<UserControl.Resources>
<!--<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />-->
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
<converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
<!--<ResourceDictionary Source="/Serein.Workbench;Node/View/NodeExecuteJunctionControl.xaml" x:Key="NodeExecuteJunctionControl"/>-->
</UserControl.Resources>
@@ -27,15 +27,17 @@
<!--<TextBlock Text="{Binding NodelModel.DebugSetting.IsInterrupt}}"></TextBlock>-->
<Border x:Name="InterruptBorder">
<Border x:Name="InterruptBorder" DataContext="{Binding}">
<Border.Style>
<Style TargetType="Border">
<!--默认无边框-->
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=NodeModel.DebugSetting.IsInterrupt}" Value="True">
<!--NodeModel.DebugSetting.IsInterrupt-->
<!--<DataTrigger Binding="{Binding DataContext.NodeModel.DebugSetting.IsInterrup, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="True">-->
<DataTrigger Binding="{Binding IsInterrupt,Mode=OneTime}" Value="True">
<!--<DataTrigger Binding="{Binding NodeModel.DebugSetting.IsInterrup}" Value="True">-->
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="Background" Value="#80000000" />
@@ -53,8 +55,8 @@
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--<Grid Grid.Row="0" Background="#8DE9FD" >-->
<Grid Grid.Row="0" >
<Grid Grid.Row="0" Background="#8DE9FD" >
<!--<Grid Grid.Row="0" >-->
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition/>
@@ -64,6 +66,7 @@
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<local:ExecuteJunctionControl Grid.Column="0" MyNode="{Binding NodeModel}" x:Name="ExecuteJunctionControl" HorizontalAlignment="Left" Grid.RowSpan="2"/>
<StackPanel Grid.Column="1" Grid.RowSpan="2" >
<TextBlock Text="{Binding NodeModel.MethodDetails.MethodTips, Mode=TwoWay}" HorizontalAlignment="Center"/>
@@ -105,6 +108,7 @@
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
@@ -114,6 +118,8 @@
<TextBlock Grid.Row="0" Grid.Column="1" Text="是否使能" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="参数保护" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<CheckBox Grid.Row="2" Grid.Column="0" IsChecked="{Binding NodeModel.DebugSetting.IsInterrupt, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="中断节点" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</Grid>

View File

@@ -27,6 +27,7 @@
<Compile Remove="Node\FlipflopRegionControl.xaml.cs" />
<Compile Remove="Node\Junction\NodeJunctionViewBase.cs" />
<Compile Remove="Node\NodeBase.cs" />
<Compile Remove="Node\View\ActionRegionControl.xaml.cs" />
<Compile Remove="Themes\ConditionControl.xaml.cs" />
<Compile Remove="Themes\ConditionControlModel.cs" />
<Compile Remove="Themes\ConnectionControl.xaml.cs" />
@@ -36,6 +37,7 @@
<ItemGroup>
<Page Remove="Node\FlipflopRegionControl.xaml" />
<Page Remove="Node\View\ActionRegionControl.xaml" />
<Page Remove="Themes\ConditionControl.xaml" />
<Page Remove="Themes\ConnectionControl.xaml" />
<Page Remove="Themes\ExplicitDataControl.xaml" />

View File

@@ -16,7 +16,13 @@
<ItemsControl ItemsSource="{Binding MethodDetails.ParameterDetailss, RelativeSource={RelativeSource TemplatedParent}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<Grid Background="#E3FDFD" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
<ContentControl Content="{Binding}" Grid.Column="1">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
@@ -27,10 +33,8 @@
</MultiDataTrigger.Conditions>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
<Grid Background="#E3FDFD">
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="30"/>
@@ -42,7 +46,7 @@
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Grid.Column="3" MinWidth="50" Text="无须指定参数"/>
</Grid>
</StackPanel>
</DataTemplate>
</Setter.Value>
@@ -59,8 +63,8 @@
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
<Grid Background="#E3FDFD">
<!--<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="30"/>
@@ -90,8 +94,8 @@
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
<Grid Background="#E3FDFD">
<!--<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="30"/>
@@ -114,6 +118,7 @@
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

View File

@@ -0,0 +1,53 @@
using Serein.Library;
using Serein.Workbench.Node.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace Serein.Workbench.Extension
{
/// <summary>
/// 线条颜色
/// </summary>
public static class LineExtension
{
/// <summary>
/// 根据连接类型指定颜色
/// </summary>
/// <param name="currentConnectionType"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static SolidColorBrush ToLineColor(this ConnectionInvokeType currentConnectionType)
{
return currentConnectionType switch
{
ConnectionInvokeType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")), // 04FC10 & 027E08
ConnectionInvokeType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
ConnectionInvokeType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
ConnectionInvokeType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
ConnectionInvokeType.None => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")),
_ => throw new Exception(),
};
}
/// <summary>
/// 根据连接类型指定颜色
/// </summary>
/// <param name="connection"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static SolidColorBrush ToLineColor(this ConnectionArgSourceType connection)
{
return connection switch
{
ConnectionArgSourceType.GetPreviousNodeData => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")), // 04FC10 & 027E08
ConnectionArgSourceType.GetOtherNodeData => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")),
ConnectionArgSourceType.GetOtherNodeDataOfInvoke => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#B06BBB")),
_ => throw new Exception(),
};
}
}
}

View File

@@ -28,10 +28,12 @@ namespace Serein.Workbench.Node.View
Semicircle,
}
/// <summary>
/// 贝塞尔曲线
/// </summary>
public class BezierLine : Shape
public class ConnectionLineShape : Shape
{
private readonly double strokeThickness;
@@ -40,28 +42,44 @@ namespace Serein.Workbench.Node.View
/// <summary>
/// 确定起始坐标和目标坐标、外光样式的曲线
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <param name="brush"></param>
/// <param name="strokeThickness"></param>
public BezierLine(LineType lineType, Point start, Point end, Brush brush, double strokeThickness = 4)
/// <param name="lineType">线条类型</param>
/// <param name="start">起始坐标</param>
/// <param name="end">结束坐标</param>
/// <param name="brush">颜色</param>
/// <param name="isDotted">是否为虚线</param>
public ConnectionLineShape(LineType lineType,
Point start,
Point end,
Brush brush,
bool isDotted = false,
bool isTop = false)
{
this.lineType = lineType;
this.brush = brush;
startPoint = start;
endPoint = end;
this.strokeThickness = strokeThickness;
InitElementPoint();
this.strokeThickness = 4;
InitElementPoint(isDotted, isTop);
InvalidateVisual(); // 触发重绘
}
public void InitElementPoint()
public void InitElementPoint(bool isDotted , bool isTop = false)
{
hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
hitVisiblePen.Freeze(); // Freeze以提高性能
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
if (isDotted)
{
visualPen.DashStyle = DashStyles.Dash; // 选择虚线样式
}
visualPen.Freeze(); // Freeze以提高性能
linkSize = 4; // 整线条粗细
Panel.SetZIndex(this, -9999999); // 置底
int zIndex = -999999;
if (isTop)
{
zIndex *= -1;
}
Panel.SetZIndex(this, zIndex); // 置底
}
/// <summary>
@@ -104,16 +122,17 @@ namespace Serein.Workbench.Node.View
switch (this.lineType)
{
case LineType.Bezier:
DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize);
DrawBezierCurve(drawingContext, startPoint, endPoint);
break;
case LineType.Semicircle:
DrawBezierCurve(drawingContext, startPoint, endPoint);
DrawSemicircleCurve(drawingContext, startPoint, endPoint);
break;
default:
break;
}
}
#region
private readonly StreamGeometry streamGeometry = new StreamGeometry();
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
@@ -125,8 +144,12 @@ namespace Serein.Workbench.Node.View
private Brush brush; // 线条颜色
double linkSize; // 根据缩放比例调整线条粗细
protected override Geometry DefiningGeometry => streamGeometry;
#region
public void UpdateLineColor(Brush brush)
{
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
InvalidateVisual(); // 触发重绘
}
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
@@ -134,15 +157,10 @@ namespace Serein.Workbench.Node.View
private Vector startToEnd;
private void DrawBezierCurve(DrawingContext drawingContext,
Point start,
Point end,
double linkSize,
bool isHitTestVisible = false,
double strokeThickness = 1.0,
bool isMouseOver = false,
double dashOffset = 0.0)
Point end)
{
// 控制点的计算逻辑
double power = 100; // 控制贝塞尔曲线的“拉伸”强度
double power = 140; // 控制贝塞尔曲线的“拉伸”强度
// 计算轴向向量与起点到终点的向量
//var axis = new Vector(1, 0);
@@ -165,31 +183,16 @@ namespace Serein.Workbench.Node.View
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
private void DrawBezierCurve(DrawingContext drawingContext, Point start, Point end)
private void DrawSemicircleCurve(DrawingContext drawingContext, Point start, Point end)
{
// 计算中心点和半径
// 计算圆心和半径
double x = 35 ;
double x = 35;
// 创建一个弧线路径
streamGeometry.Clear();
using (var context = streamGeometry.Open())
@@ -216,6 +219,7 @@ namespace Serein.Workbench.Node.View
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
}
#endregion
}

View File

@@ -4,6 +4,7 @@ using System;
using System.Net;
using System.Reflection;
using System.Windows;
using Serein.Workbench.Extension;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
@@ -16,18 +17,20 @@ namespace Serein.Workbench.Node.View
public abstract class JunctionControlBase : Shape
{
protected JunctionControlBase()
{
this.Width = 25;
this.Height = 20;
this.MouseDown += ControlPointBase_MouseDown;
this.MouseMove += ControlPointBase_MouseMove;
this.MouseDown += JunctionControlBase_MouseDown;
this.MouseMove += JunctionControlBase_MouseMove;
this.MouseLeave += JunctionControlBase_MouseLeave; ;
}
#region
public static readonly DependencyProperty NodeProperty =
DependencyProperty.Register(nameof(MyNode), typeof(NodeModelBase), typeof(JunctionControlBase), new PropertyMetadata(default(NodeModelBase)));
DependencyProperty.Register(nameof(MyNode), typeof(NodeModelBase), typeof(JunctionControlBase), new PropertyMetadata(default(NodeModelBase)));
//public NodeModelBase NodeModel;
/// <summary>
/// 所在的节点
@@ -52,6 +55,7 @@ namespace Serein.Workbench.Node.View
set { SetValue(JunctionTypeProperty, value.ToString()); }
}
#endregion
protected readonly StreamGeometry StreamGeometry = new StreamGeometry();
protected override Geometry DefiningGeometry => StreamGeometry;
@@ -65,15 +69,29 @@ namespace Serein.Workbench.Node.View
/// </summary>
public abstract Point MyCenterPoint { get; }
// 处理鼠标悬停状态
/// <summary>
/// 禁止连接
/// </summary>
private bool IsConnectionDisable;
/// <summary>
/// 处理鼠标悬停状态
/// </summary>
private bool _isMouseOver;
public bool IsMouseOver
{
get => _isMouseOver;
set
{
_isMouseOver = value;
InvalidateVisual();
if(_isMouseOver != value)
{
GlobalJunctionData.MyGlobalConnectingData.CurrentJunction = this;
_isMouseOver = value;
InvalidateVisual();
}
}
}
@@ -86,13 +104,63 @@ namespace Serein.Workbench.Node.View
Render(drawingContext);
}
protected void ControlPointBase_MouseMove(object sender, MouseEventArgs e)
/// <summary>
/// 获取背景颜色
/// </summary>
/// <returns></returns>
protected Brush GetBackgrounp()
{
if (GlobalJunctionData.MyGlobalConnectingData is null) return;
GlobalJunctionData.MyGlobalConnectingData.CurrentJunction = this;
var myData = GlobalJunctionData.MyGlobalConnectingData;
if(!myData.IsCreateing)
{
return Brushes.Transparent;
}
if (IsMouseOver)
{
if (myData.IsCanConnected)
{
if (myData.Type == JunctionOfConnectionType.Invoke)
{
return myData.ConnectionInvokeType.ToLineColor();
}
else
{
return myData.ConnectionArgSourceType.ToLineColor();
}
}
else
{
return Brushes.Red;
}
}
else
{
return Brushes.Transparent;
}
}
private object lockObj = new object();
// 控件获得鼠标焦点事件
private void JunctionControlBase_MouseMove(object sender, MouseEventArgs e)
{
//if (!GlobalJunctionData.MyGlobalConnectingData.IsCreateing) return;
//if (IsMouseOver) return;
IsMouseOver = true;
//this.InvalidateVisual();
}
// 控件失去鼠标焦点事件
private void JunctionControlBase_MouseLeave(object sender, MouseEventArgs e)
{
IsMouseOver = false;
e.Handled = true;
//Console.WriteLine("控件失去鼠标焦点");
}
/// <summary>
@@ -100,20 +168,43 @@ namespace Serein.Workbench.Node.View
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ControlPointBase_MouseDown(object sender, MouseButtonEventArgs e)
protected void JunctionControlBase_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
GlobalJunctionData.MyGlobalConnectingData = new ConnectingData();
var myDataType = GlobalJunctionData.MyGlobalConnectingData;
myDataType.StartJunction = this;
var canvas = MainWindow.GetParentOfType<Canvas>(this);
if (canvas != null)
{
//myDataType.StartPoint = this.MyCenterPoint;
myDataType.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas);
var bezierLine = new BezierLine(LineType.Bezier, myDataType.StartPoint, myDataType.StartPoint, Brushes.Green);
myDataType.VirtualLine = new MyLine(canvas, bezierLine);
var myData = GlobalJunctionData.MyGlobalConnectingData;
myData.Reset();
myData.IsCreateing = true; // 表示开始连接
myData.StartJunction = this;
myData.CurrentJunction = this;
myData.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas);
var junctionOfConnectionType = this.JunctionType.ToConnectyionType();
ConnectionLineShape bezierLine; // 类别
Brush brushColor; // 临时线的颜色
if (junctionOfConnectionType == JunctionOfConnectionType.Invoke)
{
brushColor = ConnectionInvokeType.IsSucceed.ToLineColor();
}
else if(junctionOfConnectionType == JunctionOfConnectionType.Arg)
{
brushColor = ConnectionArgSourceType.GetOtherNodeData.ToLineColor();
}
else
{
return;
}
bezierLine = new ConnectionLineShape(LineType.Bezier,
myData.StartPoint,
myData.StartPoint,
brushColor,
isTop: true); // 绘制临时的线
Mouse.OverrideCursor = Cursors.Cross; // 设置鼠标为正在创建连线
myData.MyLine = new MyLine(canvas, bezierLine);
}
}
e.Handled = true;
@@ -123,6 +214,11 @@ namespace Serein.Workbench.Node.View
{
return new Point(this.ActualWidth / 2, this.ActualHeight / 2); // 起始节点选择右侧边缘中心
}
}

View File

@@ -1,7 +1,10 @@
using System;
using Serein.Library;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@@ -13,28 +16,60 @@ namespace Serein.Workbench.Node.View
#region Model
public class MyLine
{
public MyLine(Canvas canvas, BezierLine line)
public MyLine(Canvas canvas, ConnectionLineShape line)
{
Canvas = canvas;
VirtualLine = line;
Line = line;
canvas?.Children.Add(line);
}
public Canvas Canvas { get; set; }
public BezierLine VirtualLine { get; set; }
public ConnectionLineShape Line { get; set; }
public void Remove()
{
Canvas?.Children.Remove(VirtualLine);
Canvas?.Children.Remove(Line);
}
}
public class ConnectingData
{
/// <summary>
/// 是否正在创建连线
/// </summary>
public bool IsCreateing { get; set; }
/// <summary>
/// 起始控制点
/// </summary>
public JunctionControlBase StartJunction { get; set; }
/// <summary>
/// 当前的控制点
/// </summary>
public JunctionControlBase CurrentJunction { get; set; }
/// <summary>
/// 开始坐标
/// </summary>
public Point StartPoint { get; set; }
public MyLine VirtualLine { get; set; }
/// <summary>
/// 线条样式
/// </summary>
public MyLine MyLine { get; set; }
/// <summary>
/// 线条类别(方法调用)
/// </summary>
public ConnectionInvokeType ConnectionInvokeType { get; set; } = ConnectionInvokeType.IsSucceed;
/// <summary>
/// 线条类别(参数传递)
/// </summary>
public ConnectionArgSourceType ConnectionArgSourceType { get; set; } = ConnectionArgSourceType.GetOtherNodeData;
/// <summary>
/// 判断当前连接类型
/// </summary>
public JunctionOfConnectionType Type => StartJunction.JunctionType.ToConnectyionType();
/// <summary>
/// 是否允许连接
@@ -42,32 +77,22 @@ namespace Serein.Workbench.Node.View
public bool IsCanConnected { get
{
if(StartJunction is null
|| CurrentJunction is null
)
{
return false;
}
if (!StartPoint.Equals(CurrentJunction))
if (!StartJunction.MyNode.Equals(CurrentJunction.MyNode)
&& StartJunction.JunctionType.IsCanConnection(CurrentJunction.JunctionType))
{
return true;
}
else
{
// 自己连接自己的情况下只能是从arg控制点连接到execute控制点。
if (CurrentJunction.JunctionType == Library.JunctionType.Execute
&& StartJunction.JunctionType == Library.JunctionType.ArgData)
{
return true;
}
if (CurrentJunction.JunctionType == Library.JunctionType.ArgData
&& StartJunction.JunctionType == Library.JunctionType.Execute)
{
// 需要是自己连接自己且只能是从arg控制点连接到execute控制点。
return true;
}
return false;
}
}
@@ -88,46 +113,48 @@ namespace Serein.Workbench.Node.View
if (StartJunction.JunctionType == Library.JunctionType.Execute
|| StartJunction.JunctionType == Library.JunctionType.ArgData)
{
VirtualLine.VirtualLine.UpdateStartPoints(point);
MyLine.Line.UpdateStartPoints(point);
}
else
{
VirtualLine.VirtualLine.UpdateEndPoints(point);
MyLine.Line.UpdateEndPoints(point);
}
}
/// <summary>
/// 重置
/// </summary>
public void Reset()
{
IsCreateing = false;
StartJunction = null;
CurrentJunction = null;
MyLine?.Remove();
ConnectionInvokeType = ConnectionInvokeType.IsSucceed;
ConnectionArgSourceType = ConnectionArgSourceType.GetOtherNodeData;
}
}
public static class GlobalJunctionData
{
private static ConnectingData? myGlobalData;
private static object _lockObj = new object();
//private static ConnectingData? myGlobalData;
//private static object _lockObj = new object();
/// <summary>
/// 创建节点之间控制点的连接行为
/// </summary>
public static ConnectingData? MyGlobalConnectingData
{
get => myGlobalData;
set
{
lock (_lockObj)
{
myGlobalData ??= value;
}
}
}
public static ConnectingData MyGlobalConnectingData { get; } = new ConnectingData();
/// <summary>
/// 删除连接视觉效果
/// </summary>
public static void OK()
{
myGlobalData?.VirtualLine.Remove();
myGlobalData = null;
MyGlobalConnectingData.Reset();
}
}
#endregion

View File

@@ -30,29 +30,27 @@ namespace Serein.Workbench.Node.View
#endregion
private Point _myCenterPoint;
public override Point MyCenterPoint { get => _myCenterPoint; }
public override void Render(DrawingContext drawingContext)
{
double width = ActualWidth;
double height = ActualHeight;
var background = GetBackgrounp();
// 输入连接器的背景
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
var connectorRect = new Rect(4, 4, width - 8, height - 8);
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
var connectorRect = new Rect(0, 0, width, height);
drawingContext.DrawRectangle(Brushes.Transparent, null, connectorRect);
// 定义圆形的大小和位置
double connectorSize = 10; // 连接器的大小
double circleCenterX = 8; // 圆心 X 坐标
double circleCenterY = height / 2; // 圆心 Y 坐标
var circlePoint = new Point(circleCenterX, circleCenterY);
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY); // 中心坐标
// 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY);
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse);
// 定义三角形的间距
double triangleOffsetX = 4; // 三角形与圆形的间距
@@ -68,7 +66,7 @@ namespace Serein.Workbench.Node.View
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
}
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry);
}
}

View File

@@ -23,7 +23,7 @@ namespace Serein.Workbench.Node.View
{
double width = ActualWidth;
double height = ActualHeight;
var background = GetBackgrounp();
// 绘制边框
//var borderBrush = new SolidColorBrush(Colors.Black);
//var borderThickness = 1.0;
@@ -31,22 +31,22 @@ namespace Serein.Workbench.Node.View
//drawingContext.DrawRectangle(null, new Pen(borderBrush, borderThickness), borderRect);
// 输入连接器的背景
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
var connectorRect = new Rect(4, 4, width - 8, height - 8);
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
var connectorRect = new Rect(0, 0, width, height);
drawingContext.DrawRectangle(Brushes.Transparent,null, connectorRect);
//drawingContext.DrawRectangle(Brushes.Transparent, new Pen(background,2), connectorRect);
// 定义圆形的大小和位置
double connectorSize = 10; // 连接器的大小
double circleCenterX = 8; // 圆心 X 坐标
double circleCenterY = height / 2; // 圆心 Y 坐标
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY); // 中心坐标
var circlePoint = new Point(circleCenterX, circleCenterY);
// 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY);
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse);
@@ -65,7 +65,7 @@ namespace Serein.Workbench.Node.View
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
}
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry);
// 绘制标签
//var formattedText = new FormattedText(

View File

@@ -20,26 +20,25 @@ namespace Serein.Workbench.Node.View
{
double width = ActualWidth;
double height = ActualHeight;
var background = GetBackgrounp();
// 输入连接器的背景
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
var connectorRect = new Rect(4, 4, width - 8, height - 8);
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
var connectorRect = new Rect(0, 0, width, height);
drawingContext.DrawRectangle(Brushes.Transparent, null, connectorRect);
// 定义圆形的大小和位置
double connectorSize = 10; // 连接器的大小
double circleCenterX = 8; // 圆心 X 坐标
double circleCenterY = height / 2; // 圆心 Y 坐标
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY); // 中心坐标
var circlePoint = new Point(circleCenterX, circleCenterY);
// 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
_myCenterPoint = new Point(circleCenterX + connectorSize / 2, circleCenterY);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse);
// 绘制连接器的圆形部分
//var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
// 定义三角形的间距
double triangleOffsetX = 4; // 三角形与圆形的间距
@@ -55,7 +54,7 @@ namespace Serein.Workbench.Node.View
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
}
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry);
}
}
}

View File

@@ -24,9 +24,10 @@ namespace Serein.Workbench.Node.View
double height = ActualHeight;
// 输入连接器的背景
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
var connectorRect = new Rect(4, 4, width - 8, height - 8);
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
var connectorRect = new Rect(0, 0, width, height);
drawingContext.DrawRectangle(Brushes.Transparent, null, connectorRect);
var background = GetBackgrounp();
// 定义圆形的大小和位置
double connectorSize = 10; // 连接器的大小
@@ -34,10 +35,11 @@ namespace Serein.Workbench.Node.View
double circleCenterY = height / 2; // 圆心 Y 坐标
var circlePoint = new Point(circleCenterX, circleCenterY);
_myCenterPoint = new Point(circleCenterX - connectorSize / 2 , circleCenterY); // 中心坐标
// 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY);
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse);
// 定义三角形的间距
double triangleOffsetX = 4; // 三角形与圆形的间距
@@ -53,7 +55,7 @@ namespace Serein.Workbench.Node.View
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
}
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry);
}
}
}

View File

@@ -112,52 +112,6 @@ namespace Serein.Workbench.Node.View
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.LineType = LineType.Bezier;
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(LineType LineType,
Canvas Canvas,
int argIndex,
ConnectionArgSourceType connectionArgSourceType,
JunctionControlBase Start,
JunctionControlBase End,
Action remoteCallback)
{
this.LineType = LineType;
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>
/// 所在的画布
@@ -167,7 +121,7 @@ namespace Serein.Workbench.Node.View
/// <summary>
/// 调用方法类型,连接类型
/// </summary>
public ConnectionInvokeType Type { get; }
public ConnectionInvokeType InvokeType { get; }
/// <summary>
/// 获取参数类型,第几个参数
@@ -177,7 +131,7 @@ namespace Serein.Workbench.Node.View
/// <summary>
/// 参数来源(决定了连接线的样式)
/// </summary>
public ConnectionArgSourceType ConnectionArgSourceType { get; set; }
public ConnectionArgSourceType ArgSourceType { get; set; }
/// <summary>
/// 起始控制点
@@ -192,14 +146,86 @@ namespace Serein.Workbench.Node.View
/// <summary>
/// 连接线
/// </summary>
private BezierLine BezierLine;
private ConnectionLineShape BezierLine;
private LineType LineType;
/// <summary>
/// 关于调用
/// </summary>
/// <param name="Canvas"></param>
/// <param name="invokeType"></param>
public ConnectionControl(Canvas Canvas,
ConnectionInvokeType invokeType,
JunctionControlBase Start,
JunctionControlBase End,
Action remoteCallback)
{
this.LineType = LineType.Bezier;
this.RemoteCallback = remoteCallback;
this.Canvas = Canvas;
this.InvokeType = invokeType;
this.Start = Start;
this.End = End;
InitElementPoint();
}
/// <summary>
/// 关于入参
/// </summary>
/// <param name="Canvas"></param>
/// <param name="Type"></param>
public ConnectionControl(LineType LineType,
Canvas Canvas,
int argIndex,
ConnectionArgSourceType argSourceType,
JunctionControlBase Start,
JunctionControlBase End,
Action remoteCallback)
{
this.LineType = LineType;
this.RemoteCallback = remoteCallback;
this.Canvas = Canvas;
this.ArgIndex = ArgIndex;
this.ArgSourceType = argSourceType;
this.Start = Start;
this.End = End;
InitElementPoint();
}
/// <summary>
/// 绘制
/// </summary>
public void InitElementPoint()
{
leftCenterOfEndLocation = Start.MyCenterPoint;
rightCenterOfStartLocation = End.MyCenterPoint;
(Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End);
var connectionType = Start.JunctionType.ToConnectyionType();
bool isDotted;
Brush brush;
if(connectionType == JunctionOfConnectionType.Invoke)
{
brush = InvokeType.ToLineColor();
isDotted = false;
}
else
{
brush = ArgSourceType.ToLineColor();
isDotted = true; // 如果为参数,则绘制虚线
}
BezierLine = new ConnectionLineShape(LineType, startPoint, endPoint, brush, isDotted);
Grid.SetZIndex(BezierLine, -9999999); // 置底
Canvas.Children.Add(BezierLine);
ConfigureLineContextMenu(); //配置右键菜单
}
/// <summary>
/// 配置连接曲线的右键菜单
/// </summary>
/// <param name="line"></param>
private void ConfigureLineContextMenu()
{
var contextMenu = new ContextMenu();
@@ -213,17 +239,15 @@ namespace Serein.Workbench.Node.View
/// <param name="line"></param>
public void DeleteConnection()
{
if(this.Start is JunctionControlBase startJunctionControlBase)
{
//startJunctionControlBase.Background = Brushes.Transparent;
}
if (this.End is JunctionControlBase endJunctionControlBase)
{
//endJunctionControlBase.Background = Brushes.Transparent;
}
Canvas.Children.Remove(BezierLine); // 移除线
RemoteCallback?.Invoke();
}
/// <summary>
/// 删除该连线
/// </summary>
public void Remote()
{
Canvas.Children.Remove(BezierLine);
}
/// <summary>
/// 重新绘制
@@ -232,38 +256,8 @@ namespace Serein.Workbench.Node.View
{
(Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End);
BezierLine.UpdatePoints(startPoint, endPoint);
//BezierLine.UpdatePoints(startPoint, endPoint);
}
/// <summary>
/// 删除该连线
/// </summary>
public void Remote()
{
Canvas.Children.Remove(BezierLine);
}
/// <summary>
/// 绘制
/// </summary>
public void InitElementPoint()
{
leftCenterOfEndLocation = Start.MyCenterPoint;
rightCenterOfStartLocation = End.MyCenterPoint;
//leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心
//rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心
linkSize = 4; // 整线条粗细
(Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End);
BezierLine = new BezierLine(LineType, startPoint, endPoint, GetLineColor(Type), linkSize);
Grid.SetZIndex(BezierLine, -9999999); // 置底
Canvas.Children.Add(BezierLine);
ConfigureLineContextMenu(); //配置右键菜单
}
double linkSize; // 根据缩放比例调整线条粗细
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
@@ -275,24 +269,8 @@ namespace Serein.Workbench.Node.View
return (startPoint, endPoint);
}
/// <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")),
_ => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")),
//_ => throw new Exception(),
};
}
#endregion
}