mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
重写了节点的view、viewmodel关系,实现了对画布元素的选取功能,重构了底层依赖,添加了对net .Framework4.6.1以上的Framework类库支持
This commit is contained in:
35
Library.Core/NodeFlow/DynamicContext.cs
Normal file
35
Library.Core/NodeFlow/DynamicContext.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
|
||||
namespace Serein.Library.Core.NodeFlow
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 动态流程上下文
|
||||
/// </summary>
|
||||
public class DynamicContext: IDynamicContext
|
||||
{
|
||||
public DynamicContext(ISereinIoc sereinIoc)
|
||||
{
|
||||
SereinIoc = sereinIoc;
|
||||
}
|
||||
|
||||
public NodeRunCts NodeRunCts { get; set; }
|
||||
public ISereinIoc SereinIoc { get; }
|
||||
public Task CreateTimingTask(Action action, int time = 100, int count = -1)
|
||||
{
|
||||
NodeRunCts ??= SereinIoc.GetOrInstantiate<NodeRunCts>();
|
||||
return Task.Factory.StartNew(async () =>
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
NodeRunCts.Token.ThrowIfCancellationRequested();
|
||||
await Task.Delay(time);
|
||||
action.Invoke();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
38
Library.Core/NodeFlow/DynamicNodeCoreType.cs
Normal file
38
Library.Core/NodeFlow/DynamicNodeCoreType.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.LibraryCore.NodeFlow
|
||||
{
|
||||
public enum DynamicNodeCoreType
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
Init,
|
||||
/// <summary>
|
||||
/// 开始载入
|
||||
/// </summary>
|
||||
Loading,
|
||||
/// <summary>
|
||||
/// 结束
|
||||
/// </summary>
|
||||
Exit,
|
||||
|
||||
/// <summary>
|
||||
/// 触发器
|
||||
/// </summary>
|
||||
Flipflop,
|
||||
/// <summary>
|
||||
/// 条件节点
|
||||
/// </summary>
|
||||
Condition,
|
||||
/// <summary>
|
||||
/// 动作节点
|
||||
/// </summary>
|
||||
Action,
|
||||
}
|
||||
|
||||
}
|
||||
85
Library.Core/NodeFlow/FlipflopContext.cs
Normal file
85
Library.Core/NodeFlow/FlipflopContext.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Enums;
|
||||
|
||||
namespace Serein.Library.Core.NodeFlow
|
||||
{
|
||||
public static class FlipflopFunc
|
||||
{
|
||||
/// <summary>
|
||||
/// 传入触发器方法的返回类型,尝试获取Task[Flipflop[]] 中的泛型类型
|
||||
/// </summary>
|
||||
//public static Type GetFlipflopInnerType(Type type)
|
||||
//{
|
||||
// // 检查是否为泛型类型且为 Task<>
|
||||
// if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
// {
|
||||
// // 获取 Task<> 的泛型参数类型,即 Flipflop<>
|
||||
// var innerType = type.GetGenericArguments()[0];
|
||||
|
||||
// // 检查泛型参数是否为 Flipflop<>
|
||||
// if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||
// {
|
||||
// // 获取 Flipflop<> 的泛型参数类型,即 T
|
||||
// var flipflopInnerType = innerType.GetGenericArguments()[0];
|
||||
|
||||
// // 返回 Flipflop<> 中的具体类型
|
||||
// return flipflopInnerType;
|
||||
// }
|
||||
// }
|
||||
// // 如果不符合条件,返回 null
|
||||
// return null;
|
||||
//}
|
||||
|
||||
public static bool IsTaskOfFlipflop(Type type)
|
||||
{
|
||||
// 检查是否为泛型类型且为 Task<>
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
{
|
||||
// 获取 Task<> 的泛型参数类型
|
||||
var innerType = type.GetGenericArguments()[0];
|
||||
|
||||
// 判断 innerType 是否继承 IFlipflopContext
|
||||
//if (typeof(IFlipflopContext).IsAssignableFrom(innerType))
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
|
||||
// 检查泛型参数是否为 Flipflop<>
|
||||
if (innerType == typeof(IFlipflopContext))
|
||||
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发器上下文
|
||||
/// </summary>
|
||||
public class FlipflopContext : IFlipflopContext
|
||||
{
|
||||
public FlowStateType State { get; set; }
|
||||
//public TResult? Data { get; set; }
|
||||
public object Data { get; set; }
|
||||
|
||||
public FlipflopContext(FlowStateType ffState)
|
||||
{
|
||||
State = ffState;
|
||||
}
|
||||
public FlipflopContext(FlowStateType ffState, object data)
|
||||
{
|
||||
State = ffState;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
25
Library.Core/NodeFlow/FlowStateType.cs
Normal file
25
Library.Core/NodeFlow/FlowStateType.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Core.NodeFlow
|
||||
{
|
||||
|
||||
//public enum FlowStateType
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// 成功(方法成功执行)
|
||||
// /// </summary>
|
||||
// Succeed,
|
||||
// /// <summary>
|
||||
// /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误)
|
||||
// /// </summary>
|
||||
// Fail,
|
||||
// /// <summary>
|
||||
// /// 异常(节点没有成功执行,执行时发生非预期的错误)
|
||||
// /// </summary>
|
||||
// Error,
|
||||
//}
|
||||
}
|
||||
35
Library.Core/NodeFlow/Tool/Attribute.cs
Normal file
35
Library.Core/NodeFlow/Tool/Attribute.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Serein.Library.Api.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Core.NodeFlow.Tool
|
||||
{
|
||||
/// <summary>
|
||||
/// 用来判断一个类是否需要注册并构建实例(单例模式场景使用)
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class DynamicFlowAttribute(bool scan = true) : Attribute
|
||||
{
|
||||
public bool Scan { get; set; } = scan;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记一个方法是什么类型,加载dll后用来拖拽到画布中
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class MethodDetailAttribute(DynamicNodeType methodDynamicType,
|
||||
string methodTips = "",
|
||||
bool scan = true,
|
||||
string lockName = "") : Attribute
|
||||
{
|
||||
public bool Scan { get; set; } = scan;
|
||||
public string MethodTips { get; } = methodTips;
|
||||
public DynamicNodeType MethodDynamicType { get; } = methodDynamicType;
|
||||
public string LockName { get; } = lockName;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
202
Library.Core/NodeFlow/Tool/DynamicTool.cs
Normal file
202
Library.Core/NodeFlow/Tool/DynamicTool.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Printing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Core.NodeFlow.Tool
|
||||
{
|
||||
|
||||
#region 锁、tsk工具 (已注释)
|
||||
/*public class LockManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, LockQueue> _locks = new ConcurrentDictionary<string, LockQueue>();
|
||||
|
||||
public void CreateLock(string name)
|
||||
{
|
||||
_locks.TryAdd(name, new LockQueue());
|
||||
}
|
||||
|
||||
public async Task AcquireLockAsync(string name, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_locks.ContainsKey(name))
|
||||
{
|
||||
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
||||
}
|
||||
|
||||
var lockQueue = _locks[name];
|
||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
lock (lockQueue.Queue)
|
||||
{
|
||||
lockQueue.Queue.Enqueue(tcs);
|
||||
if (lockQueue.Queue.Count == 1)
|
||||
{
|
||||
tcs.SetResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
|
||||
// 处理取消操作
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationToken.Register(() =>
|
||||
{
|
||||
lock (lockQueue.Queue)
|
||||
{
|
||||
if (lockQueue.Queue.Contains(tcs))
|
||||
{
|
||||
tcs.TrySetCanceled();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseLock(string name)
|
||||
{
|
||||
if (!_locks.ContainsKey(name))
|
||||
{
|
||||
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
||||
}
|
||||
|
||||
var lockQueue = _locks[name];
|
||||
|
||||
lock (lockQueue.Queue)
|
||||
{
|
||||
if (lockQueue.Queue.Count > 0)
|
||||
{
|
||||
lockQueue.Queue.Dequeue();
|
||||
|
||||
if (lockQueue.Queue.Count > 0)
|
||||
{
|
||||
var next = lockQueue.Queue.Peek();
|
||||
next.SetResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LockQueue
|
||||
{
|
||||
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface ITaskResult
|
||||
{
|
||||
object Result { get; }
|
||||
}
|
||||
|
||||
public class TaskResult<T> : ITaskResult
|
||||
{
|
||||
public TaskResult(T result)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
|
||||
public T Result { get; }
|
||||
|
||||
object ITaskResult.Result => Result;
|
||||
}
|
||||
|
||||
public class DynamicTasks
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, Task<ITaskResult>> TaskGuidPairs = new();
|
||||
public static Task<ITaskResult> GetTask(string Guid)
|
||||
{
|
||||
TaskGuidPairs.TryGetValue(Guid, out Task<ITaskResult> task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public static bool AddTask<T>(string Guid, T result)
|
||||
{
|
||||
var task = Task.FromResult<ITaskResult>(new TaskResult<T>(result));
|
||||
|
||||
return TaskGuidPairs.TryAdd(Guid, task);
|
||||
}
|
||||
}
|
||||
public class TaskNodeManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, TaskQueue> _taskQueues = new ConcurrentDictionary<string, TaskQueue>();
|
||||
|
||||
public void CreateTaskNode(string name)
|
||||
{
|
||||
_taskQueues.TryAdd(name, new TaskQueue());
|
||||
}
|
||||
|
||||
public async Task WaitForTaskNodeAsync(string name, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_taskQueues.ContainsKey(name))
|
||||
{
|
||||
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
||||
}
|
||||
|
||||
var taskQueue = _taskQueues[name];
|
||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
lock (taskQueue.Queue)
|
||||
{
|
||||
taskQueue.Queue.Enqueue(tcs);
|
||||
if (taskQueue.Queue.Count == 1)
|
||||
{
|
||||
tcs.SetResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
|
||||
// 处理取消操作
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
cancellationToken.Register(() =>
|
||||
{
|
||||
lock (taskQueue.Queue)
|
||||
{
|
||||
if (taskQueue.Queue.Contains(tcs))
|
||||
{
|
||||
tcs.TrySetCanceled();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void CompleteTaskNode(string name)
|
||||
{
|
||||
if (!_taskQueues.ContainsKey(name))
|
||||
{
|
||||
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
||||
}
|
||||
|
||||
var taskQueue = _taskQueues[name];
|
||||
|
||||
lock (taskQueue.Queue)
|
||||
{
|
||||
if (taskQueue.Queue.Count > 0)
|
||||
{
|
||||
taskQueue.Queue.Dequeue();
|
||||
|
||||
if (taskQueue.Queue.Count > 0)
|
||||
{
|
||||
var next = taskQueue.Queue.Peek();
|
||||
next.SetResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TaskQueue
|
||||
{
|
||||
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
||||
}
|
||||
}*/
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user