实现了远程属性更改、数据交互。

This commit is contained in:
fengjiayi
2024-10-20 21:59:42 +08:00
parent e38833a58c
commit 838158f446
28 changed files with 961 additions and 637 deletions

View File

@@ -15,6 +15,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Sockets;
using System.Numerics;
using System.Reflection;
using System.Security.Cryptography;
using System.Threading;
@@ -29,10 +30,6 @@ namespace Serein.NodeFlow.Env
// ******************************************************88
/// <summary>
/// 运行环境
/// </summary>
@@ -54,8 +51,15 @@ namespace Serein.NodeFlow.Env
this.ChannelFlowInterrupt = new ChannelFlowInterrupt();
this.IsGlobalInterrupt = false;
this.flowStarter = null;
this.sereinIOC.OnIOCMembersChanged += e => this?.OnIOCMembersChanged?.Invoke(e); // 监听IOC容器的注册
this.uiContextOperation = uiContextOperation; // 本地环境需要存放视图管理
this.sereinIOC.OnIOCMembersChanged += e =>
{
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => this?.OnIOCMembersChanged?.Invoke(e)); // 监听IOC容器的注册
}
};
this.UIContextOperation = uiContextOperation; // 本地环境需要存放视图管理
}
#region
@@ -139,7 +143,7 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 移除节点事件
/// </summary>
public event NodeRemoteHandler? OnNodeRemote;
public event NodeRemoveHandler? OnNodeRemove;
/// <summary>
/// 起始节点变化事件
@@ -190,10 +194,15 @@ namespace Serein.NodeFlow.Env
#region
/// <summary>
/// 当前环境
/// </summary>
public IFlowEnvironment CurrentEnv { get => this; }
/// <summary>
/// UI线程操作类
/// </summary>
public UIContextOperation UIContextOperation { get; set; }
/// <summary>
@@ -245,11 +254,7 @@ namespace Serein.NodeFlow.Env
/// </summary>
public ConcurrentDictionary<string, MethodDetails> MethodDetailss { get; } = [];
/// <summary>
/// UI线程操作类
/// </summary>
private readonly UIContextOperation uiContextOperation;
/// <summary>
/// 容器管理
/// </summary>
@@ -335,7 +340,11 @@ namespace Serein.NodeFlow.Env
public void WriteLineObjToJson(object obj)
{
var msg = JsonConvert.SerializeObject(obj);
OnEnvOut?.Invoke(msg + Environment.NewLine);
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg + Environment.NewLine));
}
}
@@ -370,8 +379,9 @@ namespace Serein.NodeFlow.Env
IOC.Reset(); // 开始运行时清空ioc中注册的实例
IOC.CustomRegisterInstance(typeof(UIContextOperation).FullName, this.uiContextOperation, false);
IOC.CustomRegisterInstance(typeof(IFlowEnvironment).FullName, this);
if (this.UIContextOperation is not null)
IOC.CustomRegisterInstance(typeof(UIContextOperation).FullName, this.UIContextOperation, false);
await flowStarter.RunAsync(this, nodes, AutoRegisterTypes, initMethods, loadMethods, exitMethods);
@@ -401,6 +411,12 @@ namespace Serein.NodeFlow.Env
{
return;
}
//var getExp = "@get .DebugSetting.IsEnable";
//var getExpResult1 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel,out _);
//var setExp = "@set .DebugSetting.IsEnable = false";
//SerinExpressionEvaluator.Evaluate(setExp, nodeModel,out _);
//var getExpResult2 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _);
await flowStarter.StartFlowInSelectNodeAsync(this, nodeModel);
}
else
@@ -424,9 +440,9 @@ namespace Serein.NodeFlow.Env
node.ReleaseFlowData(); // 退出时释放对象计数
}
}
UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs()));
OnFlowRunComplete?.Invoke(new FlowEventArgs());
GC.Collect();
}
@@ -435,7 +451,7 @@ namespace Serein.NodeFlow.Env
/// 激活全局触发器
/// </summary>
/// <param name="nodeGuid"></param>
[AutoSocketHandle]
// [AutoSocketHandle]
public void ActivateFlipflopNode(string nodeGuid)
{
var nodeModel = GuidToModel(nodeGuid);
@@ -455,7 +471,7 @@ namespace Serein.NodeFlow.Env
/// 关闭全局触发器
/// </summary>
/// <param name="nodeGuid"></param>
[AutoSocketHandle]
// [AutoSocketHandle]
public void TerminateFlipflopNode(string nodeGuid)
{
var nodeModel = GuidToModel(nodeGuid);
@@ -470,7 +486,7 @@ namespace Serein.NodeFlow.Env
/// 获取当前环境信息(远程连接)
/// </summary>
/// <returns></returns>
[AutoSocketHandle]
// [AutoSocketHandle]
public async Task<FlowEnvInfo> GetEnvInfoAsync()
{
Dictionary<NodeLibrary, List<MethodDetailsInfo>> LibraryMds = [];
@@ -535,7 +551,6 @@ namespace Serein.NodeFlow.Env
foreach (var dllPath in dllPaths)
{
var dllFilePath = Path.GetFullPath(Path.Combine(filePath, dllPath));
//var dllFilePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(filePath)!, dllPath));
LoadDllNodeInfo(dllFilePath);
}
@@ -564,7 +579,8 @@ namespace Serein.NodeFlow.Env
if (nodeInfo.ChildNodeGuids?.Length > 0)
{
regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids));
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position));
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)));
}
else
{
@@ -583,8 +599,9 @@ namespace Serein.NodeFlow.Env
// 节点尚未加载
continue;
}
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)));
// 存在节点
OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid));
}
}
// 加载节点
@@ -602,7 +619,8 @@ namespace Serein.NodeFlow.Env
}
}
if (IsContinue) continue;
OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position));
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)));
}
@@ -633,13 +651,14 @@ namespace Serein.NodeFlow.Env
// 遍历当前类型分支的节点(确认连接关系)
foreach (var toNode in item.toNodes)
{
ConnectNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
ConnectNodeAsync(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
}
}
}
SetStartNode(projectData.StartNode);
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs());
UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()));
}
@@ -684,7 +703,7 @@ namespace Serein.NodeFlow.Env
/// 序列化当前项目的依赖信息、节点信息
/// </summary>
/// <returns></returns>
public async Task<SereinProjectData> GetProjectInfoAsync()
public Task<SereinProjectData> GetProjectInfoAsync()
{
var projectData = new SereinProjectData()
{
@@ -692,7 +711,7 @@ namespace Serein.NodeFlow.Env
Nodes = Nodes.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
StartNode = Nodes.Values.FirstOrDefault(it => it.IsStart)?.Guid,
};
return projectData;
return Task.FromResult(projectData);
}
@@ -701,7 +720,7 @@ namespace Serein.NodeFlow.Env
/// </summary>
/// <param name="dllPath"></param>
/// <returns></returns>
[AutoSocketHandle]
// [AutoSocketHandle]
public void LoadDll(string dllPath)
{
LoadDllNodeInfo(dllPath);
@@ -766,11 +785,10 @@ namespace Serein.NodeFlow.Env
/// <param name="nodeControlType"></param>
/// <param name="position"></param>
/// <param name="methodDetailsInfo">如果是表达式节点条件节点该项为null</param>
public async Task<NodeInfo?> CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo? methodDetailsInfo = null)
public Task<NodeInfo> CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo? methodDetailsInfo = null)
{
NodeModelBase? nodeModel = null;
NodeModelBase? nodeModel;
if (methodDetailsInfo is null)
{
nodeModel = FlowFunc.CreateNode(this, nodeControlType); // 加载基础节点
@@ -783,7 +801,7 @@ namespace Serein.NodeFlow.Env
}
else
{
return null;
return Task.FromResult<NodeInfo>(null);
}
}
@@ -792,15 +810,17 @@ namespace Serein.NodeFlow.Env
nodeModel.Position = position;
// 通知UI更改
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position));
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position)));
// 因为需要UI先布置了元素才能通知UI变更特效
// 如果不存在流程起始控件,默认设置为流程起始控件
if (StartNode is null)
{
SetStartNode(nodeModel);
}
return nodeModel.ToInfo();
var nodeInfo = nodeModel.ToInfo();
return Task.FromResult(nodeInfo);
;
}
@@ -809,10 +829,12 @@ namespace Serein.NodeFlow.Env
/// </summary>
/// <param name="nodeGuid"></param>
/// <exception cref="NotImplementedException"></exception>
public void RemoveNode(string nodeGuid)
public async Task<bool> RemoveNodeAsync(string nodeGuid)
{
var remoteNode = GuidToModel(nodeGuid);
if (remoteNode is null) return;
if (remoteNode is null)
return false;
//if (remoteNode.IsStart)
//{
// return;
@@ -832,10 +854,11 @@ namespace Serein.NodeFlow.Env
NodeModelBase? pNode = pnc.Value[i];
pNode.SuccessorNodes[pCType].Remove(remoteNode);
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(pNode.Guid,
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(pNode.Guid,
remoteNode.Guid,
pCType,
NodeConnectChangeEventArgs.ConnectChangeType.Remote)); // 通知UI
NodeConnectChangeEventArgs.ConnectChangeType.Remote))); // 通知UI
}
}
@@ -847,14 +870,15 @@ namespace Serein.NodeFlow.Env
{
NodeModelBase? toNode = snc.Value[i];
RemoteConnect(remoteNode, toNode, connectionType);
await RemoteConnectAsync(remoteNode, toNode, connectionType);
}
}
// 从集合中移除节点
Nodes.Remove(nodeGuid);
OnNodeRemote?.Invoke(new NodeRemoteEventArgs(nodeGuid));
UIContextOperation?.Invoke(() => OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid)));
return true;
}
/// <summary>
@@ -868,10 +892,10 @@ namespace Serein.NodeFlow.Env
// 获取起始节点与目标节点
var fromNode = GuidToModel(fromNodeGuid);
var toNode = GuidToModel(toNodeGuid);
if (fromNode is null) return false;
if (toNode is null) return false;
if (fromNode is null || toNode is null) return false;
// 开始连接
return await ConnectNode(fromNode, toNode, connectionType); // 外部调用连接方法
return await ConnectNodeAsync(fromNode, toNode, connectionType); // 外部调用连接方法
}
@@ -882,15 +906,14 @@ namespace Serein.NodeFlow.Env
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="connectionType">连接关系</param>
/// <exception cref="NotImplementedException"></exception>
public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
{
// 获取起始节点与目标节点
var fromNode = GuidToModel(fromNodeGuid);
var toNode = GuidToModel(toNodeGuid);
if (fromNode is null) return;
if (toNode is null) return;
RemoteConnect(fromNode, toNode, connectionType);
if (fromNode is null || toNode is null) return false;
var result = await RemoteConnectAsync(fromNode, toNode, connectionType);
return result;
}
@@ -958,7 +981,8 @@ namespace Serein.NodeFlow.Env
if (nodeModel is null) return;
nodeModel.Position.X = x;
nodeModel.Position.Y = y;
OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y));
UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y)));
}
/// <summary>
@@ -978,12 +1002,13 @@ namespace Serein.NodeFlow.Env
/// <param name="nodeGuid">被中断的目标节点Guid</param>
/// <param name="interruptClass">中断级别</param>
/// <returns>操作是否成功</returns>
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass)
public Task<bool> SetNodeInterruptAsync(string nodeGuid, InterruptClass interruptClass)
{
var nodeModel = GuidToModel(nodeGuid);
if (nodeModel is null) return false;
if (nodeModel is null)
return Task.FromResult(false);
if (interruptClass == InterruptClass.None)
{
nodeModel.CancelInterrupt();
@@ -991,10 +1016,11 @@ namespace Serein.NodeFlow.Env
else if (interruptClass == InterruptClass.Branch)
{
nodeModel.DebugSetting.CancelInterruptCallback?.Invoke();
nodeModel.DebugSetting.GetInterruptTask = () =>
nodeModel.DebugSetting.GetInterruptTask = async () =>
{
TriggerInterrupt(nodeGuid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor);
return ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid);
var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid);
return result;
};
nodeModel.DebugSetting.CancelInterruptCallback = () =>
{
@@ -1004,11 +1030,16 @@ namespace Serein.NodeFlow.Env
}
else if (interruptClass == InterruptClass.Global) // 全局……做不了omg
{
return false;
return Task.FromResult(false);
}
nodeModel.DebugSetting.InterruptClass = interruptClass;
OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass));
return true;
nodeModel.DebugSetting.InterruptClass = interruptClass;
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass)));
}
return Task.FromResult(true);
}
@@ -1018,22 +1049,21 @@ namespace Serein.NodeFlow.Env
/// <param name="key">如果是节点传入Guid如果是对象传入类型FullName</param>
/// <param name="expression">合法的条件表达式</param>
/// <returns></returns>
public async Task<bool> AddInterruptExpressionAsync(string key, string expression)
public Task<bool> AddInterruptExpressionAsync(string key, string expression)
{
if (string.IsNullOrEmpty(expression)) return false;
if (string.IsNullOrEmpty(expression)) return Task.FromResult(false);
if (dictMonitorObjExpInterrupt.TryGetValue(key, out var condition))
{
condition.Clear(); // 暂时
condition.Add(expression);// 暂时
return true;
}
else
{
var exps = new List<string>();
exps.Add(expression);
dictMonitorObjExpInterrupt.TryAdd(key, exps);
return true;
}
return Task.FromResult(true);
}
/// <summary>
@@ -1073,19 +1103,41 @@ namespace Serein.NodeFlow.Env
/// <param name="key"></param>
/// <param name="exps"></param>
/// <returns></returns>
public async Task<(bool, string[])> CheckObjMonitorStateAsync(string key)
public Task<(bool, string[])> CheckObjMonitorStateAsync(string key)
{
if (string.IsNullOrEmpty(key))
return (false, Array.Empty<string>());
var isMonitor = dictMonitorObjExpInterrupt.TryGetValue(key, out var exps);
if (exps is null)
{
return (isMonitor, Array.Empty<string>());
var data = (false, Array.Empty<string>());
return Task.FromResult(data);
}
else
{
return (isMonitor, exps.ToArray());
var isMonitor = dictMonitorObjExpInterrupt.TryGetValue(key, out var exps);
if (exps is null)
{
var data = (isMonitor, Array.Empty<string>());
return Task.FromResult(data);
}
else
{
var data = (isMonitor, exps.ToArray());
return Task.FromResult(data);
}
}
//if (exps is null)
//{
// var data = (isMonitor, Array.Empty<string>());
// return Task.FromResult(data);
//}
//else
//{
// var data = (isMonitor, exps.ToArray());
// return Task.FromResult(data);
//}
}
@@ -1140,33 +1192,38 @@ namespace Serein.NodeFlow.Env
{
var nodeModel = GuidToModel(nodeGuid);
if (nodeModel is null) return;
if(NodeValueChangeLogger.Remove((nodeGuid, path, value)))
if (NodeValueChangeLogger.Remove((nodeGuid, path, value)))
{
// 说明存在过重复的修改
return;
}
Console.WriteLine($"本地环境收到数据更改通知:{value}");
var getExp = $"@Get .{path}";
//Console.WriteLine($"取值表达式:{getExp}");
var getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _);
Console.WriteLine($"原数据 :{getResult}");
if (getResult.Equals(value))
{
Console.WriteLine("无须修改" );
return;
}
NodeValueChangeLogger.Add((nodeGuid, path, value));
var setExp = $"@Set .{path} = {value}"; // 生成 set 表达式
SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); // 更改对应的数据
//Console.WriteLine($"本地环境收到数据更改通知:{value}");
//var getExp = $"@Get .{path}";
////Console.WriteLine($"取值表达式:{getExp}");
//var getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _);
////Console.WriteLine($"原数据 :{getResult}");
//if (getResult.Equals(value))
//{
// Console.WriteLine("无须修改");
// return;
//}
//NodeValueChangeLogger.Add((nodeGuid, path, value));
//var setExp = $"@Set .{path} = {value}";
////Console.WriteLine($"设值表达式:{setExp}");
//SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _);
//getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _);
//Console.WriteLine($"新数据 :{getResult}");
var setExp = $"@Set .{path} = {value}";
//Console.WriteLine($"设值表达式:{setExp}");
SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _);
getResult = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _);
Console.WriteLine($"新数据 :{getResult}");
}
@@ -1226,8 +1283,13 @@ namespace Serein.NodeFlow.Env
}
types.AddRange(kv.Value);
}
var mdInfo = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息
OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfo)); // 通知UI创建dll面板显示
var mdInfos = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos))); // 通知UI创建dll面板显示
}
}
}
@@ -1239,16 +1301,25 @@ namespace Serein.NodeFlow.Env
/// <param name="toNodeGuid">目标节点Model</param>
/// <param name="connectionType">连接关系</param>
/// <exception cref="NotImplementedException"></exception>
private void RemoteConnect(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
private async Task<bool> RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
{
fromNode.SuccessorNodes[connectionType].Remove(toNode);
toNode.PreviousNodes[connectionType].Remove(fromNode);
// 通知UI
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
if (OperatingSystem.IsWindows())
{
await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
toNode.Guid,
connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Remote));
NodeConnectChangeEventArgs.ConnectChangeType.Remote)));
}
//else if (OperatingSystem.IsLinux())
//{
//}
return true;
}
private (NodeLibrary?, Dictionary<RegisterSequence, List<Type>>, List<MethodDetails>) LoadAssembly(string dllPath)
@@ -1377,11 +1448,11 @@ namespace Serein.NodeFlow.Env
/// <param name="fromNode">起始节点</param>
/// <param name="toNode">目标节点</param>
/// <param name="connectionType">连接关系</param>
private Task<bool> ConnectNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
private async Task<bool> ConnectNodeAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
{
if (fromNode is null || toNode is null || fromNode == toNode)
{
return Task.FromResult(false);
return false;
}
var ToExistOnFrom = true;
@@ -1428,23 +1499,26 @@ namespace Serein.NodeFlow.Env
}
}
}
if (!isPass)
{
return Task.FromResult(false);
}
else
if (isPass)
{
fromNode.SuccessorNodes[connectionType].Add(toNode); // 添加到起始节点的子分支
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
toNode.Guid,
connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
toNode.Guid,
connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI
}
return Task.FromResult(true);
return true;
}
else
{
return false;
}
}
/// <summary>
@@ -1456,7 +1530,11 @@ namespace Serein.NodeFlow.Env
{
var oldNodeGuid = StartNode?.Guid;
StartNode = newStartNode;
OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid));
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid)));
}
}
/// <summary>
@@ -1465,7 +1543,11 @@ namespace Serein.NodeFlow.Env
/// <param name="msg"></param>
private void Output(string msg)
{
OnEnvOut?.Invoke(msg);
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg));
}
}
#endregion
@@ -1478,7 +1560,11 @@ namespace Serein.NodeFlow.Env
/// <param name="nodeGuid"></param>
public void NodeLocated(string nodeGuid)
{
OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid));
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)));
}
}
#endregion

View File

@@ -67,6 +67,7 @@ namespace Serein.NodeFlow.Env
/// </summary>
public IFlowEnvironment CurrentEnv { get => currentFlowEnvironment; }
public UIContextOperation UIContextOperation => currentFlowEnvironment.UIContextOperation;
public ISereinIOC IOC => (ISereinIOC)currentFlowEnvironment;
@@ -103,10 +104,10 @@ namespace Serein.NodeFlow.Env
remove { currentFlowEnvironment.OnNodeCreate -= value; }
}
public event NodeRemoteHandler OnNodeRemote
public event NodeRemoveHandler OnNodeRemove
{
add { currentFlowEnvironment.OnNodeRemote += value; }
remove { currentFlowEnvironment.OnNodeRemote -= value; }
add { currentFlowEnvironment.OnNodeRemove += value; }
remove { currentFlowEnvironment.OnNodeRemove -= value; }
}
public event StartNodeChangeHandler OnStartNodeChange
@@ -198,7 +199,8 @@ namespace Serein.NodeFlow.Env
(var isConnect, var remoteEnvControl) = await currentFlowEnvironment.ConnectRemoteEnv(addres, port, token);
if (isConnect)
{
remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteEnvControl);
remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteEnvControl, this.UIContextOperation);
currentFlowEnvironment = remoteFlowEnvironment;
}
return (isConnect, remoteEnvControl);
@@ -277,14 +279,14 @@ namespace Serein.NodeFlow.Env
return currentFlowEnvironment.RemoteDll(assemblyFullName);
}
public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
{
currentFlowEnvironment.RemoveConnect(fromNodeGuid, toNodeGuid, connectionType);
return await currentFlowEnvironment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connectionType);
}
public void RemoveNode(string nodeGuid)
public async Task<bool> RemoveNodeAsync(string nodeGuid)
{
currentFlowEnvironment.RemoveNode(nodeGuid);
return await currentFlowEnvironment.RemoveNodeAsync(nodeGuid);
}

View File

@@ -1,4 +1,5 @@
using Serein.Library;
using Newtonsoft.Json;
using Serein.Library;
using Serein.Library.Network.WebSocketCommunication;
using Serein.Library.Utils;
using System;
@@ -35,13 +36,14 @@ namespace Serein.NodeFlow.Env
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException">超时触发</exception>
public async Task SendAsync(string signal, object? senddata = null, int debounceTimeInMs = 100)
public async Task SendAsync(string signal, object? sendData = null, int overtimeInMs = 100)
{
if (!DebounceHelper.CanExecute(signal, debounceTimeInMs))
//Console.WriteLine($"指令[{signal}]value{JsonConvert.SerializeObject(sendData)}");
if (!DebounceHelper.CanExecute(signal, overtimeInMs))
{
return;
}
await SendCommandAsync.Invoke(signal, senddata);
await SendCommandAsync.Invoke(signal, sendData);
}
/// <summary>
@@ -49,32 +51,32 @@ namespace Serein.NodeFlow.Env
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException">超时触发</exception>
public async Task<TResult> SendAndWaitDataAsync<TResult>(string signal, object? senddata = null, int debounceTimeInMs = 50)
public async Task<TResult> SendAndWaitDataAsync<TResult>(string signal, object? sendData = null, int overtimeInMs = 50)
{
_ = SendCommandAsync.Invoke(signal, senddata);
//Console.WriteLine($"指令[{signal}]value{JsonConvert.SerializeObject(sendData)}");
_ = SendCommandAsync.Invoke(signal, sendData);
return await remoteFlowEnvironment.WaitData<TResult>(signal);
#if DEBUG
if (DebounceHelper.CanExecute(signal, debounceTimeInMs))
{
_ = SendCommandAsync.Invoke(signal, senddata);
return await remoteFlowEnvironment.WaitData<TResult>(signal);
//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;
//}
//(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;
}
#endif
}
@@ -92,11 +94,6 @@ namespace Serein.NodeFlow.Env
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode)]
public void AddInterruptExpression([UseMsgData] NodeInfo nodeInfo)
{
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.CreateNode, nodeInfo);
}
/// <summary>
@@ -122,6 +119,35 @@ namespace Serein.NodeFlow.Env
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode)]
public void CreateNode([UseMsgData] NodeInfo nodeInfo)
{
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.CreateNode, nodeInfo);
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode)]
public void RemoveNode(bool state)
{
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.RemoveNode, state);
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)]
public void ConnectNode(bool state)
{
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.ConnectNode, state);
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)]
public void RemoveConnect(bool state)
{
remoteFlowEnvironment.TriggerSignal(EnvMsgTheme.RemoveConnect, state);
}
#endregion
}

View File

@@ -184,13 +184,14 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 从远程环境运行选定的节点
/// </summary>
/// <param name="startNodeGuid"></param>
/// <param name="nodeGuid"></param>
/// <returns></returns>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlowInSelectNode)]
private async Task StartAsyncInSelectNode(string startNodeGuid)
private async Task StartAsyncInSelectNode(string nodeGuid)
{
await environment.StartAsyncInSelectNode(startNodeGuid);
await environment.StartAsyncInSelectNode(nodeGuid);
}
/// <summary>
/// 结束流程
/// </summary>
@@ -321,9 +322,15 @@ namespace Serein.NodeFlow.Env
/// <param name="nodeGuid"></param>
/// <exception cref="NotImplementedException"></exception>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode)]
public void RemoveNode(string nodeGuid)
public async Task<object> RemoveNode(string nodeGuid)
{
environment.RemoveNode(nodeGuid);
//var result = environment.RemoveNodeAsync(nodeGuid).GetAwaiter().GetResult();
var result = await environment.RemoveNodeAsync(nodeGuid);
//return result;
return new
{
state = result
};
}
@@ -334,9 +341,21 @@ namespace Serein.NodeFlow.Env
/// <param name="toNodeGuid">目标节点</param>
/// <param name="connectionType">连接关系</param>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)]
public void ConnectNode(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
public async Task<object> ConnectNode(string fromNodeGuid, string toNodeGuid, string connectionType)
{
environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, connectionType);
if (!EnumHelper.TryConvertEnum<ConnectionType>(connectionType, out var tmpConnectionType))
{
return new
{
state = false
};
}
//environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType);
var result = await environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType);
return new
{
state = result
};
}
/// <summary>
@@ -347,9 +366,21 @@ namespace Serein.NodeFlow.Env
/// <param name="connectionType">连接关系</param>
/// <exception cref="NotImplementedException"></exception>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)]
public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
public async Task<object> RemoveConnect(string fromNodeGuid, string toNodeGuid, string connectionType)
{
environment.RemoveConnect(fromNodeGuid, toNodeGuid, connectionType);
if (!EnumHelper.TryConvertEnum<ConnectionType>(connectionType, out var tmpConnectionType))
{
return new
{
state = false
};
}
var result = await environment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, tmpConnectionType);
return new
{
state = result
};
}
/// <summary>

View File

@@ -18,9 +18,11 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 连接到远程环境后切换到的环境接口实现
/// </summary>
/// <param name="webSocketClient">连接到远程环境的客户端</param>
public RemoteFlowEnvironment(RemoteEnvControl RemoteEnvControl)
/// <param name="RemoteEnvControl">连接到远程环境后,本地环境自动切换到对应的环境实体</param>
/// <param name="uIContextOperation">远程环境下需要操作UI线程时所提供的线程上下文封装工具</param>
public RemoteFlowEnvironment(RemoteEnvControl RemoteEnvControl, UIContextOperation uIContextOperation)
{
this.UIContextOperation = uIContextOperation;
remoteEnvControl = RemoteEnvControl;
msgClient = new MsgControllerOfClient(this, RemoteEnvControl.SendAsync);
RemoteEnvControl.EnvClient.MsgHandleHelper.AddModule(msgClient, (ex, send) =>
@@ -34,9 +36,6 @@ namespace Serein.NodeFlow.Env
private readonly MsgControllerOfClient msgClient;
private readonly ConcurrentDictionary<string, MethodDetails> MethodDetailss = [];
/// <summary>
/// 环境加载的节点集合
/// Node Guid - Node Model
@@ -47,7 +46,7 @@ namespace Serein.NodeFlow.Env
public event ProjectLoadedHandler OnProjectLoaded;
public event NodeConnectChangeHandler OnNodeConnectChange;
public event NodeCreateHandler OnNodeCreate;
public event NodeRemoteHandler OnNodeRemote;
public event NodeRemoveHandler OnNodeRemove;
public event StartNodeChangeHandler OnStartNodeChange;
public event FlowRunCompleteHandler OnFlowRunComplete;
public event MonitorObjectChangeHandler OnMonitorObjectChange;
@@ -72,7 +71,7 @@ namespace Serein.NodeFlow.Env
public RunState FlipFlopState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public IFlowEnvironment CurrentEnv => this;
public UIContextOperation UIContextOperation { get; }
public void SetConsoleOut()
{
var logTextWriter = new LogTextWriter(msg =>
@@ -118,8 +117,8 @@ namespace Serein.NodeFlow.Env
FilePath = "Remote",
};
var mdInfos = lib.Mds.ToList();
OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos)); // 通知UI创建dll面板显示
//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读取时生成元数据
@@ -160,7 +159,8 @@ namespace Serein.NodeFlow.Env
if (nodeInfo.ChildNodeGuids?.Length > 0)
{
regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids));
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position));
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)));
//OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position));
}
else
{
@@ -181,7 +181,8 @@ namespace Serein.NodeFlow.Env
continue;
}
// 存在节点
OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid));
//OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid));
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)));
}
}
@@ -200,7 +201,8 @@ namespace Serein.NodeFlow.Env
}
}
if (IsContinue) continue;
OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position));
//OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position));
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)));
}
@@ -208,7 +210,7 @@ namespace Serein.NodeFlow.Env
// 确定节点之间的连接关系
_ = Task.Run(async () =>
{
await Task.Delay(100);
await Task.Delay(250);
foreach (var nodeInfo in projectData.Nodes)
{
if (!Nodes.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
@@ -234,12 +236,16 @@ namespace Serein.NodeFlow.Env
// 遍历当前类型分支的节点(确认连接关系)
foreach (var toNode in item.toNodes)
{
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
toNode.Guid,
item.connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI创建节点
//ConnectNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
toNode.Guid,
item.connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点
//OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
// toNode.Guid,
// item.connectionType,
// NodeConnectChangeEventArgs.ConnectChangeType.Create)); //
}
}
}
@@ -251,7 +257,7 @@ namespace Serein.NodeFlow.Env
}
private bool TryAddNode(NodeModelBase nodeModel)
{
nodeModel.Guid ??= Guid.NewGuid().ToString();
//nodeModel.Guid ??= Guid.NewGuid().ToString();
Nodes[nodeModel.Guid] = nodeModel;
// 如果是触发器,则需要添加到专属集合中
@@ -375,7 +381,7 @@ namespace Serein.NodeFlow.Env
public async Task StartAsyncInSelectNode(string startNodeGuid)
{
await msgClient.SendAsync(EnvMsgTheme.StartFlowInSelectNode, new
_ = msgClient.SendAsync(EnvMsgTheme.StartFlowInSelectNode, new
{
nodeGuid = startNodeGuid
});
@@ -404,22 +410,24 @@ namespace Serein.NodeFlow.Env
{
nodeGuid
});
// UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid)));
}
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
{
//_ = RemoteEnv.SendAsync(EnvMsgTheme.ConnectNode, new
//{
// fromNodeGuid,
// toNodeGuid,
// connectionType = connectionType.ToString(),
//});
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ConnectNode, new
{
fromNodeGuid,
toNodeGuid,
connectionType = connectionType.ToString(),
});
if (result)
{
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
toNodeGuid,
connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
}
return result;
}
@@ -434,30 +442,47 @@ namespace Serein.NodeFlow.Env
MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息
var nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 远程环境下加载节点
TryAddNode(nodeModel);
nodeModel.LoadInfo(nodeInfo);
TryAddNode(nodeModel);
// 通知UI更改
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position));
return nodeInfo;
}
public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
{
_ = msgClient.SendAsync(EnvMsgTheme.RemoveConnect, new
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveConnect, new
{
fromNodeGuid,
toNodeGuid,
connectionType = connectionType.ToString(),
});
if (result)
{
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
toNodeGuid,
connectionType,
NodeConnectChangeEventArgs.ConnectChangeType.Remote));
}
return result;
}
public void RemoveNode(string nodeGuid)
public async Task<bool> RemoveNodeAsync(string nodeGuid)
{
_ = msgClient.SendAsync(EnvMsgTheme.RemoveNode, new
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveNode, new
{
nodeGuid
});
if (result)
{
OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid));
}
else
{
Console.WriteLine("删除失败");
}
return result;
}
public void ActivateFlipflopNode(string nodeGuid)
@@ -559,14 +584,14 @@ namespace Serein.NodeFlow.Env
public void NodeLocated(string nodeGuid)
{
Console.WriteLine("远程环境尚未实现的接口NodeLocated");
//Console.WriteLine("远程环境尚未实现的接口NodeLocated");
UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)));
}
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
{
//Console.WriteLine($"通知远程环境修改节点数据:{nodeGuid},name:{path},value:{value}");
_ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new
_ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new
{
nodeGuid = nodeGuid,
path = path,

View File

@@ -3,7 +3,6 @@ using Serein.Library.Api;
using Serein.Library.Core.NodeFlow;
using Serein.Library.Network.WebSocketCommunication;
using Serein.Library.Web;
using Serein.Library;
using Serein.NodeFlow.Env;
using Serein.NodeFlow.Model;
using System.Collections.Concurrent;
@@ -284,6 +283,7 @@ namespace Serein.NodeFlow
finally
{
env.FlowState = RunState.Completion;
Console.WriteLine($"流程运行完毕{Environment.NewLine}");;
}
#endregion
}

View File

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