From f286fc644a298fe98e443db833e78d03600365ea Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Thu, 12 Sep 2024 20:32:54 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99=E4=BA=86=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E7=9A=84view=E3=80=81viewmodel=E5=85=B3=E7=B3=BB=EF=BC=8C?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86=E5=AF=B9=E7=94=BB=E5=B8=83=E5=85=83?= =?UTF-8?q?=E7=B4=A0=E7=9A=84=E9=80=89=E5=8F=96=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BA=86=E5=BA=95=E5=B1=82=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E4=BA=86=E5=AF=B9net=20.Framework4.?= =?UTF-8?q?6.1=E4=BB=A5=E4=B8=8A=E7=9A=84Framework=E7=B1=BB=E5=BA=93?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {Library => Library.Core}/DbSql/DBSync.cs | 0 .../DbSql/FifoManualResetEvent.cs | 0 .../DbSql/IRepositoryBase.cs | 0 .../DbSql/RepositoryBase.cs | 0 {Library => Library.Core}/Flow/Api.cs | 0 {Library => Library.Core}/Flow/Attribute.cs | 0 .../Flow/DynamicContext.cs | 0 .../Flow/MethodDetails.cs | 0 .../Flow/NodeFlowStarter.cs | 0 .../Flow/NodeModel/CompositeActionNode.cs | 0 .../Flow/NodeModel/CompositeConditionNode.cs | 0 .../Flow/NodeModel/CompositeLoopNode.cs | 0 .../Flow/NodeModel/NodeBase.cs | 0 .../Flow/NodeModel/SingleActionNode.cs | 0 .../Flow/NodeModel/SingleConditionNode.cs | 0 .../Flow/NodeModel/SingleExpOpNode.cs | 0 .../Flow/NodeModel/SingleFlipflopNode.cs | 0 .../Flow/SerinExpression/ConditionResolver.cs | 0 .../SerinExpression/SerinConditionParser.cs | 0 .../SerinExpressionEvaluator.cs | 0 .../Flow/Tool/DelegateGenerator.cs | 0 .../Flow/Tool/DynamicTool.cs | 0 .../Flow/Tool/ExpressionHelper.cs | 0 .../Flow/Tool/TcsSignal.cs | 0 {Library => Library.Core}/Http/Attribute.cs | 3 +- .../Http/ControllerBase.cs | 4 +- {Library => Library.Core}/Http/Router.cs | 14 +- .../Http/WebAPIAttribute.cs | 8 +- .../IOC/SereinIoc.cs | 70 +- Library.Core/NodeFlow/DynamicContext.cs | 35 + Library.Core/NodeFlow/DynamicNodeCoreType.cs | 38 + Library.Core/NodeFlow/FlipflopContext.cs | 85 + Library.Core/NodeFlow/FlowStateType.cs | 25 + Library.Core/NodeFlow/Tool/Attribute.cs | 35 + Library.Core/NodeFlow/Tool/DynamicTool.cs | 202 + Library.Core/Serein.Library.Core.csproj | 52 + .../SerinExpression/ConditionResolver.cs | 2 +- .../SerinExpression/SerinConditionParser.cs | 3 +- .../SerinExpressionEvaluator.cs | 15 +- {Library => Library.Core}/ServiceContainer.cs | 0 {Library => Library.Core}/Tool/DataHelper.cs | 0 Library.Framework/Http/Attribute.cs | 167 + Library.Framework/Http/ControllerBase.cs | 19 + Library.Framework/Http/Router.cs | 761 ++ Library.Framework/Http/WebAPIAttribute.cs | 196 + Library.Framework/IOC/SereinIoc.cs | 378 + Library.Framework/NodeFlow/Api.cs | 13 + Library.Framework/NodeFlow/DynamicContext.cs | 40 + .../NodeFlow/DynamicNodeFrameworkType.cs | 38 + Library.Framework/NodeFlow/FlipflopContext.cs | 94 + Library.Framework/NodeFlow/FlowStateType.cs | 25 + Library.Framework/NodeFlow/Tool/Attribute.cs | 8 + .../NodeFlow/Tool/DynamicTool.cs | 194 + Library.Framework/NodeFlow/Tool/TcsSignal.cs | 65 + Library.Framework/Properties/AssemblyInfo.cs | 36 + .../Serein.Library.Framework.csproj | 63 + Library.Framework/Tool/DataHelper.cs | 186 + Library.Framework/packages.config | 4 + Library/Api/IDynamicContext.cs | 13 + Library/Api/IFlipflopContext.cs | 10 + Library/Api/ISereinIoc.cs | 36 + Library/Enums/FlowStateType.cs | 25 + Library/Enums/NodeType.cs | 38 + Library/NodeAttribute.cs | 49 + Library/Serein.Library.csproj | 23 +- Library/Utils/SereinIoc.cs | 378 + Library/Utils/TcsSignal.cs | 64 + Library/Utils/Utils.cs | 9 + MyDll/SampleCondition.cs | 111 +- NodeFlow/DynamicContext.cs | 83 +- NodeFlow/DynamicNodeFrameworkType.cs | 38 + NodeFlow/FlowStateType.cs | 24 + NodeFlow/MethodDetails.cs | 161 +- NodeFlow/Model/CompositeConditionNode.cs | 10 +- NodeFlow/Model/NodeBase.cs | 134 +- NodeFlow/Model/SingleConditionNode.cs | 7 +- NodeFlow/Model/SingleExpOpNode.cs | 13 +- NodeFlow/Model/SingleFlipflopNode.cs | 5 +- NodeFlow/NodeFlowStarter.cs | 46 +- NodeFlow/Serein - Backup.NodeFlow.csproj | 36 + NodeFlow/Serein.NodeFlow.csproj | 13 + NodeFlow/{ => Tool}/Attribute.cs | 2 +- NodeFlow/Tool/DelegateGenerator.cs | 86 +- NodeFlow/Tool/ExpressionHelper.cs | 27 +- .../Tool/SerinExpression/ConditionResolver.cs | 337 + .../SerinExpression/SerinConditionParser.cs | 341 + .../SerinExpressionEvaluator.cs | 216 + NodeFlow/Tool/TcsSignal.cs | 33 +- SereinFlow.sln | 20 +- SereinWAT/SereinWAT.cs | 1 + WorkBench/App.xaml.cs | 2 +- WorkBench/MainWindow.xaml | 12 + WorkBench/MainWindow.xaml.cs | 465 +- WorkBench/Node/View/ActionNodeControl.xaml.cs | 10 +- .../Node/View/ActionRegionControl.xaml.cs | 23 +- .../Node/View/ConditionNodeControl.xaml.cs | 13 +- .../Node/View/ConditionRegionControl.xaml | 5 - .../Node/View/ConditionRegionControl.xaml.cs | 51 +- WorkBench/Node/View/ExpOpNodeControl.xaml.cs | 14 +- .../Node/View/FlipflopNodeControl.xaml.cs | 23 +- WorkBench/Node/View/NodeControlBase.cs | 25 +- .../ViewModel/ActionNodeControlViewModel.cs | 17 +- .../ConditionNodeControlViewModel.cs | 5 +- .../ConditionRegionNodeControlViewModel.cs | 18 + .../Node/ViewModel/ExpOpNodeViewModel.cs | 4 +- .../ViewModel/FlipflopNodeControlViewModel.cs | 5 +- WorkBench/Serein.WorkBench.csproj | 4 +- .../Newtonsoft.Json.13.0.3/.signature.p7s | Bin 0 -> 24497 bytes packages/Newtonsoft.Json.13.0.3/LICENSE.md | 20 + .../Newtonsoft.Json.13.0.3.nupkg | Bin 0 -> 2441966 bytes packages/Newtonsoft.Json.13.0.3/README.md | 71 + .../lib/net20/Newtonsoft.Json.xml | 10393 ++++++++++++++ .../lib/net35/Newtonsoft.Json.xml | 9541 +++++++++++++ .../lib/net40/Newtonsoft.Json.xml | 9741 +++++++++++++ .../lib/net45/Newtonsoft.Json.xml | 11363 ++++++++++++++++ .../lib/net6.0/Newtonsoft.Json.xml | 11325 +++++++++++++++ .../lib/netstandard1.0/Newtonsoft.Json.xml | 11051 +++++++++++++++ .../lib/netstandard1.3/Newtonsoft.Json.xml | 11173 +++++++++++++++ .../lib/netstandard2.0/Newtonsoft.Json.xml | 11338 +++++++++++++++ .../Newtonsoft.Json.13.0.3/packageIcon.png | Bin 0 -> 8956 bytes 120 files changed, 91218 insertions(+), 761 deletions(-) rename {Library => Library.Core}/DbSql/DBSync.cs (100%) rename {Library => Library.Core}/DbSql/FifoManualResetEvent.cs (100%) rename {Library => Library.Core}/DbSql/IRepositoryBase.cs (100%) rename {Library => Library.Core}/DbSql/RepositoryBase.cs (100%) rename {Library => Library.Core}/Flow/Api.cs (100%) rename {Library => Library.Core}/Flow/Attribute.cs (100%) rename {Library => Library.Core}/Flow/DynamicContext.cs (100%) rename {Library => Library.Core}/Flow/MethodDetails.cs (100%) rename {Library => Library.Core}/Flow/NodeFlowStarter.cs (100%) rename {Library => Library.Core}/Flow/NodeModel/CompositeActionNode.cs (100%) rename {Library => Library.Core}/Flow/NodeModel/CompositeConditionNode.cs (100%) rename {Library => Library.Core}/Flow/NodeModel/CompositeLoopNode.cs (100%) rename {Library => Library.Core}/Flow/NodeModel/NodeBase.cs (100%) rename {Library => Library.Core}/Flow/NodeModel/SingleActionNode.cs (100%) rename {Library => Library.Core}/Flow/NodeModel/SingleConditionNode.cs (100%) rename {Library => Library.Core}/Flow/NodeModel/SingleExpOpNode.cs (100%) rename {Library => Library.Core}/Flow/NodeModel/SingleFlipflopNode.cs (100%) rename {Library => Library.Core}/Flow/SerinExpression/ConditionResolver.cs (100%) rename {Library => Library.Core}/Flow/SerinExpression/SerinConditionParser.cs (100%) rename {Library => Library.Core}/Flow/SerinExpression/SerinExpressionEvaluator.cs (100%) rename {Library => Library.Core}/Flow/Tool/DelegateGenerator.cs (100%) rename {Library => Library.Core}/Flow/Tool/DynamicTool.cs (100%) rename {Library => Library.Core}/Flow/Tool/ExpressionHelper.cs (100%) rename {Library => Library.Core}/Flow/Tool/TcsSignal.cs (100%) rename {Library => Library.Core}/Http/Attribute.cs (98%) rename {Library => Library.Core}/Http/ControllerBase.cs (90%) rename {Library => Library.Core}/Http/Router.cs (98%) rename {Library => Library.Core}/Http/WebAPIAttribute.cs (97%) rename Library/IOC/ServiceContainer.cs => Library.Core/IOC/SereinIoc.cs (79%) create mode 100644 Library.Core/NodeFlow/DynamicContext.cs create mode 100644 Library.Core/NodeFlow/DynamicNodeCoreType.cs create mode 100644 Library.Core/NodeFlow/FlipflopContext.cs create mode 100644 Library.Core/NodeFlow/FlowStateType.cs create mode 100644 Library.Core/NodeFlow/Tool/Attribute.cs create mode 100644 Library.Core/NodeFlow/Tool/DynamicTool.cs create mode 100644 Library.Core/Serein.Library.Core.csproj rename {Library => Library.Core}/SerinExpression/ConditionResolver.cs (99%) rename {Library => Library.Core}/SerinExpression/SerinConditionParser.cs (99%) rename {Library => Library.Core}/SerinExpression/SerinExpressionEvaluator.cs (92%) rename {Library => Library.Core}/ServiceContainer.cs (100%) rename {Library => Library.Core}/Tool/DataHelper.cs (100%) create mode 100644 Library.Framework/Http/Attribute.cs create mode 100644 Library.Framework/Http/ControllerBase.cs create mode 100644 Library.Framework/Http/Router.cs create mode 100644 Library.Framework/Http/WebAPIAttribute.cs create mode 100644 Library.Framework/IOC/SereinIoc.cs create mode 100644 Library.Framework/NodeFlow/Api.cs create mode 100644 Library.Framework/NodeFlow/DynamicContext.cs create mode 100644 Library.Framework/NodeFlow/DynamicNodeFrameworkType.cs create mode 100644 Library.Framework/NodeFlow/FlipflopContext.cs create mode 100644 Library.Framework/NodeFlow/FlowStateType.cs create mode 100644 Library.Framework/NodeFlow/Tool/Attribute.cs create mode 100644 Library.Framework/NodeFlow/Tool/DynamicTool.cs create mode 100644 Library.Framework/NodeFlow/Tool/TcsSignal.cs create mode 100644 Library.Framework/Properties/AssemblyInfo.cs create mode 100644 Library.Framework/Serein.Library.Framework.csproj create mode 100644 Library.Framework/Tool/DataHelper.cs create mode 100644 Library.Framework/packages.config create mode 100644 Library/Api/IDynamicContext.cs create mode 100644 Library/Api/IFlipflopContext.cs create mode 100644 Library/Api/ISereinIoc.cs create mode 100644 Library/Enums/FlowStateType.cs create mode 100644 Library/Enums/NodeType.cs create mode 100644 Library/NodeAttribute.cs create mode 100644 Library/Utils/SereinIoc.cs create mode 100644 Library/Utils/TcsSignal.cs create mode 100644 Library/Utils/Utils.cs create mode 100644 NodeFlow/DynamicNodeFrameworkType.cs create mode 100644 NodeFlow/FlowStateType.cs create mode 100644 NodeFlow/Serein - Backup.NodeFlow.csproj rename NodeFlow/{ => Tool}/Attribute.cs (98%) create mode 100644 NodeFlow/Tool/SerinExpression/ConditionResolver.cs create mode 100644 NodeFlow/Tool/SerinExpression/SerinConditionParser.cs create mode 100644 NodeFlow/Tool/SerinExpression/SerinExpressionEvaluator.cs create mode 100644 WorkBench/Node/ViewModel/ConditionRegionNodeControlViewModel.cs create mode 100644 packages/Newtonsoft.Json.13.0.3/.signature.p7s create mode 100644 packages/Newtonsoft.Json.13.0.3/LICENSE.md create mode 100644 packages/Newtonsoft.Json.13.0.3/Newtonsoft.Json.13.0.3.nupkg create mode 100644 packages/Newtonsoft.Json.13.0.3/README.md create mode 100644 packages/Newtonsoft.Json.13.0.3/lib/net20/Newtonsoft.Json.xml create mode 100644 packages/Newtonsoft.Json.13.0.3/lib/net35/Newtonsoft.Json.xml create mode 100644 packages/Newtonsoft.Json.13.0.3/lib/net40/Newtonsoft.Json.xml create mode 100644 packages/Newtonsoft.Json.13.0.3/lib/net45/Newtonsoft.Json.xml create mode 100644 packages/Newtonsoft.Json.13.0.3/lib/net6.0/Newtonsoft.Json.xml create mode 100644 packages/Newtonsoft.Json.13.0.3/lib/netstandard1.0/Newtonsoft.Json.xml create mode 100644 packages/Newtonsoft.Json.13.0.3/lib/netstandard1.3/Newtonsoft.Json.xml create mode 100644 packages/Newtonsoft.Json.13.0.3/lib/netstandard2.0/Newtonsoft.Json.xml create mode 100644 packages/Newtonsoft.Json.13.0.3/packageIcon.png diff --git a/Library/DbSql/DBSync.cs b/Library.Core/DbSql/DBSync.cs similarity index 100% rename from Library/DbSql/DBSync.cs rename to Library.Core/DbSql/DBSync.cs diff --git a/Library/DbSql/FifoManualResetEvent.cs b/Library.Core/DbSql/FifoManualResetEvent.cs similarity index 100% rename from Library/DbSql/FifoManualResetEvent.cs rename to Library.Core/DbSql/FifoManualResetEvent.cs diff --git a/Library/DbSql/IRepositoryBase.cs b/Library.Core/DbSql/IRepositoryBase.cs similarity index 100% rename from Library/DbSql/IRepositoryBase.cs rename to Library.Core/DbSql/IRepositoryBase.cs diff --git a/Library/DbSql/RepositoryBase.cs b/Library.Core/DbSql/RepositoryBase.cs similarity index 100% rename from Library/DbSql/RepositoryBase.cs rename to Library.Core/DbSql/RepositoryBase.cs diff --git a/Library/Flow/Api.cs b/Library.Core/Flow/Api.cs similarity index 100% rename from Library/Flow/Api.cs rename to Library.Core/Flow/Api.cs diff --git a/Library/Flow/Attribute.cs b/Library.Core/Flow/Attribute.cs similarity index 100% rename from Library/Flow/Attribute.cs rename to Library.Core/Flow/Attribute.cs diff --git a/Library/Flow/DynamicContext.cs b/Library.Core/Flow/DynamicContext.cs similarity index 100% rename from Library/Flow/DynamicContext.cs rename to Library.Core/Flow/DynamicContext.cs diff --git a/Library/Flow/MethodDetails.cs b/Library.Core/Flow/MethodDetails.cs similarity index 100% rename from Library/Flow/MethodDetails.cs rename to Library.Core/Flow/MethodDetails.cs diff --git a/Library/Flow/NodeFlowStarter.cs b/Library.Core/Flow/NodeFlowStarter.cs similarity index 100% rename from Library/Flow/NodeFlowStarter.cs rename to Library.Core/Flow/NodeFlowStarter.cs diff --git a/Library/Flow/NodeModel/CompositeActionNode.cs b/Library.Core/Flow/NodeModel/CompositeActionNode.cs similarity index 100% rename from Library/Flow/NodeModel/CompositeActionNode.cs rename to Library.Core/Flow/NodeModel/CompositeActionNode.cs diff --git a/Library/Flow/NodeModel/CompositeConditionNode.cs b/Library.Core/Flow/NodeModel/CompositeConditionNode.cs similarity index 100% rename from Library/Flow/NodeModel/CompositeConditionNode.cs rename to Library.Core/Flow/NodeModel/CompositeConditionNode.cs diff --git a/Library/Flow/NodeModel/CompositeLoopNode.cs b/Library.Core/Flow/NodeModel/CompositeLoopNode.cs similarity index 100% rename from Library/Flow/NodeModel/CompositeLoopNode.cs rename to Library.Core/Flow/NodeModel/CompositeLoopNode.cs diff --git a/Library/Flow/NodeModel/NodeBase.cs b/Library.Core/Flow/NodeModel/NodeBase.cs similarity index 100% rename from Library/Flow/NodeModel/NodeBase.cs rename to Library.Core/Flow/NodeModel/NodeBase.cs diff --git a/Library/Flow/NodeModel/SingleActionNode.cs b/Library.Core/Flow/NodeModel/SingleActionNode.cs similarity index 100% rename from Library/Flow/NodeModel/SingleActionNode.cs rename to Library.Core/Flow/NodeModel/SingleActionNode.cs diff --git a/Library/Flow/NodeModel/SingleConditionNode.cs b/Library.Core/Flow/NodeModel/SingleConditionNode.cs similarity index 100% rename from Library/Flow/NodeModel/SingleConditionNode.cs rename to Library.Core/Flow/NodeModel/SingleConditionNode.cs diff --git a/Library/Flow/NodeModel/SingleExpOpNode.cs b/Library.Core/Flow/NodeModel/SingleExpOpNode.cs similarity index 100% rename from Library/Flow/NodeModel/SingleExpOpNode.cs rename to Library.Core/Flow/NodeModel/SingleExpOpNode.cs diff --git a/Library/Flow/NodeModel/SingleFlipflopNode.cs b/Library.Core/Flow/NodeModel/SingleFlipflopNode.cs similarity index 100% rename from Library/Flow/NodeModel/SingleFlipflopNode.cs rename to Library.Core/Flow/NodeModel/SingleFlipflopNode.cs diff --git a/Library/Flow/SerinExpression/ConditionResolver.cs b/Library.Core/Flow/SerinExpression/ConditionResolver.cs similarity index 100% rename from Library/Flow/SerinExpression/ConditionResolver.cs rename to Library.Core/Flow/SerinExpression/ConditionResolver.cs diff --git a/Library/Flow/SerinExpression/SerinConditionParser.cs b/Library.Core/Flow/SerinExpression/SerinConditionParser.cs similarity index 100% rename from Library/Flow/SerinExpression/SerinConditionParser.cs rename to Library.Core/Flow/SerinExpression/SerinConditionParser.cs diff --git a/Library/Flow/SerinExpression/SerinExpressionEvaluator.cs b/Library.Core/Flow/SerinExpression/SerinExpressionEvaluator.cs similarity index 100% rename from Library/Flow/SerinExpression/SerinExpressionEvaluator.cs rename to Library.Core/Flow/SerinExpression/SerinExpressionEvaluator.cs diff --git a/Library/Flow/Tool/DelegateGenerator.cs b/Library.Core/Flow/Tool/DelegateGenerator.cs similarity index 100% rename from Library/Flow/Tool/DelegateGenerator.cs rename to Library.Core/Flow/Tool/DelegateGenerator.cs diff --git a/Library/Flow/Tool/DynamicTool.cs b/Library.Core/Flow/Tool/DynamicTool.cs similarity index 100% rename from Library/Flow/Tool/DynamicTool.cs rename to Library.Core/Flow/Tool/DynamicTool.cs diff --git a/Library/Flow/Tool/ExpressionHelper.cs b/Library.Core/Flow/Tool/ExpressionHelper.cs similarity index 100% rename from Library/Flow/Tool/ExpressionHelper.cs rename to Library.Core/Flow/Tool/ExpressionHelper.cs diff --git a/Library/Flow/Tool/TcsSignal.cs b/Library.Core/Flow/Tool/TcsSignal.cs similarity index 100% rename from Library/Flow/Tool/TcsSignal.cs rename to Library.Core/Flow/Tool/TcsSignal.cs diff --git a/Library/Http/Attribute.cs b/Library.Core/Http/Attribute.cs similarity index 98% rename from Library/Http/Attribute.cs rename to Library.Core/Http/Attribute.cs index 80c9f20..670a840 100644 --- a/Library/Http/Attribute.cs +++ b/Library.Core/Http/Attribute.cs @@ -1,6 +1,6 @@ using System; -namespace Serein.Library.Http +namespace Serein.Library.Core.Http { /// /// 表示参数为url中的数据(Get请求中不需要显式标注) @@ -96,6 +96,7 @@ namespace Serein.Library.Http /// public bool IsUrl = true; } + /*public sealed class WebApiAttribute(API http, bool isUrl = true, string url = "") : Attribute { public API Http { get; } = http; diff --git a/Library/Http/ControllerBase.cs b/Library.Core/Http/ControllerBase.cs similarity index 90% rename from Library/Http/ControllerBase.cs rename to Library.Core/Http/ControllerBase.cs index 812d684..714c1a0 100644 --- a/Library/Http/ControllerBase.cs +++ b/Library.Core/Http/ControllerBase.cs @@ -1,14 +1,12 @@ -namespace Serein.Library.Http +namespace Serein.Library.Core.Http { public class ControllerBase { public string Url { get; set; } - public string BobyData { get; set; } - public string GetLog(Exception ex) { return "Url : " + Url + Environment.NewLine + diff --git a/Library/Http/Router.cs b/Library.Core/Http/Router.cs similarity index 98% rename from Library/Http/Router.cs rename to Library.Core/Http/Router.cs index 6abb23d..61ad03b 100644 --- a/Library/Http/Router.cs +++ b/Library.Core/Http/Router.cs @@ -1,10 +1,10 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Serein.Library.IOC; +using Serein.Library.Api.Api; +using Serein.Library.Core.IOC; using Serein.Tool; using System.Collections; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Net; using System.Reflection; using System.Text; @@ -12,7 +12,7 @@ using System.Web; using Enum = System.Enum; using Type = System.Type; -namespace Serein.Library.Http +namespace Serein.Library.Core.Http { @@ -28,11 +28,11 @@ namespace Serein.Library.Http private readonly ConcurrentDictionary _controllerInstances; // 存储控制器实例对象 private readonly ConcurrentDictionary> _routes; // 用于存储路由信息 - private readonly IServiceContainer serviceRegistry; // 用于存储路由信息 + private readonly ISereinIoc serviceRegistry; // 用于存储路由信息 //private Type PostRequest; - public Router(IServiceContainer serviceRegistry) // 构造函数,初始化 Router 类的新实例 + public Router(ISereinIoc serviceRegistry) // 构造函数,初始化 Router 类的新实例 { this.serviceRegistry = serviceRegistry; @@ -172,7 +172,7 @@ namespace Serein.Library.Http /// 手动注册 实例持久控制器实例 /// /// - public void RegisterController(T controllerInstance) // 方法声明,用于动态注册路由 + public void RegisterController(TController controllerInstance) where TController : ControllerBase // 方法声明,用于动态注册路由 { if(controllerInstance == null) return; Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型 @@ -286,6 +286,8 @@ namespace Serein.Library.Http } } } + + /// /// 解析路由,调用对应的方法 /// diff --git a/Library/Http/WebAPIAttribute.cs b/Library.Core/Http/WebAPIAttribute.cs similarity index 97% rename from Library/Http/WebAPIAttribute.cs rename to Library.Core/Http/WebAPIAttribute.cs index 2e334e2..7973e4c 100644 --- a/Library/Http/WebAPIAttribute.cs +++ b/Library.Core/Http/WebAPIAttribute.cs @@ -1,9 +1,9 @@ -using Serein.Library.IOC; +using Serein.Library.Api.Api; +using Serein.Library.Core.IOC; using System.Collections.Concurrent; using System.Net; -using System.Security.AccessControl; -namespace Serein.Library.Http +namespace Serein.Library.Core.Http { /// @@ -27,7 +27,7 @@ namespace Serein.Library.Http } // 启动服务器 - public WebServer Start(string prefixe, IServiceContainer serviceContainer) + public WebServer Start(string prefixe, ISereinIoc serviceContainer) { try { diff --git a/Library/IOC/ServiceContainer.cs b/Library.Core/IOC/SereinIoc.cs similarity index 79% rename from Library/IOC/ServiceContainer.cs rename to Library.Core/IOC/SereinIoc.cs index 92a83bc..63eea9a 100644 --- a/Library/IOC/ServiceContainer.cs +++ b/Library.Core/IOC/SereinIoc.cs @@ -1,54 +1,23 @@ -using Serein.Library.Http; -using SqlSugar; +using Serein.Library.Api; +using Serein.Library.Attributes; using System.Collections.Concurrent; using System.Reflection; -namespace Serein.Library.IOC +namespace Serein.Library.Core.IOC { - - - public interface IServiceContainer - { - /// - /// 获取或创建类型的实例(不注入依赖项) - /// - object GetOrCreateServiceInstance(Type serviceType, params object[] parameters); - T CreateServiceInstance(params object[] parameters); - IServiceContainer Reset(); // 清空 - IServiceContainer Register(Type type, params object[] parameters); - IServiceContainer Register(params object[] parameters); - IServiceContainer Register(params object[] parameters) where TImplementation : TService; - T GetOrInstantiate(); - object GetOrInstantiate(Type type); - - /// - /// 创建目标类型的对象, 并注入依赖项 - /// - object? Instantiate(Type type, params object[] parameters); - IServiceContainer Build(); - IServiceContainer Run(Action action); - IServiceContainer Run(Action action); - IServiceContainer Run(Action action); - IServiceContainer Run(Action action); - IServiceContainer Run(Action action); - IServiceContainer Run(Action action); - IServiceContainer Run(Action action); - IServiceContainer Run(Action action); - } - - public class ServiceContainer : IServiceContainer + public class SereinIoc : ISereinIoc { private readonly ConcurrentDictionary _dependencies; private readonly ConcurrentDictionary _typeMappings; private readonly List _waitingForInstantiation; - public ServiceContainer() + public SereinIoc() { _dependencies = new ConcurrentDictionary { - [typeof(IServiceContainer).FullName] = this + [typeof(ISereinIoc).FullName] = this }; _typeMappings = new ConcurrentDictionary(); @@ -75,14 +44,13 @@ namespace Serein.Library.IOC return instance; - } public T CreateServiceInstance(params object[] parameters) { return (T)GetOrCreateServiceInstance(typeof(T), parameters); } - public IServiceContainer Reset() + public ISereinIoc Reset() { foreach(var instancei in _dependencies.Values) { @@ -97,7 +65,7 @@ namespace Serein.Library.IOC return this; } - public IServiceContainer Register(Type type, params object[] parameters) + public ISereinIoc Register(Type type, params object[] parameters) { if (!_typeMappings.ContainsKey(type.FullName)) @@ -107,13 +75,13 @@ namespace Serein.Library.IOC return this; } - public IServiceContainer Register(params object[] parameters) + public ISereinIoc Register(params object[] parameters) { Register(typeof(T), parameters); return this; } - public IServiceContainer Register(params object[] parameters) + public ISereinIoc Register(params object[] parameters) where TImplementation : TService { _typeMappings[typeof(TService).FullName!] = typeof(TImplementation); @@ -152,7 +120,7 @@ namespace Serein.Library.IOC return (T)value; //throw new InvalidOperationException("目标类型未创建实例"); } - public IServiceContainer Build() + public ISereinIoc Build() { foreach (var type in _typeMappings.Values) { @@ -224,7 +192,7 @@ namespace Serein.Library.IOC } #region run() - public IServiceContainer Run(Action action) + public ISereinIoc Run(Action action) { var service = GetOrInstantiate(); if (service != null) @@ -234,7 +202,7 @@ namespace Serein.Library.IOC return this; } - public IServiceContainer Run(Action action) + public ISereinIoc Run(Action action) { var service1 = GetOrInstantiate(); var service2 = GetOrInstantiate(); @@ -243,7 +211,7 @@ namespace Serein.Library.IOC return this; } - public IServiceContainer Run(Action action) + public ISereinIoc Run(Action action) { var service1 = GetOrInstantiate(); var service2 = GetOrInstantiate(); @@ -252,7 +220,7 @@ namespace Serein.Library.IOC return this; } - public IServiceContainer Run(Action action) + public ISereinIoc Run(Action action) { var service1 = GetOrInstantiate(); var service2 = GetOrInstantiate(); @@ -262,7 +230,7 @@ namespace Serein.Library.IOC return this; } - public IServiceContainer Run(Action action) + public ISereinIoc Run(Action action) { var service1 = GetOrInstantiate(); var service2 = GetOrInstantiate(); @@ -273,7 +241,7 @@ namespace Serein.Library.IOC return this; } - public IServiceContainer Run(Action action) + public ISereinIoc Run(Action action) { var service1 = GetOrInstantiate(); var service2 = GetOrInstantiate(); @@ -285,7 +253,7 @@ namespace Serein.Library.IOC return this; } - public IServiceContainer Run(Action action) + public ISereinIoc Run(Action action) { var service1 = GetOrInstantiate(); var service2 = GetOrInstantiate(); @@ -298,7 +266,7 @@ namespace Serein.Library.IOC return this; } - public IServiceContainer Run(Action action) + public ISereinIoc Run(Action action) { var service1 = GetOrInstantiate(); var service2 = GetOrInstantiate(); diff --git a/Library.Core/NodeFlow/DynamicContext.cs b/Library.Core/NodeFlow/DynamicContext.cs new file mode 100644 index 0000000..5e9020d --- /dev/null +++ b/Library.Core/NodeFlow/DynamicContext.cs @@ -0,0 +1,35 @@ +using Serein.Library.Api; +using Serein.Library.Utils; + +namespace Serein.Library.Core.NodeFlow +{ + + /// + /// 动态流程上下文 + /// + 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(); + return Task.Factory.StartNew(async () => + { + for (int i = 0; i < count; i++) + { + NodeRunCts.Token.ThrowIfCancellationRequested(); + await Task.Delay(time); + action.Invoke(); + } + }); + } + } + + +} diff --git a/Library.Core/NodeFlow/DynamicNodeCoreType.cs b/Library.Core/NodeFlow/DynamicNodeCoreType.cs new file mode 100644 index 0000000..e97d99f --- /dev/null +++ b/Library.Core/NodeFlow/DynamicNodeCoreType.cs @@ -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 + { + /// + /// 初始化 + /// + Init, + /// + /// 开始载入 + /// + Loading, + /// + /// 结束 + /// + Exit, + + /// + /// 触发器 + /// + Flipflop, + /// + /// 条件节点 + /// + Condition, + /// + /// 动作节点 + /// + Action, + } + +} diff --git a/Library.Core/NodeFlow/FlipflopContext.cs b/Library.Core/NodeFlow/FlipflopContext.cs new file mode 100644 index 0000000..81c0e1b --- /dev/null +++ b/Library.Core/NodeFlow/FlipflopContext.cs @@ -0,0 +1,85 @@ +using Serein.Library.Api; +using Serein.Library.Enums; + +namespace Serein.Library.Core.NodeFlow +{ + public static class FlipflopFunc + { + /// + /// 传入触发器方法的返回类型,尝试获取Task[Flipflop[]] 中的泛型类型 + /// + //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; + } + } + + /// + /// 触发器上下文 + /// + 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; + } + + + } + +} diff --git a/Library.Core/NodeFlow/FlowStateType.cs b/Library.Core/NodeFlow/FlowStateType.cs new file mode 100644 index 0000000..aae089c --- /dev/null +++ b/Library.Core/NodeFlow/FlowStateType.cs @@ -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 + //{ + // /// + // /// 成功(方法成功执行) + // /// + // Succeed, + // /// + // /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误) + // /// + // Fail, + // /// + // /// 异常(节点没有成功执行,执行时发生非预期的错误) + // /// + // Error, + //} +} diff --git a/Library.Core/NodeFlow/Tool/Attribute.cs b/Library.Core/NodeFlow/Tool/Attribute.cs new file mode 100644 index 0000000..338d11b --- /dev/null +++ b/Library.Core/NodeFlow/Tool/Attribute.cs @@ -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 +{ + /// + /// 用来判断一个类是否需要注册并构建实例(单例模式场景使用) + /// + [AttributeUsage(AttributeTargets.Class)] + public class DynamicFlowAttribute(bool scan = true) : Attribute + { + public bool Scan { get; set; } = scan; + } + + /// + /// 标记一个方法是什么类型,加载dll后用来拖拽到画布中 + /// + [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; + } + + +} diff --git a/Library.Core/NodeFlow/Tool/DynamicTool.cs b/Library.Core/NodeFlow/Tool/DynamicTool.cs new file mode 100644 index 0000000..058eb11 --- /dev/null +++ b/Library.Core/NodeFlow/Tool/DynamicTool.cs @@ -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 _locks = new ConcurrentDictionary(); + + 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(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> Queue { get; } = new Queue>(); + } + } + + + public interface ITaskResult + { + object Result { get; } + } + + public class TaskResult : ITaskResult + { + public TaskResult(T result) + { + Result = result; + } + + public T Result { get; } + + object ITaskResult.Result => Result; + } + + public class DynamicTasks + { + private static readonly ConcurrentDictionary> TaskGuidPairs = new(); + public static Task GetTask(string Guid) + { + TaskGuidPairs.TryGetValue(Guid, out Task task); + return task; + } + + public static bool AddTask(string Guid, T result) + { + var task = Task.FromResult(new TaskResult(result)); + + return TaskGuidPairs.TryAdd(Guid, task); + } + } + public class TaskNodeManager + { + private readonly ConcurrentDictionary _taskQueues = new ConcurrentDictionary(); + + 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(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> Queue { get; } = new Queue>(); + } + }*/ + #endregion + + + +} diff --git a/Library.Core/Serein.Library.Core.csproj b/Library.Core/Serein.Library.Core.csproj new file mode 100644 index 0000000..232da48 --- /dev/null +++ b/Library.Core/Serein.Library.Core.csproj @@ -0,0 +1,52 @@ + + + + net8.0-windows7.0 + enable + enable + D:\Project\C#\DynamicControl\SereinFlow\.Output + Library + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Library/SerinExpression/ConditionResolver.cs b/Library.Core/SerinExpression/ConditionResolver.cs similarity index 99% rename from Library/SerinExpression/ConditionResolver.cs rename to Library.Core/SerinExpression/ConditionResolver.cs index f95b47b..0e13911 100644 --- a/Library/SerinExpression/ConditionResolver.cs +++ b/Library.Core/SerinExpression/ConditionResolver.cs @@ -1,6 +1,6 @@ using System.Reflection; -namespace Serein.Library.SerinExpression +namespace Serein.LibraryCore.SerinExpression { /// /// 条件解析抽象类 diff --git a/Library/SerinExpression/SerinConditionParser.cs b/Library.Core/SerinExpression/SerinConditionParser.cs similarity index 99% rename from Library/SerinExpression/SerinConditionParser.cs rename to Library.Core/SerinExpression/SerinConditionParser.cs index 5b9348e..3a57418 100644 --- a/Library/SerinExpression/SerinConditionParser.cs +++ b/Library.Core/SerinExpression/SerinConditionParser.cs @@ -1,7 +1,7 @@ using System.Globalization; using System.Reflection; -namespace Serein.Library.SerinExpression; +namespace Serein.LibraryCore.SerinExpression; public class SerinConditionParser { @@ -290,6 +290,7 @@ public class SerinConditionParser { ">" => ValueTypeConditionResolver.Operator.GreaterThan, "<" => ValueTypeConditionResolver.Operator.LessThan, + "=" => ValueTypeConditionResolver.Operator.Equal, "==" => ValueTypeConditionResolver.Operator.Equal, ">=" => ValueTypeConditionResolver.Operator.GreaterThanOrEqual, "≥" => ValueTypeConditionResolver.Operator.GreaterThanOrEqual, diff --git a/Library/SerinExpression/SerinExpressionEvaluator.cs b/Library.Core/SerinExpression/SerinExpressionEvaluator.cs similarity index 92% rename from Library/SerinExpression/SerinExpressionEvaluator.cs rename to Library.Core/SerinExpression/SerinExpressionEvaluator.cs index 97602c8..0a12633 100644 --- a/Library/SerinExpression/SerinExpressionEvaluator.cs +++ b/Library.Core/SerinExpression/SerinExpressionEvaluator.cs @@ -1,6 +1,6 @@ using System.Data; -namespace Serein.Library.SerinExpression +namespace Serein.LibraryCore.SerinExpression { public class SerinArithmeticExpressionEvaluator { @@ -25,7 +25,16 @@ namespace Serein.Library.SerinExpression public class SerinExpressionEvaluator { - public static object Evaluate(string expression, object targetObJ, out bool IsChange) + /// + /// + /// + /// 表达式 + /// 操作对象 + /// 是否改变了对象(get语法) + /// + /// + /// + public static object Evaluate(string expression, object targetObJ, out bool isChange) { var parts = expression.Split([' '], 2); if (parts.Length != 2) @@ -45,7 +54,7 @@ namespace Serein.Library.SerinExpression _ => throw new NotSupportedException($"Operation {operation} is not supported.") }; - IsChange = operation switch + isChange = operation switch { "@num" => true, "@call" => true, diff --git a/Library/ServiceContainer.cs b/Library.Core/ServiceContainer.cs similarity index 100% rename from Library/ServiceContainer.cs rename to Library.Core/ServiceContainer.cs diff --git a/Library/Tool/DataHelper.cs b/Library.Core/Tool/DataHelper.cs similarity index 100% rename from Library/Tool/DataHelper.cs rename to Library.Core/Tool/DataHelper.cs diff --git a/Library.Framework/Http/Attribute.cs b/Library.Framework/Http/Attribute.cs new file mode 100644 index 0000000..2971e2e --- /dev/null +++ b/Library.Framework/Http/Attribute.cs @@ -0,0 +1,167 @@ +using Serein.Library.Framework.NodeFlow; +using System; + +namespace Serein.Library.Framework.Http +{ + + ///// + ///// 用来判断一个类是否需要注册并构建实例(单例模式场景使用) + ///// + //[AttributeUsage(AttributeTargets.Class)] + //public class DynamicFlowAttribute : Attribute + //{ + // public DynamicFlowAttribute(bool scan = true) + // { + // Scan = scan; + // } + // public bool Scan { get; set; } + //} + + ///// + ///// 标记一个方法是什么类型,加载dll后用来拖拽到画布中 + ///// + //[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + //public class MethodDetailAttribute : Attribute + //{ + + // public MethodDetailAttribute(DynamicNodeType methodDynamicType, + // string methodTips = "", + // bool scan = true, + // string lockName = "") + // { + // Scan = scan; + // MethodDynamicType = methodDynamicType; + // MethodTips = methodTips; + // LockName = lockName; + // } + // public bool Scan { get; set; } + // public string MethodTips { get; } + // public DynamicNodeType MethodDynamicType { get; } + // public string LockName { get; } + //} + + /// + /// 是否为显式参数 + /// + //[AttributeUsage(AttributeTargets.Parameter)] + //public class ExplicitAttribute : Attribute // where TEnum : Enum + //{ + //} + + + + /// + /// 表示参数为url中的数据(Get请求中不需要显式标注) + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class IsUrlDataAttribute : Attribute + { + + } + + /// + /// 表示入参参数为整个boby的数据 + /// + /// 例如:User类型含有int id、string name字段 + /// + /// ① Add(User user) + /// 请求需要传入的json为 + /// {"user":{ + /// "id":2, + /// "name":"李志忠"}} + /// + /// ② Add([Boby]User user) + /// 请求需要传入的json为 + /// {"id":2,"name":"李志忠"} + /// + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class IsBobyDataAttribute : Attribute + { + + } + + /// + /// 表示该控制器会被自动注册(与程序集同一命名空间,暂时不支持运行时自动加载DLL,需要手动注册) + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class AutoHostingAttribute : Attribute + { + public AutoHostingAttribute(string url = "") + { + Url = url; + } + public string Url { get; } + } + /// + /// 表示该属性为自动注入依赖项 + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class AutoInjectionAttribute : Attribute + { + } + + + /// + /// 方法的接口类型与附加URL + /// + /// + /// 假设UserController.Add()的WebAPI特性中 + /// http是HTTP.POST + /// url被显示标明“temp” + /// 那么请求的接口是POST,URL是 + /// [http://localhost:8080]/user/add/temp + /// + /// + /// + [AttributeUsage(AttributeTargets.Method)] + + public sealed class WebApiAttribute : Attribute + + { + public API Type; + public string Url; + /// + /// 方法名称不作为url的部分 + /// + public bool IsUrl; + } + [AttributeUsage(AttributeTargets.Method)] + + public sealed class ApiPostAttribute : Attribute + + { + public string Url; + /// + /// 方法名称不作为url的部分 + /// + public bool IsUrl = true; + } + [AttributeUsage(AttributeTargets.Method)] + + public sealed class ApiGetAttribute : Attribute + + { + public string Url; + /// + /// 方法名称不作为url的部分 + /// + public bool IsUrl = true; + } + /*public sealed class WebApiAttribute(API http, bool isUrl = true, string url = "") : Attribute + { + public API Http { get; } = http; + public string Url { get; } = url; + /// + /// 方法名称不作为url的部分 + /// + public bool IsUrl { get; } = isUrl; + }*/ + public enum API + { + POST, + GET, + //PUT, + //DELETE + } +} diff --git a/Library.Framework/Http/ControllerBase.cs b/Library.Framework/Http/ControllerBase.cs new file mode 100644 index 0000000..7eba7a1 --- /dev/null +++ b/Library.Framework/Http/ControllerBase.cs @@ -0,0 +1,19 @@ +using System; + +namespace Serein.Library.Framework.Http +{ + public class ControllerBase + { + + public string Url { get; set; } + + public string BobyData { get; set; } + + public string GetLog(Exception ex) + { + return "Url : " + Url + Environment.NewLine + + "Ex : " + ex.Message + Environment.NewLine + + "Data : " + BobyData + Environment.NewLine; + } + } +} diff --git a/Library.Framework/Http/Router.cs b/Library.Framework/Http/Router.cs new file mode 100644 index 0000000..6e6ba99 --- /dev/null +++ b/Library.Framework/Http/Router.cs @@ -0,0 +1,761 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Serein.Library.Api.Api; +using Serein.Library.Framework.IOC; +using Serein.Tool; +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Runtime.Remoting.Messaging; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using Enum = System.Enum; +using Type = System.Type; + +namespace Serein.Library.Framework.Http +{ + + + + /// + /// 路由注册与解析 + /// + public class Router + { + + private readonly ConcurrentDictionary _controllerAutoHosting; // 存储是否实例化 + private readonly ConcurrentDictionary _controllerTypes; // 存储控制器类型 + private readonly ConcurrentDictionary _controllerInstances; // 存储控制器实例对象 + private readonly ConcurrentDictionary> _routes; // 用于存储路由信息 + + private readonly ISereinIoc serviceRegistry; // 用于存储路由信息 + + //private Type PostRequest; + + public Router(ISereinIoc serviceRegistry) // 构造函数,初始化 Router 类的新实例 + { + this.serviceRegistry = serviceRegistry; + + _routes = new ConcurrentDictionary>(); // 初始化路由字典 + + _controllerAutoHosting = new ConcurrentDictionary(); // 初始化控制器实例对象字典 + _controllerTypes = new ConcurrentDictionary(); // 初始化控制器实例对象字典 + _controllerInstances = new ConcurrentDictionary(); // 初始化控制器实例对象字典 + + foreach (API method in Enum.GetValues(typeof(API))) // 遍历 HTTP 枚举类型的所有值 + { + _routes.TryAdd(method.ToString(), new ConcurrentDictionary()); // 初始化每种 HTTP 方法对应的路由字典 + } + + // 获取当前程序集 + Assembly assembly = Assembly.GetExecutingAssembly(); + + // 获取包含“Controller”名称的类型 + var controllerTypes = assembly.GetTypes() + .Where(t => t.Name.Contains("Controller")); + + Type baseAttribute = typeof(AutoHostingAttribute); + Type baseController = typeof(ControllerBase); + foreach (var controllerType in controllerTypes) + { + if (controllerType.IsSubclassOf(baseController) && controllerType.IsDefined(baseAttribute)) + { + + // 如果属于控制器,并标记了AutoHosting特性,进行自动注册 + AutoRegisterAutoController(controllerType); + } + else + { + continue; + } + } + } + + + /// + /// 自动注册 自动实例化控制器 类型 + /// + /// + public void AutoRegisterAutoController(Type controllerType) // 方法声明,用于注册并实例化控制器类型 + { + if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回 + + var autoHostingAttribute = controllerType.GetCustomAttribute(); + if (autoHostingAttribute != null) { + foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 + { + var apiGetAttribute = method.GetCustomAttribute(); + var apiPostAttribute = method.GetCustomAttribute(); + if( apiGetAttribute == null && apiPostAttribute == null ) + { + continue; + } + + + + WebApiAttribute webApiAttribute = new WebApiAttribute() + { + Type = apiGetAttribute != null ? API.GET : API.POST, + Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url, + IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl, + }; + + + + if (apiPostAttribute != null) // 如果存在 WebAPIAttribute 属性 + { + var url = AddRoutesUrl(autoHostingAttribute, + webApiAttribute, + controllerType, method); + Console.WriteLine(url); + if (url == null) continue; + _controllerAutoHosting[url] = true; + _controllerTypes[url] = controllerType; + + _controllerInstances[url] = null; + + } + + + /* var routeAttribute = method.GetCustomAttribute(); // 获取方法上的 WebAPIAttribute 自定义属性 + if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性 + { + var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method); + Console.WriteLine(url); + if (url == null) continue; + _controllerAutoHosting[url] = true; + _controllerTypes[url] = controllerType; + _controllerInstances[url] = null; + }*/ + } + } + } + /// + /// 手动注册 自动实例化控制器实例 + /// + public void RegisterAutoController() // 方法声明,用于动态注册路由 + { + Type controllerType = typeof(T); // 获取控制器实例的类型 + foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 + { + var apiGetAttribute = method.GetCustomAttribute(); + var apiPostAttribute = method.GetCustomAttribute(); + if (apiGetAttribute == null && apiPostAttribute == null) + { + continue; + } + + + + WebApiAttribute webApiAttribute = new WebApiAttribute() + { + Type = apiGetAttribute != null ? API.GET : API.POST, + Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url, + IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl, + }; + + + + var url = AddRoutesUrl(null, webApiAttribute, controllerType, method); + + if (url == null) continue; + _controllerAutoHosting[url] = true; + _controllerTypes[url] = controllerType; + + _controllerInstances[url] = null; + + } + } + + + /// + /// 手动注册 实例持久控制器实例 + /// + /// + public void RegisterController(TController controllerInstance) where TController : ControllerBase // 方法声明,用于动态注册路由 + { + if(controllerInstance == null) return; + Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型 + foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 + { + var apiGetAttribute = method.GetCustomAttribute(); + var apiPostAttribute = method.GetCustomAttribute(); + if (apiGetAttribute == null && apiPostAttribute == null) + { + continue; + } + + + + WebApiAttribute webApiAttribute = new WebApiAttribute() + { + Type = apiGetAttribute != null ? API.GET : API.POST, + Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url, + IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl, + }; + + + + var url = AddRoutesUrl(null, webApiAttribute, controllerType, method); + + if (url == null) continue; + _controllerInstances[url] = controllerInstance; + _controllerAutoHosting[url] = false; + } + } + + /// + /// 从方法中收集路由信息 + /// + /// + public string AddRoutesUrl(AutoHostingAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method) + { + string controllerName; + if (autoHostingAttribute == null || string.IsNullOrWhiteSpace(autoHostingAttribute.Url)) + { + controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写 + } + else + { + controllerName = autoHostingAttribute.Url; + } + + var httpMethod = webAttribute.Type; // 获取 HTTP 方法 + var customUrl = webAttribute.Url; // 获取自定义 URL + + string url; + + if (webAttribute.IsUrl) + { + + if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空 + { + url = $"/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL + } + else + { + customUrl = CleanUrl(customUrl); + url = $"/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL + } + _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中 + } + else + { + if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空 + { + url = $"/{controllerName}".ToLower(); // 构建默认 URL + } + else + { + customUrl = CleanUrl(customUrl); + url = $"/{controllerName}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL + } + _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中 + } + + return url; + + } + + + /// + /// 收集路由信息 + /// + /// + public void CollectRoutes(Type controllerType) + { + string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写 + foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法 + { + var routeAttribute = method.GetCustomAttribute(); // 获取方法上的 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.Type; // 获取 HTTP 方法 + _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中 + } + } + } + + + /// + /// 解析路由,调用对应的方法 + /// + /// + /// + public async Task RouteAsync(HttpListenerContext context) + { + var request = context.Request; // 获取请求对象 + var response = context.Response; // 获取响应对象 + var url = request.Url; // 获取请求的 URL + var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法 + + var template = request.Url.AbsolutePath.ToLower(); + + + if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method)) + { + return false; + } + + + + var routeValues = GetUrlData(url); // 解析 URL 获取路由参数 + + ControllerBase controllerInstance; + if (!_controllerAutoHosting[template]) + { + controllerInstance = (ControllerBase)_controllerInstances[template]; + } + else + { + + controllerInstance = (ControllerBase)serviceRegistry.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例 + + + } + + if (controllerInstance == null) + { + return false; // 未找到控制器实例 + } + + controllerInstance.Url = url.AbsolutePath; + object result; + switch (httpMethod) // 根据请求的 HTTP 方法执行不同的操作 + { + case "GET": // 如果是 GET 请求,传入方法、控制器、url参数 + result = InvokeControllerMethodWithRouteValues(method, controllerInstance, routeValues); + break; + case "POST": // POST 请求传入方法、控制器、请求体内容,url参数 + var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容 + controllerInstance.BobyData = requestBody; + var requestJObject = requestBody.FromJSON(); + + result = InvokeControllerMethod(method, controllerInstance, requestJObject, routeValues); + break; + default: + + result = null; + + break; + } + + Return(response, result); // 返回结果 + + return true; + } + + public static string GetLog(string Url, string BobyData = "") + { + return Environment.NewLine + + "Url : " + Url + Environment.NewLine + + "Data : " + BobyData + Environment.NewLine; + } + + /// + /// GET请求的控制器方法 + /// + private object InvokeControllerMethodWithRouteValues(MethodInfo method, object controllerInstance, Dictionary routeValues) + { + object[] parameters = GetMethodParameters(method, routeValues); + return InvokeMethod(method, controllerInstance, parameters); + } + + private static readonly Dictionary methodParameterCache = new Dictionary(); + /// + /// POST请求的调用控制器方法 + /// + public object InvokeControllerMethod(MethodInfo method, object controllerInstance, dynamic requestData, Dictionary routeValues) + { + object[] cachedMethodParameters; + + if (!methodParameterCache.TryGetValue(method, out ParameterInfo[] parameters)) + { + parameters = method.GetParameters(); + } + + cachedMethodParameters = new object[parameters.Length]; + + for (int i = 0; i < parameters.Length; i++) + { + string paramName = parameters[i].Name; + bool isUrlData = parameters[i].GetCustomAttribute(typeof(IsUrlDataAttribute)) != null; + bool isBobyData = parameters[i].GetCustomAttribute(typeof(IsBobyDataAttribute)) != null; + + if (isUrlData) + { + if (!string.IsNullOrEmpty(paramName) && routeValues.TryGetValue(paramName, out string value)) + { + cachedMethodParameters[i] = ConvertValue(value, parameters[i].ParameterType); + } + else + { + cachedMethodParameters[i] = null; + } + } + else if (isBobyData) + { + cachedMethodParameters[i] = ConvertValue(requestData.ToString(), parameters[i].ParameterType); + } + else + { + if (requestData.ContainsKey(paramName)) + { + if (parameters[i].ParameterType == typeof(string)) + { + cachedMethodParameters[i] = requestData[paramName].ToString(); + } + else if (parameters[i].ParameterType == typeof(bool)) + { + cachedMethodParameters[i] = requestData[paramName?.ToLower()].ToBool(); + } + else if (parameters[i].ParameterType == typeof(int)) + { + cachedMethodParameters[i] = requestData[paramName].ToInt(); + } + else if (parameters[i].ParameterType == typeof(double)) + { + cachedMethodParameters[i] = requestData[paramName].ToDouble(); + } + else + { + cachedMethodParameters[i] = ConvertValue(requestData[paramName], parameters[i].ParameterType); + } + } + else + { + cachedMethodParameters[i] = null; + } + } + } + + // 缓存方法和参数的映射 + //methodParameterCache[method] = cachedMethodParameters; + + + // 调用方法 + + return method.Invoke(controllerInstance, cachedMethodParameters); + + } + + + /// + /// 检查方法入参参数类型,返回对应的入参数组 + /// + /// + /// + /// + private object[] GetMethodParameters(MethodInfo method, Dictionary routeValues) + { + ParameterInfo[] methodParameters = method.GetParameters(); + object[] parameters = new object[methodParameters.Length]; + + for (int i = 0; i < methodParameters.Length; i++) + { + + string paramName = methodParameters[i].Name; + + + if (routeValues.TryGetValue(paramName, out string value)) + { + parameters[i] = ConvertValue(value, methodParameters[i].ParameterType); + } + else + { + + parameters[i] = null; + + } + + } + + return parameters; + } + + /*/// + /// 转为对应的类型 + /// + /// + /// + /// + private object ConvertValue(object value, Type targetType) + { + try + { + return JsonConvert.DeserializeObject(value.ToString(), targetType); + } + catch (JsonReaderException ex) + { + return value; + } + catch (JsonSerializationException ex) + { + // 如果无法转为对应的JSON对象 + int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引 + int endIndex = ex.Message.IndexOf("'", startIndex); // 查找类型信息结束的索引 + var typeInfo = ex.Message.Substring(startIndex, endIndex - startIndex); // 提取出错类型信息,该怎么传出去? + return null; + } + catch // (Exception ex) + { + return value; + } + }*/ + /// + /// 转为对应的类型 + /// + /// + /// + /// + private object ConvertValue(string value, Type targetType) + { + if(targetType == typeof(string)) + { + return value; + } + + try + { + + return JsonConvert.DeserializeObject(value.ToString(), targetType); + + } + catch (JsonReaderException ex) + { + return value; + } + catch (JsonSerializationException ex) + { + // 如果无法转为对应的JSON对象 + int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引 + int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引 + var typeInfo = ex.Message.Substring(startIndex, endIndex); // 提取出错类型信息,该怎么传出去? + + return null; + + } + catch // (Exception ex) + { + return value; + } + } + + /// + /// 调用控制器方法传入参数 + /// + /// 方法 + /// 控制器实例 + /// 参数列表 + /// + private static object InvokeMethod(MethodInfo method, object controllerInstance, object[] methodParameters) + { + + object result = null; + + try + { + + result = method?.Invoke(controllerInstance, methodParameters); + + } + catch (ArgumentException ex) + { + string targetType = ExtractTargetTypeFromExceptionMessage(ex.Message); + + // 如果方法调用失败 + result = new + { + error = $"函数签名类型[{targetType}]不符合", + }; + } + catch (JsonSerializationException ex) + { + + // 查找类型信息开始的索引 + int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; + // 查找类型信息结束的索引 + int endIndex = ex.Message.IndexOf('\''); + // 提取类型信息 + string typeInfo = ex.Message.Substring(startIndex, endIndex); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + + return result; // 调用方法并返回结果 + + } + + + /// + /// 方法声明,用于解析 URL 获取路由参数 + /// + /// + /// + private static Dictionary GetUrlData(Uri uri) + { + Dictionary routeValues = new Dictionary(); + + var pathParts = uri.ToString().Split('?'); // 拆分 URL,获取路径部分 + + if (pathParts.Length > 1) // 如果包含查询字符串 + { + var queryParams = HttpUtility.ParseQueryString(pathParts[1]); // 解析查询字符串 + + foreach (string key in queryParams) // 遍历查询字符串的键值对 + { + if (key == null) continue; + + routeValues[key] = queryParams[key]; // 将键值对添加到路由参数字典中 + + } + } + + return routeValues; // 返回路由参数字典 + } + + /// + /// 读取Body中的消息 + /// + /// + /// + private static async Task ReadRequestBodyAsync(HttpListenerRequest request) + { + using (Stream stream = request.InputStream) + using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) + { + return await reader.ReadToEndAsync(); + } + } + /// + /// 返回响应消息 + /// + /// + /// + private static void Return(HttpListenerResponse response, dynamic msg) + { + string resultData; + if (response != null) + { + try + { + if (msg is IEnumerable && !(msg is string)) + { + // If msg is a collection (e.g., array or list), serialize it as JArray + resultData = JArray.FromObject(msg).ToString(); + } + else + { + // Otherwise, serialize it as JObject + resultData = JObject.FromObject(msg).ToString(); + } + byte[] buffer = Encoding.UTF8.GetBytes(resultData); + response.ContentLength64 = buffer.Length; + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch + { + // If serialization fails, use the original message's string representation + resultData = msg.ToString(); + } + + + } + } + + /// + /// 解析JSON + /// + /// + /// + /// + private static dynamic ParseJson(string requestBody) + { + try + { + if (string.IsNullOrWhiteSpace(requestBody)) + { + throw new Exception("Invalid JSON format"); + } + return JObject.Parse(requestBody); + } + catch + { + throw new Exception("Invalid JSON format"); + } + } + + /// + /// 修正方法特性中的URL格式 + /// + /// + /// + private static string CleanUrl(string url) + { + + while (url.Length > 0 && url[0] == '/') // 去除开头的斜杠 + { + url = url.Substring(1,url.Length-1); + } + + while (url.Length > 0 && url[url.Length-1] == '/') // 去除末尾的斜杠 + { + url = url.Substring(0,url.Length-1); + } + + for (int i = 0; i < url.Length - 1; i++) // 去除连续的斜杠 + { + if (url[i] == '/' && url[i + 1] == '/') + { + url = url.Remove(i, 1); + i--; + } + } + + return url; // 返回清理后的 URL + } + /// + /// 从控制器调用方法的异常中获取出出错类型的信息 + /// + /// + /// + public static string ExtractTargetTypeFromExceptionMessage(string errorMessage) + { + string targetText = "为类型“"; + int startIndex = errorMessage.IndexOf(targetText); + if (startIndex != -1) + { + startIndex += targetText.Length; + int endIndex = errorMessage.IndexOf('\''); + if (endIndex != -1) + { + return errorMessage.Substring(startIndex, endIndex); + } + } + + + return null; + + } + } +} + diff --git a/Library.Framework/Http/WebAPIAttribute.cs b/Library.Framework/Http/WebAPIAttribute.cs new file mode 100644 index 0000000..b13539f --- /dev/null +++ b/Library.Framework/Http/WebAPIAttribute.cs @@ -0,0 +1,196 @@ +using Serein.Library.Api.Api; +using Serein.Library.Framework.IOC; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace Serein.Library.Framework.Http +{ + + /// + /// HTTP接口监听类 + /// + public class WebServer + { + private readonly HttpListener listener; // HTTP 监听器 + private Router router; // 路由器 + private readonly RequestLimiter requestLimiter; //接口防刷 + + + + public WebServer() + + { + listener = new HttpListener(); + + requestLimiter = new RequestLimiter(5, 8); + + } + + // 启动服务器 + public WebServer Start(string prefixe, ISereinIoc serviceContainer) + { + try + { + router = new Router(serviceContainer); + if (listener.IsListening) + { + return this; + } + + if (!prefixe.Substring(prefixe.Length - 1, 1).Equals(@"/")) + { + prefixe += @"/"; + } + + + listener.Prefixes.Add(prefixe); // 添加监听前缀 + listener.Start(); // 开始监听 + + Console.WriteLine($"开始监听:{prefixe}"); + Task.Run(async () => + { + while (listener.IsListening) + { + var context = await listener.GetContextAsync(); // 获取请求上下文 + _ = Task.Run(() => ProcessRequestAsync(context)); // 处理请求) + } + }); + return this; + } + catch (HttpListenerException ex) when (ex.ErrorCode == 183) + { + return this; + } + } + + + /// + /// 处理请求 + /// + /// + /// + private async Task ProcessRequestAsync(HttpListenerContext context) + { + // 添加CORS头部 + context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); + context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); + context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type"); + + // 处理OPTIONS预检请求 + if (context.Request.HttpMethod == "OPTIONS") + { + context.Response.StatusCode = (int)HttpStatusCode.OK; + context.Response.Close(); + return; + } + + var isPass = await router.RouteAsync(context); // 路由解析 + if (isPass) + { + context.Response.StatusCode = (int)HttpStatusCode.OK; + context.Response.Close(); // 关闭响应 + } + else + { + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + context.Response.Close(); // 关闭响应 + } + + //var isPass = requestLimiter.AllowRequest(context.Request); + //if (isPass) + //{ + // // 如果路由没有匹配,返回 404 + // router.RouteAsync(context); // 路由解析 + //} + //else + //{ + // context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误 + // context.Response.Close(); // 关闭响应 + //} + + // var request = context.Request; + // 获取远程终结点信息 + //var remoteEndPoint = context.Request.RemoteEndPoint; + //// 获取用户的IP地址和端口 + //IPAddress ipAddress = remoteEndPoint.Address; + //int port = remoteEndPoint.Port; + //Console.WriteLine("外部连接:" + ipAddress.ToString() + ":" + port); + } + + // 停止服务器 + public void Stop() + { + if (listener.IsListening) + { + listener?.Stop(); // 停止监听 + listener?.Close(); // 关闭监听器 + } + } + + public void RegisterAutoController() + { + //var instance = Activator.CreateInstance(typeof(T)); + router.RegisterAutoController(); + } + + /*public void RegisterRoute(T controllerInstance) + { + router.RegisterRoute(controllerInstance); + }*/ + } + /// + /// 判断访问接口的频次是否正常 + /// + public class RequestLimiter + { + public RequestLimiter(int seconds, int maxRequests) + { + interval = TimeSpan.FromSeconds(seconds); + maxRequests = maxRequests; + } + + private readonly ConcurrentDictionary> requestHistory = new ConcurrentDictionary>(); + private readonly TimeSpan interval; + private readonly int maxRequests; + + /// + /// 判断访问接口的频次是否正常 + /// + /// + public bool AllowRequest(HttpListenerRequest request) + { + var clientIp = request.RemoteEndPoint.Address.ToString(); + var clientPort = request.RemoteEndPoint.Port; + var clientKey = clientIp + ":" + clientPort; + + var now = DateTime.Now; + + // 尝试从字典中获取请求队列,不存在则创建新的队列 + var requests = requestHistory.GetOrAdd(clientKey, new Queue()); + + lock (requests) + { + // 移除超出时间间隔的请求记录 + while (requests.Count > 0 && now - requests.Peek() > interval) + { + requests.Dequeue(); + } + + // 如果请求数超过限制,拒绝请求 + if (requests.Count >= maxRequests) + { + return false; + } + + // 添加当前请求时间,并允许请求 + requests.Enqueue(now); + } + + return true; + } + } + +} diff --git a/Library.Framework/IOC/SereinIoc.cs b/Library.Framework/IOC/SereinIoc.cs new file mode 100644 index 0000000..2842b93 --- /dev/null +++ b/Library.Framework/IOC/SereinIoc.cs @@ -0,0 +1,378 @@ +using Serein.Library.Api; +using Serein.Library.Attributes; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Serein.Library.Framework.IOC +{ + + public class SereinIoc : ISereinIoc + { + + private readonly ConcurrentDictionary _dependencies; + private readonly ConcurrentDictionary _typeMappings; + private readonly List _waitingForInstantiation; + + public SereinIoc() + { + + _dependencies = new ConcurrentDictionary + { + [typeof(ISereinIoc).FullName] = this + }; + + _typeMappings = new ConcurrentDictionary(); + _waitingForInstantiation = new List(); + } + public object GetOrCreateServiceInstance(Type type, params object[] parameters) + { + Register(type); + object instance; + + if (_dependencies.ContainsKey(type.FullName)) + { + instance = _dependencies[type.FullName]; + } + else + { + + instance = Activator.CreateInstance(type); + + + _dependencies[type.FullName] = instance; + + } + + + return instance; + + } + public T CreateServiceInstance(params object[] parameters) + { + return (T)GetOrCreateServiceInstance(typeof(T), parameters); + } + + public ISereinIoc Reset() + { + foreach(var instancei in _dependencies.Values) + { + if (typeof(IDisposable).IsAssignableFrom(instancei.GetType()) && instancei is IDisposable disposable) + { + disposable.Dispose(); + } + } + _dependencies.Clear(); + _waitingForInstantiation.Clear(); + //_typeMappings.Clear(); + return this; + } + + public ISereinIoc Register(Type type, params object[] parameters) + { + + if (!_typeMappings.ContainsKey(type.FullName)) + { + _typeMappings[type.FullName] = type; + } + + return this; + } + public ISereinIoc Register(params object[] parameters) + { + Register(typeof(T), parameters); + return this; + } + + public ISereinIoc Register(params object[] parameters) + where TImplementation : TService + { + _typeMappings[typeof(TService).FullName] = typeof(TImplementation); + return this; + } + + public object GetOrInstantiate(Type type) + { + + + if (!_dependencies.TryGetValue(type.FullName, out object value)) + { + Register(type); + + value = Instantiate(type); + + InjectDependencies(type); + } + + + + return value; + + } + + + public T GetOrInstantiate() + { + if(!_dependencies.TryGetValue(typeof(T).FullName, out object value)) + { + Register(); + + value = Instantiate(typeof(T)); + } + + return (T)value; + //throw new InvalidOperationException("目标类型未创建实例"); + } + public ISereinIoc Build() + { + foreach (var type in _typeMappings.Values) + { + + if(!_dependencies.ContainsKey(type.FullName)) + { + + _dependencies[type.FullName] = Activator.CreateInstance(type); + + } + + } + + foreach (var instance in _dependencies.Values) + { + + InjectDependencies(instance); // 替换占位符 + } + + //var instance = Instantiate(item.Value); + + TryInstantiateWaitingDependencies(); + return this; + } + + public object Instantiate(Type controllerType, params object[] parameters) + { + var instance = Activator.CreateInstance(controllerType, parameters); + if(instance != null) + { + InjectDependencies(instance); + } + return instance; + } + + private void InjectDependencies(object instance) + { + var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToArray() + .Where(p => p.CanWrite && p.GetCustomAttribute() != null); + + foreach (var property in properties) + { + var propertyType = property.PropertyType; + + if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance)) + { + property.SetValue(instance, dependencyInstance); + } + + } + } + + private void TryInstantiateWaitingDependencies() + { + foreach (var waitingType in _waitingForInstantiation.ToList()) + { + if (_typeMappings.TryGetValue(waitingType.FullName, out var implementationType)) + { + var instance = Instantiate(implementationType); + if (instance != null) + { + + _dependencies[waitingType.FullName] = instance; + + _waitingForInstantiation.Remove(waitingType); + } + } + } + } + + #region run() + public ISereinIoc Run(Action action) + { + var service = GetOrInstantiate(); + if (service != null) + { + action(service); + } + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + + action(service1, service2); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + action(service1, service2, service3); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + action(service1, service2, service3, service4); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + var service5 = GetOrInstantiate(); + action(service1, service2, service3, service4, service5); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + var service5 = GetOrInstantiate(); + var service6 = GetOrInstantiate(); + action(service1, service2, service3, service4, service5, service6); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + var service5 = GetOrInstantiate(); + var service6 = GetOrInstantiate(); + var service7 = GetOrInstantiate(); + action(service1, service2, service3, service4, service5, service6, service7); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + var service5 = GetOrInstantiate(); + var service6 = GetOrInstantiate(); + var service7 = GetOrInstantiate(); + var service8 = GetOrInstantiate(); + action(service1, service2, service3, service4, service5, service6, service7, service8); + return this; + } + + #endregion + } + + + /* public interface IServiceContainer + { + ServiceContainer Register(params object[] parameters); + ServiceContainer Register(params object[] parameters) where TImplementation : TService; + TService Resolve(); + void Get(Action action); + object Instantiate(Type type, params object[] parameters); + + } + public class ServiceContainer : IServiceContainer + { + private readonly Dictionary _dependencies; + public ServiceContainer() + { + _dependencies = new Dictionary + { + [typeof(IServiceContainer)] = this + }; + } + + public void Get(Action action) + { + var service = Resolve(); + action(service); + } + public ServiceContainer Register(params object[] parameters) + { + var instance = Instantiate(typeof(T), parameters); + _dependencies[typeof(T)] = instance; + return this; + } + + public ServiceContainer Register(params object[] parameters) + where TImplementation : TService + { + + _dependencies[typeof(TService)] = Instantiate(typeof(TImplementation), parameters); + return this; + } + + + public TService Resolve() + { + return (TService)_dependencies[typeof(TService)]; + } + + public object Instantiate(Type controllerType, params object[] parameters) + { + var constructors = controllerType.GetConstructors(); // 获取控制器的所有构造函数 + + // 查找具有最多参数的构造函数 + var constructor = constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault(); + + if (constructor != null) + { + if (parameters.Length > 0) + { + return Activator.CreateInstance(controllerType, parameters); + } + else { + var tmpParameters = constructor.GetParameters(); + var dependencyInstances = new List(); + + foreach (var parameter in tmpParameters) + { + var parameterType = parameter.ParameterType; + _dependencies.TryGetValue(parameterType, out var dependencyInstance); + dependencyInstances.Add(dependencyInstance); + if (dependencyInstance == null) + { + return null; + } + } + // 用解析的依赖项实例化目标类型 + return Activator.CreateInstance(controllerType, dependencyInstances.ToArray()); + } + } + else + { + return Activator.CreateInstance(controllerType); + } + } + }*/ + + + +} diff --git a/Library.Framework/NodeFlow/Api.cs b/Library.Framework/NodeFlow/Api.cs new file mode 100644 index 0000000..e0b48ac --- /dev/null +++ b/Library.Framework/NodeFlow/Api.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Framework.NodeFlow +{ + public interface IDynamicFlowNode + { + } + +} diff --git a/Library.Framework/NodeFlow/DynamicContext.cs b/Library.Framework/NodeFlow/DynamicContext.cs new file mode 100644 index 0000000..5260c33 --- /dev/null +++ b/Library.Framework/NodeFlow/DynamicContext.cs @@ -0,0 +1,40 @@ +using Serein.Library.Api; +using Serein.Library.Utils; +using System; +using System.Threading.Tasks; + +namespace Serein.Library.Framework.NodeFlow +{ + + + /// + /// 动态流程上下文 + /// + 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) + { + if(NodeRunCts == null) + { + + NodeRunCts = SereinIoc.GetOrInstantiate(); + } + return Task.Factory.StartNew(async () => + { + for (int i = 0; i < count; i++) + { + NodeRunCts.Token.ThrowIfCancellationRequested(); + await Task.Delay(time); + action.Invoke(); + } + }); + } + } +} diff --git a/Library.Framework/NodeFlow/DynamicNodeFrameworkType.cs b/Library.Framework/NodeFlow/DynamicNodeFrameworkType.cs new file mode 100644 index 0000000..c1a9b5f --- /dev/null +++ b/Library.Framework/NodeFlow/DynamicNodeFrameworkType.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.LibraryFramework.NodeFlow +{ + public enum DynamicNodeFrameworkType + { + /// + /// 初始化 + /// + Init, + /// + /// 开始载入 + /// + Loading, + /// + /// 结束 + /// + Exit, + + /// + /// 触发器 + /// + Flipflop, + /// + /// 条件节点 + /// + Condition, + /// + /// 动作节点 + /// + Action, + } + +} diff --git a/Library.Framework/NodeFlow/FlipflopContext.cs b/Library.Framework/NodeFlow/FlipflopContext.cs new file mode 100644 index 0000000..f97056b --- /dev/null +++ b/Library.Framework/NodeFlow/FlipflopContext.cs @@ -0,0 +1,94 @@ +using Serein.Library.Api; +using Serein.Library.Enums; +using System; +using System.Threading.Tasks; + +namespace Serein.Library.Framework.NodeFlow +{ + //public enum FfState + //{ + // Succeed, + // Cancel, + // Error, + //} + + //public class FlipflopContext + //{ + // public FlowStateType State { get; set; } + // public object? Data { get; set; } + // public FlipflopContext(FlowStateType ffState, object? data = null) + // { + // State = ffState; + // Data = data; + // } + //} + + public static class FlipflopFunc + { + /// + /// 传入触发器方法的返回类型,尝试获取Task[Flipflop[]] 中的泛型类型 + /// + //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]; + + // 检查泛型参数是否为 Flipflop<> + if (innerType == typeof(FlipflopContext)) + //if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>)) + { + return true; + } + } + + return false; + } + } + + /// + /// 触发器上下文 + /// + 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; + } + + + } + +} diff --git a/Library.Framework/NodeFlow/FlowStateType.cs b/Library.Framework/NodeFlow/FlowStateType.cs new file mode 100644 index 0000000..7760a19 --- /dev/null +++ b/Library.Framework/NodeFlow/FlowStateType.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Framework.NodeFlow +{ + + //public enum FlowStateType + //{ + // /// + // /// 成功(方法成功执行) + // /// + // Succeed, + // /// + // /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误) + // /// + // Fail, + // /// + // /// 异常(节点没有成功执行,执行时发生非预期的错误) + // /// + // Error, + //} +} diff --git a/Library.Framework/NodeFlow/Tool/Attribute.cs b/Library.Framework/NodeFlow/Tool/Attribute.cs new file mode 100644 index 0000000..1b82d58 --- /dev/null +++ b/Library.Framework/NodeFlow/Tool/Attribute.cs @@ -0,0 +1,8 @@ +using Serein.Library.Api.Enums; +using System; + +namespace Serein.Library.Framework.NodeFlow.Tool +{ + + +} diff --git a/Library.Framework/NodeFlow/Tool/DynamicTool.cs b/Library.Framework/NodeFlow/Tool/DynamicTool.cs new file mode 100644 index 0000000..b1862fa --- /dev/null +++ b/Library.Framework/NodeFlow/Tool/DynamicTool.cs @@ -0,0 +1,194 @@ +namespace Serein.Library.Framework.NodeFlow.Tool +{ + + #region 锁、tsk工具 (已注释) + /*public class LockManager + { + private readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); + + 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(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> Queue { get; } = new Queue>(); + } + } + + + public interface ITaskResult + { + object Result { get; } + } + + public class TaskResult : ITaskResult + { + public TaskResult(T result) + { + Result = result; + } + + public T Result { get; } + + object ITaskResult.Result => Result; + } + + public class DynamicTasks + { + private static readonly ConcurrentDictionary> TaskGuidPairs = new(); + public static Task GetTask(string Guid) + { + TaskGuidPairs.TryGetValue(Guid, out Task task); + return task; + } + + public static bool AddTask(string Guid, T result) + { + var task = Task.FromResult(new TaskResult(result)); + + return TaskGuidPairs.TryAdd(Guid, task); + } + } + public class TaskNodeManager + { + private readonly ConcurrentDictionary _taskQueues = new ConcurrentDictionary(); + + 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(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> Queue { get; } = new Queue>(); + } + }*/ + #endregion + + + +} diff --git a/Library.Framework/NodeFlow/Tool/TcsSignal.cs b/Library.Framework/NodeFlow/Tool/TcsSignal.cs new file mode 100644 index 0000000..6989b6e --- /dev/null +++ b/Library.Framework/NodeFlow/Tool/TcsSignal.cs @@ -0,0 +1,65 @@ +using Serein.Library.Enums; +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; + +namespace Serein.Library.Framework.NodeFlow.Tool +{ + public class TcsSignalException : Exception + { + public FlowStateType FsState { get; set; } + public TcsSignalException(string message) : base(message) + { + FsState = FlowStateType.Error; + } + } + + public class TcsSignal where TSignal : struct, Enum + { + //public ConcurrentDictionary>> TcsEvent { get; } = new(); + public ConcurrentDictionary> TcsEvent { get; } = new ConcurrentDictionary>(); + + public ConcurrentDictionary TcsLock { get; } = new ConcurrentDictionary(); + + /// + /// 触发信号 + /// + /// + /// 信号 + /// 传递的参数 + /// 是否成功触发 + public bool TriggerSignal(TSignal signal, T value) + { + var tcsLock = TcsLock.GetOrAdd(signal, new object()); + lock (tcsLock) + { + if (TcsEvent.TryRemove(signal, out var waitTcs)) + { + waitTcs.SetResult(value); + return true; + } + return false; + } + } + + public TaskCompletionSource CreateTcs(TSignal signal) + { + var tcsLock = TcsLock.GetOrAdd(signal, new object()); + lock (tcsLock) + { + var tcs = TcsEvent.GetOrAdd(signal, new TaskCompletionSource()); + return tcs; + } + + } + + public void CancelTask() + { + foreach (var tcs in TcsEvent.Values) + { + tcs.SetException(new TcsSignalException("任务取消")); + } + TcsEvent.Clear(); + } + } +} diff --git a/Library.Framework/Properties/AssemblyInfo.cs b/Library.Framework/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..615246e --- /dev/null +++ b/Library.Framework/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("Serein.Library.Framework")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Serein.Library.Framework")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("73b272e8-222d-4d08-a030-f1e1db70b9d1")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Library.Framework/Serein.Library.Framework.csproj b/Library.Framework/Serein.Library.Framework.csproj new file mode 100644 index 0000000..f0f00c1 --- /dev/null +++ b/Library.Framework/Serein.Library.Framework.csproj @@ -0,0 +1,63 @@ + + + + + Debug + AnyCPU + {73B272E8-222D-4D08-A030-F1E1DB70B9D1} + Library + Properties + Serein.Library.Framework + Serein.Library.Framework + v4.6.1 + 512 + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + {5e19d0f2-913a-4d1c-a6f8-1e1227baa0e3} + Serein.Library + + + + \ No newline at end of file diff --git a/Library.Framework/Tool/DataHelper.cs b/Library.Framework/Tool/DataHelper.cs new file mode 100644 index 0000000..a2140e7 --- /dev/null +++ b/Library.Framework/Tool/DataHelper.cs @@ -0,0 +1,186 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Tool +{ + public static class DataHelper + { + /// + /// 把Object转换为Json字符串 + /// + /// + /// + public static string ToJson(this object obj) + { + IsoDateTimeConverter val = new IsoDateTimeConverter(); + val.DateTimeFormat = "yyyy-MM-dd HH:mm:ss"; + IsoDateTimeConverter val2 = val; + return JsonConvert.SerializeObject(obj, (JsonConverter[])(object)new JsonConverter[1] { (JsonConverter)val2 }); + } + + + + /// + /// 把Json文本转为实体 + /// + /// + /// + /// + public static T FromJSON(this string input) + { + try + { + if (typeof(T).IsAssignableFrom(typeof(T))) + { + + } + + return JsonConvert.DeserializeObject(input); + + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + // return default(T); + + return default; + + } + } + + public static List IListToList(IList list) + { + T[] array = new T[list.Count]; + list.CopyTo(array, 0); + return new List(array); + } + + public static DataTable GetNewDataTable(DataTable dt, string condition) + { + if (!IsExistRows(dt)) + { + if (condition.Trim() == "") + { + return dt; + } + + DataTable dataTable = new DataTable(); + dataTable = dt.Clone(); + DataRow[] array = dt.Select(condition); + for (int i = 0; i < array.Length; i++) + { + dataTable.ImportRow(array[i]); + } + + return dataTable; + } + + + return null; + + } + + public static bool IsExistRows(DataTable dt) + { + if (dt != null && dt.Rows.Count > 0) + { + return false; + } + + return true; + } + + public static Hashtable DataTableToHashtable(DataTable dt) + { + Hashtable hashtable = new Hashtable(); + foreach (DataRow row in dt.Rows) + { + for (int i = 0; i < dt.Columns.Count; i++) + { + string columnName = dt.Columns[i].ColumnName; + hashtable[columnName] = row[columnName]; + } + } + + return hashtable; + } + + public static DataTable ListToDataTable(List entitys) + { + if (entitys == null || entitys.Count < 1) + { + + return null; + + } + + + Type type = entitys[0].GetType(); + + PropertyInfo[] properties = type.GetProperties(); + DataTable dataTable = new DataTable(); + for (int i = 0; i < properties.Length; i++) + { + dataTable.Columns.Add(properties[i].Name); + } + + foreach (T entity in entitys) + { + + object obj = entity; + + + if (obj.GetType() != type) + { + throw new Exception("要转换的集合元素类型不一致"); + } + + + object[] array = new object[properties.Length]; + for (int j = 0; j < properties.Length; j++) + { + + array[j] = properties[j].GetValue(obj, null); + + } + + dataTable.Rows.Add(array); + } + + return dataTable; + } + + public static string DataTableToXML(DataTable dt) + { + if (dt != null && dt.Rows.Count > 0) + { + StringWriter stringWriter = new StringWriter(); + dt.WriteXml((TextWriter)stringWriter); + return stringWriter.ToString(); + } + + return string.Empty; + } + + public static string DataSetToXML(DataSet ds) + { + if (ds != null) + { + StringWriter stringWriter = new StringWriter(); + ds.WriteXml((TextWriter)stringWriter); + return stringWriter.ToString(); + } + + return string.Empty; + } + } +} diff --git a/Library.Framework/packages.config b/Library.Framework/packages.config new file mode 100644 index 0000000..d04b6cf --- /dev/null +++ b/Library.Framework/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Library/Api/IDynamicContext.cs b/Library/Api/IDynamicContext.cs new file mode 100644 index 0000000..991bc47 --- /dev/null +++ b/Library/Api/IDynamicContext.cs @@ -0,0 +1,13 @@ +using Serein.Library.Utils; +using System; +using System.Threading.Tasks; + +namespace Serein.Library.Api +{ + public interface IDynamicContext + { + NodeRunCts NodeRunCts { get; set; } + ISereinIoc SereinIoc { get; } + Task CreateTimingTask(Action action, int time = 100, int count = -1); + } +} diff --git a/Library/Api/IFlipflopContext.cs b/Library/Api/IFlipflopContext.cs new file mode 100644 index 0000000..8722869 --- /dev/null +++ b/Library/Api/IFlipflopContext.cs @@ -0,0 +1,10 @@ +using Serein.Library.Enums; + +namespace Serein.Library.Api +{ + public interface IFlipflopContext + { + FlowStateType State { get; set; } + object Data { get; set; } + } +} diff --git a/Library/Api/ISereinIoc.cs b/Library/Api/ISereinIoc.cs new file mode 100644 index 0000000..2d64cb5 --- /dev/null +++ b/Library/Api/ISereinIoc.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Serein.Library.Api +{ + public interface ISereinIoc + { + /// + /// 获取或创建类型的实例(不注入依赖项) + /// + object GetOrCreateServiceInstance(Type serviceType, params object[] parameters); + T CreateServiceInstance(params object[] parameters); + ISereinIoc Reset(); // 清空 + ISereinIoc Register(Type type, params object[] parameters); + ISereinIoc Register(params object[] parameters); + ISereinIoc Register(params object[] parameters) where TImplementation : TService; + T GetOrInstantiate(); + object GetOrInstantiate(Type type); + + /// + /// 创建目标类型的对象, 并注入依赖项 + /// + object Instantiate(Type type, params object[] parameters); + ISereinIoc Build(); + ISereinIoc Run(Action action); + ISereinIoc Run(Action action); + ISereinIoc Run(Action action); + ISereinIoc Run(Action action); + ISereinIoc Run(Action action); + ISereinIoc Run(Action action); + ISereinIoc Run(Action action); + ISereinIoc Run(Action action); + } + +} diff --git a/Library/Enums/FlowStateType.cs b/Library/Enums/FlowStateType.cs new file mode 100644 index 0000000..a80c395 --- /dev/null +++ b/Library/Enums/FlowStateType.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Enums +{ + + public enum FlowStateType + { + /// + /// 成功(方法成功执行) + /// + Succeed, + /// + /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误) + /// + Fail, + /// + /// 异常(节点没有成功执行,执行时发生非预期的错误) + /// + Error, + } +} diff --git a/Library/Enums/NodeType.cs b/Library/Enums/NodeType.cs new file mode 100644 index 0000000..8307133 --- /dev/null +++ b/Library/Enums/NodeType.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Enums +{ + public enum NodeType + { + /// + /// 初始化 + /// + Init, + /// + /// 开始载入 + /// + Loading, + /// + /// 结束 + /// + Exit, + + /// + /// 触发器 + /// + Flipflop, + /// + /// 条件节点 + /// + Condition, + /// + /// 动作节点 + /// + Action, + } + +} diff --git a/Library/NodeAttribute.cs b/Library/NodeAttribute.cs new file mode 100644 index 0000000..7ebde65 --- /dev/null +++ b/Library/NodeAttribute.cs @@ -0,0 +1,49 @@ +using Serein.Library.Enums; +using System; + +namespace Serein.Library.Attributes +{ + /// + /// 表示该属性为自动注入依赖项 + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class AutoInjectionAttribute : Attribute + { + } + + /// + /// 用来判断一个类是否需要注册并构建实例(单例模式场景使用) + /// + [AttributeUsage(AttributeTargets.Class)] + public class DynamicFlowAttribute : Attribute + { + public DynamicFlowAttribute(bool scan = true) + { + Scan = scan; + } + public bool Scan { get; set; } = true; + } + + /// + /// 标记一个方法是什么类型,加载dll后用来拖拽到画布中 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class NodeActionAttribute : Attribute + { + public NodeActionAttribute(NodeType methodDynamicType, + string methodTips = "", + bool scan = true, + string lockName = "") + { + Scan = scan; + MethodDynamicType = methodDynamicType; + MethodTips = methodTips; + LockName = lockName; + } + public bool Scan { get; set; } + public string MethodTips { get; } + public NodeType MethodDynamicType { get; } + public string LockName { get; } + } + +} diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index d766447..756371e 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -1,28 +1,7 @@  - net8.0-windows7.0 - enable - enable - D:\Project\C#\DynamicControl\SereinFlow\.Output - Library + netstandard2.0;net461 - - - - - - - - - - - - - - - - - diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs new file mode 100644 index 0000000..f2d8417 --- /dev/null +++ b/Library/Utils/SereinIoc.cs @@ -0,0 +1,378 @@ +using Serein.Library.Api; +using Serein.Library.Attributes; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Serein.Library.Utils +{ + + public class SereinIoc : ISereinIoc + { + + private readonly ConcurrentDictionary _dependencies; + private readonly ConcurrentDictionary _typeMappings; + private readonly List _waitingForInstantiation; + + public SereinIoc() + { + + _dependencies = new ConcurrentDictionary + { + [typeof(ISereinIoc).FullName] = this + }; + + _typeMappings = new ConcurrentDictionary(); + _waitingForInstantiation = new List(); + } + public object GetOrCreateServiceInstance(Type type, params object[] parameters) + { + Register(type); + object instance; + + if (_dependencies.ContainsKey(type.FullName)) + { + instance = _dependencies[type.FullName]; + } + else + { + + instance = Activator.CreateInstance(type); + + + _dependencies[type.FullName] = instance; + + } + + + return instance; + + } + public T CreateServiceInstance(params object[] parameters) + { + return (T)GetOrCreateServiceInstance(typeof(T), parameters); + } + + public ISereinIoc Reset() + { + foreach(var instancei in _dependencies.Values) + { + if (typeof(IDisposable).IsAssignableFrom(instancei.GetType()) && instancei is IDisposable disposable) + { + disposable.Dispose(); + } + } + _dependencies.Clear(); + _waitingForInstantiation.Clear(); + //_typeMappings.Clear(); + return this; + } + + public ISereinIoc Register(Type type, params object[] parameters) + { + + if (!_typeMappings.ContainsKey(type.FullName)) + { + _typeMappings[type.FullName] = type; + } + + return this; + } + public ISereinIoc Register(params object[] parameters) + { + Register(typeof(T), parameters); + return this; + } + + public ISereinIoc Register(params object[] parameters) + where TImplementation : TService + { + _typeMappings[typeof(TService).FullName] = typeof(TImplementation); + return this; + } + + public object GetOrInstantiate(Type type) + { + + + if (!_dependencies.TryGetValue(type.FullName, out object value)) + { + Register(type); + + value = Instantiate(type); + + InjectDependencies(type); + } + + + + return value; + + } + + + public T GetOrInstantiate() + { + if(!_dependencies.TryGetValue(typeof(T).FullName, out object value)) + { + Register(); + + value = Instantiate(typeof(T)); + } + + return (T)value; + //throw new InvalidOperationException("目标类型未创建实例"); + } + public ISereinIoc Build() + { + foreach (var type in _typeMappings.Values) + { + + if(!_dependencies.ContainsKey(type.FullName)) + { + + _dependencies[type.FullName] = Activator.CreateInstance(type); + + } + + } + + foreach (var instance in _dependencies.Values) + { + + InjectDependencies(instance); // 替换占位符 + } + + //var instance = Instantiate(item.Value); + + TryInstantiateWaitingDependencies(); + return this; + } + + public object Instantiate(Type controllerType, params object[] parameters) + { + var instance = Activator.CreateInstance(controllerType, parameters); + if(instance != null) + { + InjectDependencies(instance); + } + return instance; + } + + private void InjectDependencies(object instance) + { + var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToArray() + .Where(p => p.CanWrite && p.GetCustomAttribute() != null); + + foreach (var property in properties) + { + var propertyType = property.PropertyType; + + if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance)) + { + property.SetValue(instance, dependencyInstance); + } + + } + } + + private void TryInstantiateWaitingDependencies() + { + foreach (var waitingType in _waitingForInstantiation.ToList()) + { + if (_typeMappings.TryGetValue(waitingType.FullName, out var implementationType)) + { + var instance = Instantiate(implementationType); + if (instance != null) + { + + _dependencies[waitingType.FullName] = instance; + + _waitingForInstantiation.Remove(waitingType); + } + } + } + } + + #region run() + public ISereinIoc Run(Action action) + { + var service = GetOrInstantiate(); + if (service != null) + { + action(service); + } + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + + action(service1, service2); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + action(service1, service2, service3); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + action(service1, service2, service3, service4); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + var service5 = GetOrInstantiate(); + action(service1, service2, service3, service4, service5); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + var service5 = GetOrInstantiate(); + var service6 = GetOrInstantiate(); + action(service1, service2, service3, service4, service5, service6); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + var service5 = GetOrInstantiate(); + var service6 = GetOrInstantiate(); + var service7 = GetOrInstantiate(); + action(service1, service2, service3, service4, service5, service6, service7); + return this; + } + + public ISereinIoc Run(Action action) + { + var service1 = GetOrInstantiate(); + var service2 = GetOrInstantiate(); + var service3 = GetOrInstantiate(); + var service4 = GetOrInstantiate(); + var service5 = GetOrInstantiate(); + var service6 = GetOrInstantiate(); + var service7 = GetOrInstantiate(); + var service8 = GetOrInstantiate(); + action(service1, service2, service3, service4, service5, service6, service7, service8); + return this; + } + + #endregion + } + + + /* public interface IServiceContainer + { + ServiceContainer Register(params object[] parameters); + ServiceContainer Register(params object[] parameters) where TImplementation : TService; + TService Resolve(); + void Get(Action action); + object Instantiate(Type type, params object[] parameters); + + } + public class ServiceContainer : IServiceContainer + { + private readonly Dictionary _dependencies; + public ServiceContainer() + { + _dependencies = new Dictionary + { + [typeof(IServiceContainer)] = this + }; + } + + public void Get(Action action) + { + var service = Resolve(); + action(service); + } + public ServiceContainer Register(params object[] parameters) + { + var instance = Instantiate(typeof(T), parameters); + _dependencies[typeof(T)] = instance; + return this; + } + + public ServiceContainer Register(params object[] parameters) + where TImplementation : TService + { + + _dependencies[typeof(TService)] = Instantiate(typeof(TImplementation), parameters); + return this; + } + + + public TService Resolve() + { + return (TService)_dependencies[typeof(TService)]; + } + + public object Instantiate(Type controllerType, params object[] parameters) + { + var constructors = controllerType.GetConstructors(); // 获取控制器的所有构造函数 + + // 查找具有最多参数的构造函数 + var constructor = constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault(); + + if (constructor != null) + { + if (parameters.Length > 0) + { + return Activator.CreateInstance(controllerType, parameters); + } + else { + var tmpParameters = constructor.GetParameters(); + var dependencyInstances = new List(); + + foreach (var parameter in tmpParameters) + { + var parameterType = parameter.ParameterType; + _dependencies.TryGetValue(parameterType, out var dependencyInstance); + dependencyInstances.Add(dependencyInstance); + if (dependencyInstance == null) + { + return null; + } + } + // 用解析的依赖项实例化目标类型 + return Activator.CreateInstance(controllerType, dependencyInstances.ToArray()); + } + } + else + { + return Activator.CreateInstance(controllerType); + } + } + }*/ + + + +} diff --git a/Library/Utils/TcsSignal.cs b/Library/Utils/TcsSignal.cs new file mode 100644 index 0000000..6355acc --- /dev/null +++ b/Library/Utils/TcsSignal.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Concurrent; + +namespace Serein.Library.Core.NodeFlow.Tool +{ + //public class TcsSignalException : Exception + //{ + // public FlowStateType FsState { get; set; } + // public TcsSignalException(string? message) : base(message) + // { + // FsState = FlowStateType.Error; + // } + //} + + public class TcsSignal where TSignal : struct, Enum + { + //public ConcurrentDictionary>> TcsEvent { get; } = new(); + public ConcurrentDictionary> TcsEvent { get; } = new ConcurrentDictionary>(); + + public ConcurrentDictionary TcsLock { get; } = new ConcurrentDictionary(); + + /// + /// 触发信号 + /// + /// + /// 信号 + /// 传递的参数 + /// 是否成功触发 + public bool TriggerSignal(TSignal signal, T value) + { + var tcsLock = TcsLock.GetOrAdd(signal, new object()); + lock (tcsLock) + { + if (TcsEvent.TryRemove(signal, out var waitTcs)) + { + waitTcs.SetResult(value); + return true; + } + return false; + } + } + + public TaskCompletionSource CreateTcs(TSignal signal) + { + var tcsLock = TcsLock.GetOrAdd(signal, new object()); + lock (tcsLock) + { + var tcs = TcsEvent.GetOrAdd(signal, new TaskCompletionSource()); + return tcs; + } + + } + + public void CancelTask() + { + foreach (var tcs in TcsEvent.Values) + { + tcs.SetException(new Exception("任务取消")); + } + TcsEvent.Clear(); + } + } +} diff --git a/Library/Utils/Utils.cs b/Library/Utils/Utils.cs new file mode 100644 index 0000000..34790b0 --- /dev/null +++ b/Library/Utils/Utils.cs @@ -0,0 +1,9 @@ +using System; +using System.Threading; + +namespace Serein.Library.Utils +{ + public class NodeRunCts : CancellationTokenSource + { + } +} diff --git a/MyDll/SampleCondition.cs b/MyDll/SampleCondition.cs index 9357607..9dd30c8 100644 --- a/MyDll/SampleCondition.cs +++ b/MyDll/SampleCondition.cs @@ -1,30 +1,31 @@ -using Serein.Library.Http; -using Serein.NodeFlow; -using Serein.NodeFlow.Model; -using Serein.NodeFlow.Tool; +using Serein.Library.Api; +using Serein.Library.Enums; +using Serein.Library.Attributes; +using Serein.Library.Core.NodeFlow; +using Serein.Library.Core.NodeFlow.Tool; using static MyDll.PlcDevice; namespace MyDll { # region Web Api 层 - public class ApiController: ControllerBase - { - [AutoInjection] - public required PlcDevice PLCDevice { get; set; } + //public class ApiController: ControllerBase + //{ + // [AutoInjection] + // public required PlcDevice PLCDevice { get; set; } - // example => http://127.0.0.1:8089/api/trigger?type=超宽光电信号&value=网络触发 - [ApiPost] - public dynamic Trigger([IsUrlData] string type, [IsUrlData]string value) - { - if (Enum.TryParse(type, out SignalType result) && Enum.IsDefined(typeof(SignalType), result)) - { - PLCDevice.TriggerSignal(result, value);// 通过 Web Api 模拟外部输入信号 - return new {state = "succeed" }; - } - return new { state = "fail" }; - } + // // example => http://127.0.0.1:8089/api/trigger?type=超宽光电信号&value=网络触发 + // [ApiPost] + // public dynamic Trigger([IsUrlData] string type, [IsUrlData]string value) + // { + // if (Enum.TryParse(type, out SignalType result) && Enum.IsDefined(typeof(SignalType), result)) + // { + // PLCDevice.TriggerSignal(result, value);// 通过 Web Api 模拟外部输入信号 + // return new {state = "succeed" }; + // } + // return new { state = "fail" }; + // } - } + //} #endregion #region 设备层 @@ -69,18 +70,18 @@ namespace MyDll public class LogicControl { [AutoInjection] - public required PlcDevice MyPlc { get; set; } + public PlcDevice MyPlc { get; set; } #region 初始化、初始化完成以及退出的事件 - [MethodDetail(DynamicNodeType.Init)] - public void Init(DynamicContext context) + [NodeAction(NodeType.Init)] + public void Init(IDynamicContext context) { - context.InitService(); + context.SereinIoc.Register(); } - [MethodDetail(DynamicNodeType.Loading)] - public void Loading(DynamicContext context) + [NodeAction(NodeType.Loading)] + public void Loading(IDynamicContext context) { #region 初始化Web Api、Db @@ -124,8 +125,8 @@ namespace MyDll Console.WriteLine("初始化完成"); } - [MethodDetail(DynamicNodeType.Exit)] - public void Exit(DynamicContext context) + [NodeAction(NodeType.Exit)] + public void Exit(IDynamicContext context) { MyPlc.Disconnect(); MyPlc.CancelTask(); @@ -135,8 +136,8 @@ namespace MyDll #region 触发器 - [MethodDetail(DynamicNodeType.Flipflop, "等待信号触发")] - public async Task WaitTask(SignalType triggerType = SignalType.光电1) + [NodeAction(NodeType.Flipflop, "等待信号触发")] + public async Task WaitTask(SignalType triggerType = SignalType.光电1) { /*if (!Enum.TryParse(triggerValue, out SignalType triggerType) && Enum.IsDefined(typeof(SignalType), triggerType)) { @@ -144,50 +145,25 @@ namespace MyDll }*/ try - { - //Console.WriteLine($"{Environment.NewLine}订阅信号 - {triggerValue}"); - + { var tcs = MyPlc.CreateTcs(triggerType); var result = await tcs.Task; - //Interlocked.Increment(ref MyPlc.Count); // 原子自增 - //Console.WriteLine($"信号触发[{triggerType}] : {MyPlc.Count}{Environment.NewLine} thread :{Thread.CurrentThread.ManagedThreadId}{Environment.NewLine}"); return new FlipflopContext(FlowStateType.Succeed, MyPlc.Count); } - catch (TcsSignalException) + catch (Exception ex) { // await Console.Out.WriteLineAsync($"取消等待信号[{triggerType}]"); return new FlipflopContext(FlowStateType.Error); } - } - [MethodDetail(DynamicNodeType.Flipflop, "等待信号触发")] - public async Task WaitTask2(string triggerValue = nameof(SignalType.光电1)) - { - try - { - if (!Enum.TryParse(triggerValue, out SignalType triggerType) && Enum.IsDefined(typeof(SignalType), triggerType)) - { - throw new TcsSignalException("parameter[triggerValue] is not a value in an enumeration"); - } - var tcs = MyPlc.CreateTcs(triggerType); - var result = await tcs.Task; - - Interlocked.Increment(ref MyPlc.Count); // 原子自增 - Console.WriteLine($"信号触发[{triggerType}] : {MyPlc.Count}"); - return new FlipflopContext(FlowStateType.Succeed, MyPlc.Count); - } - catch(TcsSignalException ex) - { - // await Console.Out.WriteLineAsync($"取消等待信号[{triggerValue}]"); - return new FlipflopContext(ex.FsState); - } } + #endregion #region 动作 - [MethodDetail(DynamicNodeType.Action, "初始化")] + [NodeAction(NodeType.Action, "初始化")] public PlcDevice PlcInit(string ip = "192.168.1.1", int port = 6688, string tips = "测试") @@ -197,7 +173,7 @@ namespace MyDll } - [MethodDetail(DynamicNodeType.Action, "自增")] + [NodeAction(NodeType.Action, "自增")] public PlcDevice 自增(int number = 1) { MyPlc.Count += number; @@ -205,9 +181,9 @@ namespace MyDll } - [MethodDetail(DynamicNodeType.Action, "模拟循环触发")] - public void 模拟循环触发(DynamicContext context, - int time = 20, + [NodeAction(NodeType.Action, "模拟循环触发")] + public void 模拟循环触发(IDynamicContext context, + int time = 200, int count = 5, SignalType signal = SignalType.光电1) { @@ -217,24 +193,25 @@ namespace MyDll }; _ = context.CreateTimingTask(action, time, count); } - [MethodDetail(DynamicNodeType.Action, "重置计数")] + [NodeAction(NodeType.Action, "重置计数")] public void 重置计数() { MyPlc.Count = 0; } - [MethodDetail(DynamicNodeType.Action, "触发光电")] + + [NodeAction(NodeType.Action, "触发光电1")] public void 光电1信号触发(int data) { MyPlc.Write($"信号源[光电1] - 模拟写入 : {data}{Environment.NewLine}"); } - [MethodDetail(DynamicNodeType.Action, "触发光电")] + [NodeAction(NodeType.Action, "触发光电2")] public void 光电2信号触发(int data) { MyPlc.Write($"信号源[光电2] - 模拟写入 : {data}{Environment.NewLine}"); } - [MethodDetail(DynamicNodeType.Action, "触发光电")] + [NodeAction(NodeType.Action, "触发光电3")] public void 光电3信号触发(int data) { MyPlc.Write($"信号源[光电3] - 模拟写入 : {data}{Environment.NewLine}"); diff --git a/NodeFlow/DynamicContext.cs b/NodeFlow/DynamicContext.cs index 82d9e94..732e26c 100644 --- a/NodeFlow/DynamicContext.cs +++ b/NodeFlow/DynamicContext.cs @@ -1,4 +1,4 @@ -using Serein.Library.IOC; + using Serein.NodeFlow.Model; using System; using System.Collections.Concurrent; @@ -19,36 +19,97 @@ namespace Serein.NodeFlow // Cancel, // Error, //} + + //public class FlipflopContext + //{ + // public FlowStateType State { get; set; } + // public object? Data { get; set; } + // public FlipflopContext(FlowStateType ffState, object? data = null) + // { + // State = ffState; + // Data = data; + // } + //} + + public static class FlipflopFunc + { + /// + /// 传入触发器方法的返回类型,尝试获取Task[Flipflop[]] 中的泛型类型 + /// + //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]; + + // 检查泛型参数是否为 Flipflop<> + if (innerType == typeof(FlipflopContext)) + //if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>)) + { + return true; + } + } + + return false; + } + } + /// /// 触发器上下文 /// - public class FlipflopContext + public class FlipflopContext// { - public FlowStateType State { get; set; } + public LibraryCore.NodeFlow.FlowStateType State { get; set; } + //public TResult? Data { get; set; } public object? Data { get; set; } - /*public FlipflopContext() + public FlipflopContext(FlowStateType ffState) { - State = FfState.Cancel; - }*/ - public FlipflopContext(FlowStateType ffState, object? data = null) + State = ffState; + } + public FlipflopContext(FlowStateType ffState, object data) { State = ffState; Data = data; } - } + } + /// /// 动态流程上下文 /// - public class DynamicContext(IServiceContainer serviceContainer) + public class DynamicContext(ISereinIoc serviceContainer) { private readonly string contextGuid = "";//System.Guid.NewGuid().ToString(); - public IServiceContainer ServiceContainer { get; } = serviceContainer; + public ISereinIoc ServiceContainer { get; } = serviceContainer; private List InitServices { get; set; } = []; // private ConcurrentDictionary ContextData { get; set; } = []; @@ -152,7 +213,7 @@ namespace Serein.NodeFlow for (int i = 0; i < count; i++) { NodeRunCts.Token.ThrowIfCancellationRequested(); - await time; + await Task.Delay(time); action.Invoke(); } }); diff --git a/NodeFlow/DynamicNodeFrameworkType.cs b/NodeFlow/DynamicNodeFrameworkType.cs new file mode 100644 index 0000000..1d68608 --- /dev/null +++ b/NodeFlow/DynamicNodeFrameworkType.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.NodeFlow +{ + //public enum DynamicNodeType + //{ + // /// + // /// 初始化 + // /// + // Init, + // /// + // /// 开始载入 + // /// + // Loading, + // /// + // /// 结束 + // /// + // Exit, + + // /// + // /// 触发器 + // /// + // Flipflop, + // /// + // /// 条件节点 + // /// + // Condition, + // /// + // /// 动作节点 + // /// + // Action, + //} + +} diff --git a/NodeFlow/FlowStateType.cs b/NodeFlow/FlowStateType.cs new file mode 100644 index 0000000..33debd0 --- /dev/null +++ b/NodeFlow/FlowStateType.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.NodeFlow +{ + //public enum FlowStateType + //{ + // /// + // /// 成功(方法成功执行) + // /// + // Succeed, + // /// + // /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误) + // /// + // Fail, + // /// + // /// 异常(节点没有成功执行,执行时发生非预期的错误) + // /// + // Error, + //} +} diff --git a/NodeFlow/MethodDetails.cs b/NodeFlow/MethodDetails.cs index c73d38b..4c6f0c4 100644 --- a/NodeFlow/MethodDetails.cs +++ b/NodeFlow/MethodDetails.cs @@ -1,5 +1,9 @@ -namespace Serein.NodeFlow +using Serein.Library.Enums; + +namespace Serein.NodeFlow { + + /// /// 显式参数 /// @@ -63,6 +67,10 @@ public class MethodDetails { + /// + /// 拷贝 + /// + /// public MethodDetails Clone() { return new MethodDetails @@ -76,7 +84,7 @@ ReturnType = ReturnType, MethodName = MethodName, MethodLockName = MethodLockName, - + IsNetFramework = IsNetFramework, ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(), }; } @@ -114,7 +122,7 @@ /// /// 节点类型 /// - public DynamicNodeType MethodDynamicType { get; set; } + public NodeType MethodDynamicType { get; set; } /// /// 锁名称 /// @@ -135,98 +143,97 @@ public ExplicitData[] ExplicitDatas { get; set; } - - /// /// 出参类型 /// public Type ReturnType { get; set; } + public bool IsNetFramework { get; set; } - public bool IsCanConnect(Type returnType) - { - if (ExplicitDatas.Length == 0) - { - // 目标不需要传参,可以舍弃结果? - return true; - } - var types = ExplicitDatas.Select(it => it.DataType).ToArray(); - // 检查返回类型是否是元组类型 - if (returnType.IsGenericType && IsValueTuple(returnType)) - { + //public bool IsCanConnect(Type returnType) + //{ + // if (ExplicitDatas.Length == 0) + // { + // // 目标不需要传参,可以舍弃结果? + // return true; + // } + // var types = ExplicitDatas.Select(it => it.DataType).ToArray(); + // // 检查返回类型是否是元组类型 + // if (returnType.IsGenericType && IsValueTuple(returnType)) + // { - return CompareGenericArguments(returnType, types); - } - else - { - int index = 0; - if (types[index] == typeof(DynamicContext)) - { - index++; - if (types.Length == 1) - { - return true; - } - } - // 被连接节点检查自己需要的参数类型,与发起连接的节点比较返回值类型 - if (returnType == types[index]) - { - return true; - } - } - return false; - } + // return CompareGenericArguments(returnType, types); + // } + // else + // { + // int index = 0; + // if (types[index] == typeof(DynamicContext)) + // { + // index++; + // if (types.Length == 1) + // { + // return true; + // } + // } + // // 被连接节点检查自己需要的参数类型,与发起连接的节点比较返回值类型 + // if (returnType == types[index]) + // { + // return true; + // } + // } + // return false; + //} - /// - /// 检查元组类型 - /// - /// - /// - private bool IsValueTuple(Type type) - { - if (!type.IsGenericType) return false; + ///// + ///// 检查元组类型 + ///// + ///// + ///// + //private bool IsValueTuple(Type type) + //{ + // if (!type.IsGenericType) return false; - var genericTypeDef = type.GetGenericTypeDefinition(); - return genericTypeDef == typeof(ValueTuple<>) || - genericTypeDef == typeof(ValueTuple<,>) || - genericTypeDef == typeof(ValueTuple<,,>) || - genericTypeDef == typeof(ValueTuple<,,,>) || - genericTypeDef == typeof(ValueTuple<,,,,>) || - genericTypeDef == typeof(ValueTuple<,,,,,>) || - genericTypeDef == typeof(ValueTuple<,,,,,,>) || - genericTypeDef == typeof(ValueTuple<,,,,,,,>); - } + // var genericTypeDef = type.GetGenericTypeDefinition(); + // return genericTypeDef == typeof(ValueTuple<>) || + // genericTypeDef == typeof(ValueTuple<,>) || + // genericTypeDef == typeof(ValueTuple<,,>) || + // genericTypeDef == typeof(ValueTuple<,,,>) || + // genericTypeDef == typeof(ValueTuple<,,,,>) || + // genericTypeDef == typeof(ValueTuple<,,,,,>) || + // genericTypeDef == typeof(ValueTuple<,,,,,,>) || + // genericTypeDef == typeof(ValueTuple<,,,,,,,>); + //} - private bool CompareGenericArguments(Type returnType, Type[] parameterTypes) - { - var genericArguments = returnType.GetGenericArguments(); - var length = parameterTypes.Length; + //private bool CompareGenericArguments(Type returnType, Type[] parameterTypes) + //{ + // var genericArguments = returnType.GetGenericArguments(); + // var length = parameterTypes.Length; - for (int i = 0; i < genericArguments.Length; i++) - { - if (i >= length) return false; + // for (int i = 0; i < genericArguments.Length; i++) + // { + // if (i >= length) return false; - if (IsValueTuple(genericArguments[i])) - { - // 如果当前参数也是 ValueTuple,递归检查嵌套的泛型参数 - if (!CompareGenericArguments(genericArguments[i], parameterTypes.Skip(i).ToArray())) - { - return false; - } - } - else if (genericArguments[i] != parameterTypes[i]) - { - return false; - } - } + // if (IsValueTuple(genericArguments[i])) + // { + // // 如果当前参数也是 ValueTuple,递归检查嵌套的泛型参数 + // if (!CompareGenericArguments(genericArguments[i], parameterTypes.Skip(i).ToArray())) + // { + // return false; + // } + // } + // else if (genericArguments[i] != parameterTypes[i]) + // { + // return false; + // } + // } - return true; - } + // return true; + //} } diff --git a/NodeFlow/Model/CompositeConditionNode.cs b/NodeFlow/Model/CompositeConditionNode.cs index 7d2119d..8ad69c6 100644 --- a/NodeFlow/Model/CompositeConditionNode.cs +++ b/NodeFlow/Model/CompositeConditionNode.cs @@ -1,4 +1,8 @@ -namespace Serein.NodeFlow.Model +using Serein.Library.Api; +using Serein.Library.Enums; +using Serein.Library.Core.NodeFlow; + +namespace Serein.NodeFlow.Model { /// /// 组合条件节点(用于条件区域) @@ -19,7 +23,7 @@ /// /// /// - public override object? Execute(DynamicContext context) + public override object? Execute(IDynamicContext context) { // bool allTrue = ConditionNodes.All(condition => Judge(context,condition.MethodDetails)); // bool IsAllTrue = true; // 初始化为 true @@ -50,7 +54,7 @@ // } //} } - private FlowStateType Judge(DynamicContext context, SingleConditionNode node) + private FlowStateType Judge(IDynamicContext context, SingleConditionNode node) { try { diff --git a/NodeFlow/Model/NodeBase.cs b/NodeFlow/Model/NodeBase.cs index 402dfaf..499a0d0 100644 --- a/NodeFlow/Model/NodeBase.cs +++ b/NodeFlow/Model/NodeBase.cs @@ -1,6 +1,9 @@ using Newtonsoft.Json; -using Serein.NodeFlow; +using Serein.Library.Api; +using Serein.Library.Enums; +using Serein.Library.Core.NodeFlow; using Serein.NodeFlow.Tool; +using Serein.NodeFlow.Tool.SerinExpression; namespace Serein.NodeFlow.Model { @@ -25,21 +28,6 @@ namespace Serein.NodeFlow.Model Upstream, } - public enum FlowStateType - { - /// - /// 成功(方法成功执行) - /// - Succeed, - /// - /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误) - /// - Fail, - /// - /// 异常(节点没有 - /// - Error, - } /// /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域 @@ -103,7 +91,7 @@ namespace Serein.NodeFlow.Model /// /// 流程上下文 /// 节点传回数据对象 - public virtual object? Execute(DynamicContext context) + public virtual object? Execute(IDynamicContext context) { MethodDetails md = MethodDetails; object? result = null; @@ -155,7 +143,7 @@ namespace Serein.NodeFlow.Model /// /// 节点传回数据对象 /// - public virtual async Task ExecuteAsync(DynamicContext context) + public virtual async Task ExecuteAsync(IDynamicContext context) { MethodDetails md = MethodDetails; object? result = null; @@ -165,18 +153,18 @@ namespace Serein.NodeFlow.Model return result; } - FlipflopContext flipflopContext = null; + IFlipflopContext flipflopContext = null; try { // 调用委托并获取结果 if (md.ExplicitDatas.Length == 0) { - flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance); + flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance); } else { object?[]? parameters = GetParameters(context, MethodDetails); - flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance, parameters); + flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance, parameters); } if (flipflopContext != null) @@ -186,6 +174,10 @@ namespace Serein.NodeFlow.Model { result = flipflopContext.Data; } + else + { + result = null; + } } } catch (Exception ex) @@ -202,9 +194,9 @@ namespace Serein.NodeFlow.Model /// /// /// - public async Task StartExecution(DynamicContext context) + public async Task StartExecution(IDynamicContext context) { - var cts = context.ServiceContainer.GetOrInstantiate(); + var cts = context.SereinIoc.GetOrInstantiate(); Stack stack = []; stack.Push(this); @@ -217,7 +209,7 @@ namespace Serein.NodeFlow.Model // 设置方法执行的对象 if (currentNode.MethodDetails != null) { - currentNode.MethodDetails.ActingInstance ??= context.ServiceContainer.GetOrInstantiate(MethodDetails.ActingInstanceType); + currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrInstantiate(MethodDetails.ActingInstanceType); } // 获取上游分支,首先执行一次 @@ -228,7 +220,7 @@ namespace Serein.NodeFlow.Model await upstreamNodes[i].StartExecution(context); } - if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == DynamicNodeType.Flipflop) + if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == NodeType.Flipflop) { // 触发器节点 currentNode.FlowData = await currentNode.ExecuteAsync(context); @@ -259,7 +251,7 @@ namespace Serein.NodeFlow.Model /// /// 获取对应的参数数组 /// - public object[]? GetParameters(DynamicContext context, MethodDetails md) + public object[]? GetParameters(IDynamicContext context, MethodDetails md) { // 用正确的大小初始化参数数组 var types = md.ExplicitDatas.Select(it => it.DataType).ToArray(); @@ -278,7 +270,7 @@ namespace Serein.NodeFlow.Model var f1 = PreviousNode?.FlowData?.GetType(); var f2 = mdEd.DataType; - if (type == typeof(DynamicContext)) + if (type == typeof(IDynamicContext)) { parameters[i] = context; } @@ -292,37 +284,79 @@ namespace Serein.NodeFlow.Model } else if (mdEd.IsExplicitData) // 显式参数 { - if (mdEd.DataType.IsEnum) + // 判断是否使用表达式解析 + if (mdEd.DataValue[0] == '@') { - var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue); - parameters[i] = enumValue; - } - else if (mdEd.ExplicitType == typeof(string)) - { - parameters[i] = mdEd.DataValue; - } - else if (mdEd.ExplicitType == typeof(bool)) - { - parameters[i] = bool.Parse(mdEd.DataValue); - } - else if (mdEd.ExplicitType == typeof(int)) - { - parameters[i] = int.Parse(mdEd.DataValue); - } - else if (mdEd.ExplicitType == typeof(double)) - { - parameters[i] = double.Parse(mdEd.DataValue); + var expResult = SerinExpressionEvaluator.Evaluate(mdEd.DataValue, PreviousNode?.FlowData, out bool isChange); + + + if (mdEd.DataType.IsEnum) + { + var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue); + parameters[i] = enumValue; + } + else if (mdEd.ExplicitType == typeof(string)) + { + parameters[i] = Convert.ChangeType(expResult, typeof(string)); + } + else if (mdEd.ExplicitType == typeof(bool)) + { + parameters[i] = Convert.ChangeType(expResult, typeof(bool)); + } + else if (mdEd.ExplicitType == typeof(int)) + { + parameters[i] = Convert.ChangeType(expResult, typeof(int)); + } + else if (mdEd.ExplicitType == typeof(double)) + { + parameters[i] = Convert.ChangeType(expResult, typeof(double)); + } + else + { + parameters[i] = expResult; + //parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType); + } } else { - parameters[i] = ""; + if (mdEd.DataType.IsEnum) + { + var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue); + parameters[i] = enumValue; + } + else if (mdEd.ExplicitType == typeof(string)) + { + parameters[i] = mdEd.DataValue; + } + else if (mdEd.ExplicitType == typeof(bool)) + { + parameters[i] = bool.Parse(mdEd.DataValue); + } + else if (mdEd.ExplicitType == typeof(int)) + { + parameters[i] = int.Parse(mdEd.DataValue); + } + else if (mdEd.ExplicitType == typeof(double)) + { + parameters[i] = double.Parse(mdEd.DataValue); + } + else + { + parameters[i] = ""; - //parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType); + //parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType); + } } + + } - else if (f1 != null && f2 != null && f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName)) + else if (f1 != null && f2 != null) { - parameters[i] = PreviousNode?.FlowData; + if(f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName)) + { + parameters[i] = PreviousNode?.FlowData; + + } } else { diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs index 8a23224..9e86fe1 100644 --- a/NodeFlow/Model/SingleConditionNode.cs +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -1,5 +1,8 @@  -using Serein.Library.SerinExpression; +using Serein.Library.Api; +using Serein.Library.Enums; +using Serein.Library.Core.NodeFlow; +using Serein.NodeFlow.Tool.SerinExpression; namespace Serein.NodeFlow.Model { @@ -24,7 +27,7 @@ namespace Serein.NodeFlow.Model public string Expression { get; set; } - public override object? Execute(DynamicContext context) + public override object? Execute(IDynamicContext context) { // 接收上一节点参数or自定义参数内容 object? result; diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/SingleExpOpNode.cs index df3e679..38f13ad 100644 --- a/NodeFlow/Model/SingleExpOpNode.cs +++ b/NodeFlow/Model/SingleExpOpNode.cs @@ -1,10 +1,7 @@ -using Serein.Library.SerinExpression; -using Serein.NodeFlow; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Serein.Library.Api; +using Serein.Library.Enums; +using Serein.Library.Core.NodeFlow; +using Serein.NodeFlow.Tool.SerinExpression; namespace Serein.NodeFlow.Model { @@ -19,7 +16,7 @@ namespace Serein.NodeFlow.Model public string Expression { get; set; } - public override object? Execute(DynamicContext context) + public override object? Execute(IDynamicContext context) { var data = PreviousNode?.FlowData; diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs index 892cc12..ae026e7 100644 --- a/NodeFlow/Model/SingleFlipflopNode.cs +++ b/NodeFlow/Model/SingleFlipflopNode.cs @@ -1,11 +1,12 @@ -using Serein.NodeFlow; +using Serein.Library.Api; +using Serein.Library.Core.NodeFlow; namespace Serein.NodeFlow.Model { public class SingleFlipflopNode : NodeBase { - public override object Execute(DynamicContext context) + public override object Execute(IDynamicContext context) { throw new NotImplementedException("无法以非await/async的形式调用触发器"); } diff --git a/NodeFlow/NodeFlowStarter.cs b/NodeFlow/NodeFlowStarter.cs index f4df7a9..3be8f4c 100644 --- a/NodeFlow/NodeFlowStarter.cs +++ b/NodeFlow/NodeFlowStarter.cs @@ -1,9 +1,8 @@ -using Serein.Library.Http; -using Serein.Library.IOC; -using Serein.NodeFlow; +using Serein.Library.Api; +using Serein.Library.Enums; +using Serein.Library.Core.NodeFlow; using Serein.NodeFlow.Model; using Serein.NodeFlow.Tool; -using SqlSugar; namespace Serein.NodeFlow { @@ -14,16 +13,16 @@ namespace Serein.NodeFlow } - public class NodeFlowStarter(IServiceContainer serviceContainer, List methodDetails) + public class NodeFlowStarter(ISereinIoc serviceContainer, List methodDetails) { - private readonly IServiceContainer ServiceContainer = serviceContainer; + private readonly ISereinIoc ServiceContainer = serviceContainer; private readonly List methodDetails = methodDetails; private Action ExitAction = null; - private DynamicContext context = null; + private IDynamicContext context = null; public NodeRunTcs MainCts; @@ -42,19 +41,26 @@ namespace Serein.NodeFlow { var startNode = nodes.FirstOrDefault(p => p.IsStart); if (startNode == null) { return; } - context = new(ServiceContainer); + if (false) + { + context = new Serein.Library.Core.NodeFlow.DynamicContext(ServiceContainer); + } + else + { + context = new Serein.Library.Framework.NodeFlow.DynamicContext(ServiceContainer); + } MainCts = ServiceContainer.CreateServiceInstance(); - var initMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Init).ToList(); - var loadingMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Loading).ToList(); - var exitMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Exit).ToList(); + var initMethods = methodDetails.Where(it => it.MethodDynamicType == NodeType.Init).ToList(); + var loadingMethods = methodDetails.Where(it => it.MethodDynamicType == NodeType.Loading).ToList(); + var exitMethods = methodDetails.Where(it => it.MethodDynamicType == NodeType.Exit).ToList(); ExitAction = () => { - ServiceContainer.Run((web) => - { - web?.Stop(); - }); + //ServiceContainer.Run((web) => + //{ + // web?.Stop(); + //}); foreach (MethodDetails? md in exitMethods) { object?[]? args = [context]; @@ -77,7 +83,7 @@ namespace Serein.NodeFlow object?[]? data = [md.ActingInstance, args]; md.MethodDelegate.DynamicInvoke(data); } - context.Biuld(); + context.SereinIoc.Build(); foreach (var md in loadingMethods) // 加载 { @@ -87,7 +93,7 @@ namespace Serein.NodeFlow md.MethodDelegate.DynamicInvoke(data); } - var flipflopNodes = nodes.Where(it => it.MethodDetails?.MethodDynamicType == DynamicNodeType.Flipflop + var flipflopNodes = nodes.Where(it => it.MethodDetails?.MethodDynamicType == NodeType.Flipflop && it.PreviousNodes.Count == 0 && it.IsStart != true).ToArray(); @@ -126,15 +132,17 @@ namespace Serein.NodeFlow { return; } + - var func = md.ExplicitDatas.Length == 0 ? (Func>)del : (Func>)del; + //var func = md.ExplicitDatas.Length == 0 ? (Func>>)del : (Func>>)del; + var func = md.ExplicitDatas.Length == 0 ? (Func>)del : (Func>)del; while (!MainCts.IsCancellationRequested) // 循环中直到栈为空才会退出 { object?[]? parameters = singleFlipFlopNode.GetParameters(context, md); // 调用委托并获取结果 - FlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters); + IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters); if (flipflopContext == null) { diff --git a/NodeFlow/Serein - Backup.NodeFlow.csproj b/NodeFlow/Serein - Backup.NodeFlow.csproj new file mode 100644 index 0000000..5826486 --- /dev/null +++ b/NodeFlow/Serein - Backup.NodeFlow.csproj @@ -0,0 +1,36 @@ + + + + net8.0-windows7.0 + enable + enable + D:\Project\C#\DynamicControl\SereinFlow\.Output + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 8a2939e..ff1efaa 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -17,6 +17,19 @@ + + + + + + + + + + + + + diff --git a/NodeFlow/Attribute.cs b/NodeFlow/Tool/Attribute.cs similarity index 98% rename from NodeFlow/Attribute.cs rename to NodeFlow/Tool/Attribute.cs index ddccad4..e6b656f 100644 --- a/NodeFlow/Attribute.cs +++ b/NodeFlow/Tool/Attribute.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Serein.NodeFlow +namespace Serein.NodeFlow.Tool { public enum DynamicNodeType diff --git a/NodeFlow/Tool/DelegateGenerator.cs b/NodeFlow/Tool/DelegateGenerator.cs index cc49738..e4e9d3c 100644 --- a/NodeFlow/Tool/DelegateGenerator.cs +++ b/NodeFlow/Tool/DelegateGenerator.cs @@ -1,5 +1,6 @@ -using Serein.Library.IOC; -using Serein.NodeFlow; +using Serein.Library.Api; +using Serein.Library.Attributes; +using Serein.Library.Core.NodeFlow; using System.Collections.Concurrent; using System.Reflection; @@ -27,16 +28,16 @@ public static class DelegateGenerator /// /// /// - public static ConcurrentDictionary GenerateMethodDetails(IServiceContainer serviceContainer, Type type) + public static ConcurrentDictionary GenerateMethodDetails(ISereinIoc serviceContainer, Type type, bool isNetFramework) { var methodDetailsDictionary = new ConcurrentDictionary(); var assemblyName = type.Assembly.GetName().Name; - var methods = GetMethodsToProcess(type); + var methods = GetMethodsToProcess(type, isNetFramework); foreach (var method in methods) { - var methodDetails = CreateMethodDetails(serviceContainer, type, method, assemblyName); + var methodDetails = CreateMethodDetails(serviceContainer, type, method, assemblyName, isNetFramework); methodDetailsDictionary.TryAdd(methodDetails.MethodName, methodDetails); } @@ -47,48 +48,56 @@ public static class DelegateGenerator /// /// 获取处理方法 /// - private static IEnumerable GetMethodsToProcess(Type type) + private static IEnumerable GetMethodsToProcess(Type type, bool isNetFramework) { - return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where(m => m.GetCustomAttribute()?.Scan == true); + if (isNetFramework) + { + + return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(m => m.GetCustomAttribute()?.Scan == true); + } + else + { + + return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(m => m.GetCustomAttribute()?.Scan == true); + } } /// /// 创建方法信息 /// /// - private static MethodDetails CreateMethodDetails(IServiceContainer serviceContainer, Type type, MethodInfo method, string assemblyName) + private static MethodDetails CreateMethodDetails(ISereinIoc serviceContainer, Type type, MethodInfo method, string assemblyName, bool isNetFramework) { - var methodName = method.Name; - var attribute = method.GetCustomAttribute(); - - var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters()); - // 生成委托 - var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型 - method, // 方法信息 - method.GetParameters(),// 方法参数 - method.ReturnType);// 返回值 + + var methodName = method.Name; + var attribute = method.GetCustomAttribute(); + var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters()); + // 生成委托 + var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型 + method, // 方法信息 + method.GetParameters(),// 方法参数 + method.ReturnType);// 返回值 - var dllTypeName = $"{assemblyName}.{type.Name}"; - serviceContainer.Register(type); - object instance = serviceContainer.GetOrCreateServiceInstance(type); - var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}"; - - - - return new MethodDetails - { - ActingInstanceType = type, - ActingInstance = instance, - MethodName = dllTypeMethodName, - MethodDelegate = methodDelegate, - MethodDynamicType = attribute.MethodDynamicType, - MethodLockName = attribute.LockName, - MethodTips = attribute.MethodTips, - ExplicitDatas = explicitDataOfParameters, - ReturnType = method.ReturnType, - }; + var dllTypeName = $"{assemblyName}.{type.Name}"; + serviceContainer.Register(type); + object instance = serviceContainer.GetOrCreateServiceInstance(type); + var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}"; + return new MethodDetails + { + ActingInstanceType = type, + ActingInstance = instance, + MethodName = dllTypeMethodName, + MethodDelegate = methodDelegate, + MethodDynamicType = attribute.MethodDynamicType, + MethodLockName = attribute.LockName, + MethodTips = attribute.MethodTips, + ExplicitDatas = explicitDataOfParameters, + ReturnType = method.ReturnType, + }; + } private static ExplicitData[] GetExplicitDataOfParameters(ParameterInfo[] parameters) @@ -162,7 +171,8 @@ public static class DelegateGenerator return ExpressionHelper.MethodCaller(type, methodInfo, parameterTypes); } } - else if (returnType == typeof(Task)) // 触发器 + // else if (returnType == typeof(Task>>( - Expression.Convert(methodCall, typeof(Task)), - instanceParam, - argsParam - ); + // 创建 lambda 表达式 + var lambda = Expression.Lambda>>( + Expression.Convert(methodCall, typeof(Task)), + instanceParam, + argsParam + ); + //获取返回类型 + //var returnType = methodInfo.ReturnType; + //var lambda = Expression.Lambda( + // typeof(Func<,,>).MakeGenericType(typeof(object), typeof(object[]), returnType), + // Expression.Convert(methodCall, returnType), + // instanceParam, + // argsParam + // ); + + //var resule = task.DynamicInvoke((object)[Activator.CreateInstance(type), [new DynamicContext(null)]]); return lambda.Compile(); } diff --git a/NodeFlow/Tool/SerinExpression/ConditionResolver.cs b/NodeFlow/Tool/SerinExpression/ConditionResolver.cs new file mode 100644 index 0000000..d46cba4 --- /dev/null +++ b/NodeFlow/Tool/SerinExpression/ConditionResolver.cs @@ -0,0 +1,337 @@ +using System.Reflection; + +namespace Serein.NodeFlow.Tool.SerinExpression +{ + /// + /// 条件解析抽象类 + /// + public abstract class ConditionResolver + { + public abstract bool Evaluate(object obj); + } + + public class PassConditionResolver : ConditionResolver + { + public Operator Op { get; set; } + public override bool Evaluate(object obj) + { + return Op switch + { + Operator.Pass => true, + Operator.NotPass => false, + _ => throw new NotSupportedException("不支持的条件类型") + }; + } + + public enum Operator + { + Pass, + NotPass, + } + + } + + public class ValueTypeConditionResolver : ConditionResolver where T : struct, IComparable + { + public enum Operator + { + /// + /// 不进行任何操作 + /// + Node, + /// + /// 大于 + /// + GreaterThan, + /// + /// 小于 + /// + LessThan, + /// + /// 等于 + /// + Equal, + /// + /// 大于或等于 + /// + GreaterThanOrEqual, + /// + /// 小于或等于 + /// + LessThanOrEqual, + /// + /// 在两者之间 + /// + InRange, + /// + /// 不在两者之间 + /// + OutOfRange + } + + public Operator Op { get; set; } + public T Value { get; set; } + public T RangeStart { get; set; } + public T RangeEnd { get; set; } + + public string ArithmeticExpression { get; set; } + + + public override bool Evaluate(object obj) + { + if (obj is T typedObj) + { + double numericValue = Convert.ToDouble(typedObj); + if (!string.IsNullOrEmpty(ArithmeticExpression)) + { + numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue); + } + + T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T)); + + return Op switch + { + Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0, + Operator.LessThan => evaluatedValue.CompareTo(Value) < 0, + Operator.Equal => evaluatedValue.CompareTo(Value) == 0, + Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0, + Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0, + Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0, + Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0, + _ => throw new NotSupportedException("不支持的条件类型") + }; + /* switch (Op) + { + case Operator.GreaterThan: + return evaluatedValue.CompareTo(Value) > 0; + case Operator.LessThan: + return evaluatedValue.CompareTo(Value) < 0; + case Operator.Equal: + return evaluatedValue.CompareTo(Value) == 0; + case Operator.GreaterThanOrEqual: + return evaluatedValue.CompareTo(Value) >= 0; + case Operator.LessThanOrEqual: + return evaluatedValue.CompareTo(Value) <= 0; + case Operator.InRange: + return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0; + case Operator.OutOfRange: + return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0; + }*/ + } + return false; + } + } + + public class BoolConditionResolver : ConditionResolver + { + public enum Operator + { + /// + /// 是 + /// + Is + } + + public Operator Op { get; set; } + public bool Value { get; set; } + + public override bool Evaluate(object obj) + { + + if (obj is bool boolObj) + { + return boolObj == Value; + /*switch (Op) + { + case Operator.Is: + return boolObj == Value; + }*/ + } + return false; + } + } + + public class StringConditionResolver : ConditionResolver + { + public enum Operator + { + /// + /// 出现过 + /// + Contains, + /// + /// 没有出现过 + /// + DoesNotContain, + /// + /// 相等 + /// + Equal, + /// + /// 不相等 + /// + NotEqual, + /// + /// 起始字符串等于 + /// + StartsWith, + /// + /// 结束字符串等于 + /// + EndsWith + } + + public Operator Op { get; set; } + + public string Value { get; set; } + + + public override bool Evaluate(object obj) + { + if (obj is string strObj) + { + return Op switch + { + Operator.Contains => strObj.Contains(Value), + Operator.DoesNotContain => !strObj.Contains(Value), + Operator.Equal => strObj == Value, + Operator.NotEqual => strObj != Value, + Operator.StartsWith => strObj.StartsWith(Value), + Operator.EndsWith => strObj.EndsWith(Value), + _ => throw new NotSupportedException("不支持的条件类型"), + }; + + /* switch (Op) + { + case Operator.Contains: + return strObj.Contains(Value); + case Operator.DoesNotContain: + return !strObj.Contains(Value); + case Operator.Equal: + return strObj == Value; + case Operator.NotEqual: + return strObj != Value; + case Operator.StartsWith: + return strObj.StartsWith(Value); + case Operator.EndsWith: + return strObj.EndsWith(Value); + }*/ + } + return false; + } + } + public class MemberConditionResolver : ConditionResolver where T : struct, IComparable + { + //public string MemberPath { get; set; } + public ValueTypeConditionResolver.Operator Op { get; set; } + public object? TargetObj { get; set; } + public T Value { get; set; } + + public string ArithmeticExpression { get; set; } + + public override bool Evaluate(object? obj) + { + //object? memberValue = GetMemberValue(obj, MemberPath); + if (TargetObj is T typedObj) + { + return new ValueTypeConditionResolver + { + Op = Op, + Value = Value, + ArithmeticExpression = ArithmeticExpression, + }.Evaluate(typedObj); + } + return false; + } + + //private object? GetMemberValue(object? obj, string memberPath) + //{ + // string[] members = memberPath[1..].Split('.'); + // foreach (var member in members) + // { + // if (obj == null) return null; + // Type type = obj.GetType(); + // PropertyInfo? propertyInfo = type.GetProperty(member); + // FieldInfo? fieldInfo = type.GetField(member); + // if (propertyInfo != null) + // obj = propertyInfo.GetValue(obj); + // else if (fieldInfo != null) + // obj = fieldInfo.GetValue(obj); + // else + // throw new ArgumentException($"Member {member} not found in type {type.FullName}"); + // } + // return obj; + //} + } + + public class MemberStringConditionResolver : ConditionResolver + { + + public string MemberPath { get; set; } + + public StringConditionResolver.Operator Op { get; set; } + + public string Value { get; set; } + + + public override bool Evaluate(object obj) + { + object memberValue = GetMemberValue(obj, MemberPath); + if (memberValue is string strObj) + { + return new StringConditionResolver + { + Op = Op, + Value = Value + }.Evaluate(strObj); + } + return false; + } + + private object GetMemberValue(object? obj, string memberPath) + { + string[] members = memberPath[1..].Split('.'); + foreach (var member in members) + { + + if (obj == null) return null; + + Type type = obj.GetType(); + PropertyInfo? propertyInfo = type.GetProperty(member); + FieldInfo? fieldInfo = type.GetField(member); + if (propertyInfo != null) + obj = propertyInfo.GetValue(obj); + else if (fieldInfo != null) + obj = fieldInfo.GetValue(obj); + else + throw new ArgumentException($"Member {member} not found in type {type.FullName}"); + } + + return obj; + + } + + + + + + private static string GetArithmeticExpression(string part) + { + int startIndex = part.IndexOf('['); + int endIndex = part.IndexOf(']'); + if (startIndex >= 0 && endIndex > startIndex) + { + return part.Substring(startIndex + 1, endIndex - startIndex - 1); + } + + return null; + + } + + + + + + } + +} diff --git a/NodeFlow/Tool/SerinExpression/SerinConditionParser.cs b/NodeFlow/Tool/SerinExpression/SerinConditionParser.cs new file mode 100644 index 0000000..5d4a3bc --- /dev/null +++ b/NodeFlow/Tool/SerinExpression/SerinConditionParser.cs @@ -0,0 +1,341 @@ +using System.Globalization; +using System.Reflection; + +namespace Serein.NodeFlow.Tool.SerinExpression +{ + + public class SerinConditionParser + { + public static bool To(T data, string expression) + { + try + { + + return ConditionParse(data, expression).Evaluate(data); + + } + catch (Exception ex) + { + Console.WriteLine(ex); + throw; + } + } + + public static ConditionResolver ConditionParse(object data, string expression) + { + if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值 + { + return ParseObjectExpression(data, expression); + } + else + { + return ParseSimpleExpression(data, expression); + } + + + //bool ContainsArithmeticOperators(string expression) + //{ + // return expression.Contains('+') || expression.Contains('-') || expression.Contains('*') || expression.Contains('/'); + //} + + } + + /// + /// 获取计算表达式的部分 + /// + /// + /// + private static string GetArithmeticExpression(string part) + { + int startIndex = part.IndexOf('['); + int endIndex = part.IndexOf(']'); + if (startIndex >= 0 && endIndex > startIndex) + { + return part.Substring(startIndex + 1, endIndex - startIndex - 1); + } + + return null; + + } + /// + /// 获取对象指定名称的成员 + /// + private static object? GetMemberValue(object? obj, string memberPath) + { + string[] members = memberPath[1..].Split('.'); + foreach (var member in members) + { + if (obj == null) return null; + Type type = obj.GetType(); + PropertyInfo? propertyInfo = type.GetProperty(member); + FieldInfo? fieldInfo = type.GetField(member); + if (propertyInfo != null) + obj = propertyInfo.GetValue(obj); + else if (fieldInfo != null) + obj = fieldInfo.GetValue(obj); + else + throw new ArgumentException($"Member {member} not found in type {type.FullName}"); + } + return obj; + } + /// + /// 解析对象表达式 + /// + private static ConditionResolver ParseObjectExpression(object data, string expression) + { + var parts = expression.Split(' '); + string operatorStr = parts[0]; + string valueStr = string.Join(' ', parts, 1, parts.Length - 1); + + int typeStartIndex = expression.IndexOf('<'); + int typeEndIndex = expression.IndexOf('>'); + + string memberPath; + Type type; + object? targetObj; + if (typeStartIndex + typeStartIndex == -2) + { + memberPath = operatorStr; + targetObj = GetMemberValue(data, operatorStr); + + type = targetObj.GetType(); + + operatorStr = parts[1].ToLower(); + valueStr = string.Join(' ', parts.Skip(2)); + } + else + { + if (typeStartIndex >= typeEndIndex) + { + throw new ArgumentException("无效的表达式格式"); + } + memberPath = expression.Substring(0, typeStartIndex).Trim(); + string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1).Trim().ToLower(); + parts = expression.Substring(typeEndIndex + 1).Trim().Split(' '); + if (parts.Length == 3) + { + operatorStr = parts[1].ToLower(); + valueStr = string.Join(' ', parts.Skip(2)); + } + else + { + operatorStr = parts[0].ToLower(); + valueStr = string.Join(' ', parts.Skip(1)); + } + targetObj = GetMemberValue(data, memberPath); + + Type? tempType = typeStr switch + { + "int" => typeof(int), + "double" => typeof(double), + "bool" => typeof(bool), + "string" => typeof(string), + _ => Type.GetType(typeStr) + }; + type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明"); + } + + + + if (type == typeof(int)) + { + int value = int.Parse(valueStr, CultureInfo.InvariantCulture); + return new MemberConditionResolver + { + TargetObj = targetObj, + //MemberPath = memberPath, + Op = ParseValueTypeOperator(operatorStr), + Value = value, + ArithmeticExpression = GetArithmeticExpression(parts[0]) + }; + } + else if (type == typeof(double)) + { + double value = double.Parse(valueStr, CultureInfo.InvariantCulture); + return new MemberConditionResolver + { + //MemberPath = memberPath, + TargetObj = targetObj, + Op = ParseValueTypeOperator(operatorStr), + Value = value, + ArithmeticExpression = GetArithmeticExpression(parts[0]) + }; + + } + else if (type == typeof(bool)) + { + return new MemberConditionResolver + { + //MemberPath = memberPath, + TargetObj = targetObj, + Op = (ValueTypeConditionResolver.Operator)ParseBoolOperator(operatorStr) + }; + } + else if (type == typeof(string)) + { + return new MemberStringConditionResolver + { + MemberPath = memberPath, + Op = ParseStringOperator(operatorStr), + Value = valueStr + }; + } + + throw new NotSupportedException($"Type {type} is not supported."); + } + + private static ConditionResolver ParseSimpleExpression(object data, string expression) + { + if ("pass".Equals(expression.ToLower())) + { + return new PassConditionResolver + { + Op = PassConditionResolver.Operator.Pass, + }; + } + else + { + if ("not pass".Equals(expression.ToLower())) + { + return new PassConditionResolver + { + Op = PassConditionResolver.Operator.NotPass, + }; + } + if ("!pass".Equals(expression.ToLower())) + { + return new PassConditionResolver + { + Op = PassConditionResolver.Operator.NotPass, + }; + } + } + + + var parts = expression.Split(' '); + + if (parts.Length < 2) + throw new ArgumentException("无效的表达式格式。"); + + //string typeStr = parts[0]; + string operatorStr = parts[0]; + string valueStr = string.Join(' ', parts, 1, parts.Length - 1); + + Type type = data.GetType();//Type.GetType(typeStr); + if (type == typeof(int)) + { + var op = ParseValueTypeOperator(operatorStr); + if (op == ValueTypeConditionResolver.Operator.InRange || op == ValueTypeConditionResolver.Operator.OutOfRange) + { + var temp = valueStr.Split('-'); + if (temp.Length < 2) + throw new ArgumentException($"范围无效:{valueStr}。"); + int rangeStart = int.Parse(temp[0], CultureInfo.InvariantCulture); + int rangeEnd = int.Parse(temp[1], CultureInfo.InvariantCulture); + return new ValueTypeConditionResolver + { + Op = op, + RangeStart = rangeStart, + RangeEnd = rangeEnd, + ArithmeticExpression = GetArithmeticExpression(parts[0]), + }; + } + else + { + int value = int.Parse(valueStr, CultureInfo.InvariantCulture); + return new ValueTypeConditionResolver + { + Op = op, + Value = value, + ArithmeticExpression = GetArithmeticExpression(parts[0]) + }; + + } + + } + else if (type == typeof(double)) + { + double value = double.Parse(valueStr, CultureInfo.InvariantCulture); + return new ValueTypeConditionResolver + { + Op = ParseValueTypeOperator(operatorStr), + Value = value, + ArithmeticExpression = GetArithmeticExpression(parts[0]) + }; + } + else if (type == typeof(bool)) + { + bool value = bool.Parse(valueStr); + return new BoolConditionResolver + { + Op = ParseBoolOperator(operatorStr), + Value = value, + }; + } + else if (type == typeof(string)) + { + return new StringConditionResolver + { + Op = ParseStringOperator(operatorStr), + Value = valueStr + }; + } + + throw new NotSupportedException($"Type {type} is not supported."); + } + + + private static ValueTypeConditionResolver.Operator ParseValueTypeOperator(string operatorStr) where T : struct, IComparable + { + return operatorStr switch + { + ">" => ValueTypeConditionResolver.Operator.GreaterThan, + "<" => ValueTypeConditionResolver.Operator.LessThan, + "=" => ValueTypeConditionResolver.Operator.Equal, + "==" => ValueTypeConditionResolver.Operator.Equal, + ">=" => ValueTypeConditionResolver.Operator.GreaterThanOrEqual, + "≥" => ValueTypeConditionResolver.Operator.GreaterThanOrEqual, + "<=" => ValueTypeConditionResolver.Operator.LessThanOrEqual, + "≤" => ValueTypeConditionResolver.Operator.LessThanOrEqual, + "equals" => ValueTypeConditionResolver.Operator.Equal, + "in" => ValueTypeConditionResolver.Operator.InRange, + "!in" => ValueTypeConditionResolver.Operator.OutOfRange, + _ => throw new ArgumentException($"Invalid operator {operatorStr} for value type.") + }; + } + + private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr) + { + return operatorStr switch + { + "is" => BoolConditionResolver.Operator.Is, + "==" => BoolConditionResolver.Operator.Is, + "equals" => BoolConditionResolver.Operator.Is, + //"isFalse" => BoolConditionNode.Operator.IsFalse, + _ => throw new ArgumentException($"Invalid operator {operatorStr} for bool type.") + }; + } + + private static StringConditionResolver.Operator ParseStringOperator(string operatorStr) + { + return operatorStr switch + { + "c" => StringConditionResolver.Operator.Contains, + "nc" => StringConditionResolver.Operator.DoesNotContain, + "sw" => StringConditionResolver.Operator.StartsWith, + "ew" => StringConditionResolver.Operator.EndsWith, + + "contains" => StringConditionResolver.Operator.Contains, + "doesNotContain" => StringConditionResolver.Operator.DoesNotContain, + "equals" => StringConditionResolver.Operator.Equal, + "==" => StringConditionResolver.Operator.Equal, + "notEquals" => StringConditionResolver.Operator.NotEqual, + "!=" => StringConditionResolver.Operator.NotEqual, + "startsWith" => StringConditionResolver.Operator.StartsWith, + "endsWith" => StringConditionResolver.Operator.EndsWith, + _ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.") + }; + } + } + +} diff --git a/NodeFlow/Tool/SerinExpression/SerinExpressionEvaluator.cs b/NodeFlow/Tool/SerinExpression/SerinExpressionEvaluator.cs new file mode 100644 index 0000000..99666ea --- /dev/null +++ b/NodeFlow/Tool/SerinExpression/SerinExpressionEvaluator.cs @@ -0,0 +1,216 @@ +using System.Data; + +namespace Serein.NodeFlow.Tool.SerinExpression +{ + public class SerinArithmeticExpressionEvaluator + { + private static readonly DataTable table = new DataTable(); + + public static double Evaluate(string expression, double inputValue) + { + // 替换占位符@为输入值 + expression = expression.Replace("@", inputValue.ToString()); + try + { + // 使用 DataTable.Compute 方法计算表达式 + var result = table.Compute(expression, string.Empty); + return Convert.ToDouble(result); + } + catch + { + throw new ArgumentException("Invalid arithmetic expression."); + } + } + } + + public class SerinExpressionEvaluator + { + /// + /// + /// + /// 表达式 + /// 操作对象 + /// 是否改变了对象(get语法) + /// + /// + /// + public static object Evaluate(string expression, object targetObJ, out bool isChange) + { + var parts = expression.Split([' '], 2); + if (parts.Length != 2) + { + throw new ArgumentException("Invalid expression format."); + } + + var operation = parts[0].ToLower(); + var operand = parts[1][0] == '.' ? parts[1][1..] : parts[1]; + + var result = operation switch + { + "@num" => ComputedNumber(targetObJ, operand), + "@call" => InvokeMethod(targetObJ, operand), + "@get" => GetMember(targetObJ, operand), + "@set" => SetMember(targetObJ, operand), + _ => throw new NotSupportedException($"Operation {operation} is not supported.") + }; + + isChange = operation switch + { + "@num" => true, + "@call" => true, + "@get" => true, + "@set" => false, + _ => throw new NotSupportedException($"Operation {operation} is not supported.") + }; + + return result; + } + + + private static readonly char[] separator = ['(', ')']; + private static readonly char[] separatorArray = [',']; + + private static object InvokeMethod(object target, string methodCall) + { + var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries); + if (methodParts.Length != 2) + { + throw new ArgumentException("Invalid method call format."); + } + + var methodName = methodParts[0]; + var parameterList = methodParts[1]; + var parameters = parameterList.Split(separatorArray, StringSplitOptions.RemoveEmptyEntries) + .Select(p => p.Trim()) + .ToArray(); + + var method = target.GetType().GetMethod(methodName); + if (method == null) + { + throw new ArgumentException($"Method {methodName} not found on target."); + } + + var parameterValues = method.GetParameters() + .Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType)) + .ToArray(); + + + return method.Invoke(target, parameterValues); + + } + + private static object GetMember(object target, string memberPath) + { + var members = memberPath.Split('.'); + foreach (var member in members) + { + + if (target == null) return null; + + + var property = target.GetType().GetProperty(member); + if (property != null) + { + + target = property.GetValue(target); + + } + else + { + var field = target.GetType().GetField(member); + if (field != null) + { + + target = field.GetValue(target); + + } + else + { + throw new ArgumentException($"Member {member} not found on target."); + } + } + } + + + return target; + + } + + private static object SetMember(object target, string assignment) + { + var parts = assignment.Split(new[] { '=' }, 2); + if (parts.Length != 2) + { + throw new ArgumentException("Invalid assignment format."); + } + + var memberPath = parts[0].Trim(); + var value = parts[1].Trim(); + + var members = memberPath.Split('.'); + for (int i = 0; i < members.Length - 1; i++) + { + var member = members[i]; + + var property = target.GetType().GetProperty(member); + + if (property != null) + { + + target = property.GetValue(target); + + } + else + { + var field = target.GetType().GetField(member); + if (field != null) + { + + target = field.GetValue(target); + + } + else + { + throw new ArgumentException($"Member {member} not found on target."); + } + } + } + + var lastMember = members.Last(); + + var lastProperty = target.GetType().GetProperty(lastMember); + + if (lastProperty != null) + { + var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType); + lastProperty.SetValue(target, convertedValue); + } + else + { + var lastField = target.GetType().GetField(lastMember); + if (lastField != null) + { + var convertedValue = Convert.ChangeType(value, lastField.FieldType); + lastField.SetValue(target, convertedValue); + } + else + { + throw new ArgumentException($"Member {lastMember} not found on target."); + } + } + + return target; + } + + private static double ComputedNumber(object value, string expression) + { + double numericValue = Convert.ToDouble(value); + if (!string.IsNullOrEmpty(expression)) + { + numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue); + } + + return numericValue; + } + } +} diff --git a/NodeFlow/Tool/TcsSignal.cs b/NodeFlow/Tool/TcsSignal.cs index bee7f2d..2057ab4 100644 --- a/NodeFlow/Tool/TcsSignal.cs +++ b/NodeFlow/Tool/TcsSignal.cs @@ -17,7 +17,8 @@ namespace Serein.NodeFlow.Tool { //public ConcurrentDictionary>> TcsEvent { get; } = new(); public ConcurrentDictionary> TcsEvent { get; } = new(); - public ConcurrentDictionary TcsValue { get; } = new(); + + public ConcurrentDictionary TcsLock { get; } = new(); /// /// 触发信号 @@ -28,30 +29,36 @@ namespace Serein.NodeFlow.Tool /// 是否成功触发 public bool TriggerSignal(TSignal signal, T value) { - if (TcsEvent.TryRemove(signal, out var waitTcs)) + var tcsLock = TcsLock.GetOrAdd(signal, new object()); + lock (tcsLock) { - waitTcs.SetResult(value); - return true; + if (TcsEvent.TryRemove(signal, out var waitTcs)) + { + waitTcs.SetResult(value); + return true; + } + return false; } - return false; } public TaskCompletionSource CreateTcs(TSignal signal) { - var tcs = TcsEvent.GetOrAdd(signal,_ = new TaskCompletionSource()); - return tcs; + var tcsLock = TcsLock.GetOrAdd(signal, new object()); + lock (tcsLock) + { + var tcs = TcsEvent.GetOrAdd(signal, new TaskCompletionSource()); + return tcs; + } + } public void CancelTask() { - lock (TcsEvent) + foreach (var tcs in TcsEvent.Values) { - foreach (var tcs in TcsEvent.Values) - { - tcs.SetException(new TcsSignalException("任务取消")); - } - TcsEvent.Clear(); + tcs.SetException(new TcsSignalException("任务取消")); } + TcsEvent.Clear(); } } } diff --git a/SereinFlow.sln b/SereinFlow.sln index 28d15fd..dadc672 100644 --- a/SereinFlow.sln +++ b/SereinFlow.sln @@ -7,9 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyDll", "MyDll\MyDll.csproj EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.WorkBench", "WorkBench\Serein.WorkBench.csproj", "{EC933A9F-DAD3-4D26-BF27-DA9DE5263BCD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Module.WAT", "SereinWAT\Serein.Module.WAT.csproj", "{C2F68A15-5D07-4418-87C0-E7402DD9F83A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library.Core", "Library.Core\Serein.Library.Core.csproj", "{4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.NodeFlow", "NodeFlow\Serein.NodeFlow.csproj", "{7B51A19A-88AB-471E-BCE3-3888C67C936D}" EndProject @@ -18,6 +16,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.Framework", "Library.Framework\Serein.Library.Framework.csproj", "{73B272E8-222D-4D08-A030-F1E1DB70B9D1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -36,14 +38,18 @@ Global {4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {4A7D23E7-B05C-4B6D-A8B9-1A488DC356FD}.Release|Any CPU.Build.0 = Release|Any CPU - {C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C2F68A15-5D07-4418-87C0-E7402DD9F83A}.Release|Any CPU.Build.0 = Release|Any CPU {7B51A19A-88AB-471E-BCE3-3888C67C936D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7B51A19A-88AB-471E-BCE3-3888C67C936D}.Debug|Any CPU.Build.0 = Debug|Any CPU {7B51A19A-88AB-471E-BCE3-3888C67C936D}.Release|Any CPU.ActiveCfg = Release|Any CPU {7B51A19A-88AB-471E-BCE3-3888C67C936D}.Release|Any CPU.Build.0 = Release|Any CPU + {73B272E8-222D-4D08-A030-F1E1DB70B9D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73B272E8-222D-4D08-A030-F1E1DB70B9D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73B272E8-222D-4D08-A030-F1E1DB70B9D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73B272E8-222D-4D08-A030-F1E1DB70B9D1}.Release|Any CPU.Build.0 = Release|Any CPU + {5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SereinWAT/SereinWAT.cs b/SereinWAT/SereinWAT.cs index 8366aa4..2391b54 100644 --- a/SereinWAT/SereinWAT.cs +++ b/SereinWAT/SereinWAT.cs @@ -4,6 +4,7 @@ using OpenQA.Selenium.Edge; using OpenQA.Selenium.Firefox; using OpenQA.Selenium.IE; using Serein.NodeFlow; +using Serein.NodeFlow.Tool; using static System.Net.Mime.MediaTypeNames; namespace Serein.Module diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index cc08f5d..e9f9efd 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -146,7 +146,7 @@ namespace Serein.WorkBench Shutdown(); // 关闭应用程序 } } - else if (1 == 1) + else if (1 == 11) { string filePath = @"F:\临时\project\U9 project.dnf"; //string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf"; diff --git a/WorkBench/MainWindow.xaml b/WorkBench/MainWindow.xaml index d8eced8..d5695eb 100644 --- a/WorkBench/MainWindow.xaml +++ b/WorkBench/MainWindow.xaml @@ -93,6 +93,10 @@ x:Name="FlowChartCanvas" Background="#D9FFEA" AllowDrop="True" + Width="1000" + Height="700" + MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown" + MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp" MouseDown="FlowChartCanvas_MouseDown" MouseMove="FlowChartCanvas_MouseMove" MouseUp="FlowChartCanvas_MouseUp" @@ -100,6 +104,14 @@ Drop="FlowChartCanvas_Drop" DragOver="FlowChartCanvas_DragOver"> + +