mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-02 15:50:47 +08:00
修改了很多
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Serein.FlowStartTool": {
|
||||
"commandName": "Project"
|
||||
},
|
||||
"配置文件 1": {
|
||||
"commandName": "DebugRoslynComponent"
|
||||
},
|
||||
"Serein.FlowStartTool": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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("")]
|
||||
|
||||
@@ -42,7 +42,8 @@ namespace Serein.Library.Api
|
||||
/// <summary>
|
||||
/// 用以提前结束分支运行
|
||||
/// </summary>
|
||||
void EndCurrentBranch();
|
||||
void Exit();
|
||||
|
||||
|
||||
/*/// <summary>
|
||||
/// 定时循环触发
|
||||
|
||||
@@ -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>
|
||||
/// 添加作用于某个对象的中断表达式
|
||||
|
||||
78
Library/Extension/SereinExtension.cs
Normal file
78
Library/Extension/SereinExtension.cs
Normal 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;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
64
Library/Network/Http/SereinExtension.cs
Normal file
64
Library/Network/Http/SereinExtension.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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}");
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
73
Library/Network/WebSocket/TestExtension.cs
Normal file
73
Library/Network/WebSocket/TestExtension.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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); // 处理消息
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -100,4 +100,14 @@ namespace Serein.Library.Utils
|
||||
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<(TriggerType, object)>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
//});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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},开始执行后继分支");
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Serein.NodeFlow.Model
|
||||
{
|
||||
//public class CompositeLoopNode : NodeBase
|
||||
//{
|
||||
//}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 }];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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容器属性-->
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
53
Workbench/Extension/LineExtension.cs
Normal file
53
Workbench/Extension/LineExtension.cs
Normal 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(),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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); // 起始节点选择右侧边缘中心
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user