diff --git a/protocols/JiShe.CollectBus.Protocol.Contracts/Abstracts/ProtocolPlugin.cs b/protocols/JiShe.CollectBus.Protocol.Contracts/Abstracts/ProtocolPlugin.cs index a323787..a196661 100644 --- a/protocols/JiShe.CollectBus.Protocol.Contracts/Abstracts/ProtocolPlugin.cs +++ b/protocols/JiShe.CollectBus.Protocol.Contracts/Abstracts/ProtocolPlugin.cs @@ -1,6 +1,8 @@ using System; using System.Reflection; +using JiShe.CollectBus.Common.Consts; using JiShe.CollectBus.Common.Extensions; +using JiShe.CollectBus.FreeRedis; using JiShe.CollectBus.IotSystems.Protocols; using JiShe.CollectBus.Protocol.Contracts.Interfaces; using JiShe.CollectBus.Protocol.Contracts.Models; @@ -22,10 +24,13 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts private readonly ILogger _logger; private readonly IRepository _protocolInfoRepository; + private readonly IFreeRedisProvider _redisProvider; + public ProtocolPlugin(IServiceProvider serviceProvider, ILogger logger) { _logger = logger; _protocolInfoRepository = serviceProvider.GetRequiredService>(); + _redisProvider = serviceProvider.GetRequiredService(); } @@ -42,7 +47,8 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts await _protocolInfoRepository.DeleteDirectAsync(a => a.Name == Info.Name); await _protocolInfoRepository.InsertAsync(Info); - //await _protocolInfoCache.Get() + await _redisProvider.Instance.HDelAsync($"{RedisConst.ProtocolKey}", Info.Name); + await _redisProvider.Instance.HSetAsync($"{RedisConst.ProtocolKey}", Info.Name, Info); } public abstract Task AnalyzeAsync(ITcpSessionClient client, string messageReceived, Action? receivedAction = null) where T :class; diff --git a/protocols/JiShe.CollectBus.Protocol.Test/JiSheCollectBusTestProtocolModule.cs b/protocols/JiShe.CollectBus.Protocol.Test/JiSheCollectBusTestProtocolModule.cs index 314c3ab..225179a 100644 --- a/protocols/JiShe.CollectBus.Protocol.Test/JiSheCollectBusTestProtocolModule.cs +++ b/protocols/JiShe.CollectBus.Protocol.Test/JiSheCollectBusTestProtocolModule.cs @@ -1,5 +1,6 @@ using JiShe.CollectBus.Protocol.Contracts.Interfaces; using Microsoft.Extensions.DependencyInjection; +using System; using Volo.Abp; using Volo.Abp.Modularity; @@ -15,9 +16,30 @@ namespace JiShe.CollectBus.Protocol.Test public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) { - Console.WriteLine("TestProtocolPlugin Initialization"); + Console.WriteLine("TestProtocolPlugin OnApplicationInitializationAsync"); var protocol = context.ServiceProvider.GetRequiredKeyedService(nameof(TestProtocolPlugin)); + RemoveServiceAtRuntime(context.ServiceProvider); await protocol.LoadAsync(); } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + Console.WriteLine("TestProtocolPlugin OnApplicationShutdown"); + base.OnApplicationShutdown(context); + } + + public void RemoveServiceAtRuntime(IServiceProvider serviceProvider) + { + var services = serviceProvider.GetService(); + services?.AddKeyedSingleton(nameof(TestProtocolPlugin)); + + + //var services = (IServiceCollection)serviceProvider.GetService(typeof(IServiceCollection)); + //var pluginService = serviceProvider.GetKeyedService(nameof(TestProtocolPlugin)); + //if (services != null && pluginService!=null) + //{ + // services.Remove(pluginService); + //} + } } } diff --git a/protocols/JiShe.CollectBus.Protocol.Test/TestProtocolPlugin.cs b/protocols/JiShe.CollectBus.Protocol.Test/TestProtocolPlugin.cs index e291aa0..50f5b0c 100644 --- a/protocols/JiShe.CollectBus.Protocol.Test/TestProtocolPlugin.cs +++ b/protocols/JiShe.CollectBus.Protocol.Test/TestProtocolPlugin.cs @@ -20,7 +20,7 @@ namespace JiShe.CollectBus.Protocol.Test { } - public sealed override ProtocolInfo Info => new(nameof(TestProtocolPlugin), "Test", "TCP", "Test协议", "DTS1980-Test"); + public sealed override ProtocolInfo Info => new(nameof(TestProtocolPlugin), "Test", "TCP", "Test协议", "DTS1980-TEST"); public override Task AnalyzeAsync(ITcpSessionClient client, string messageReceived, Action? receivedAction = null) { diff --git a/protocols/JiShe.CollectBus.Protocol/JiSheCollectBusProtocolModule.cs b/protocols/JiShe.CollectBus.Protocol/JiSheCollectBusProtocolModule.cs index 8009165..dab94d8 100644 --- a/protocols/JiShe.CollectBus.Protocol/JiSheCollectBusProtocolModule.cs +++ b/protocols/JiShe.CollectBus.Protocol/JiSheCollectBusProtocolModule.cs @@ -11,7 +11,6 @@ using Microsoft.Extensions.Logging; using Serilog.Core; using System; using System.Reflection; -using TouchSocket.Core; using Volo.Abp; using Volo.Abp.Modularity; @@ -21,7 +20,6 @@ namespace JiShe.CollectBus.Protocol { public override void ConfigureServices(ServiceConfigurationContext context) { - Console.WriteLine("StandardProtocolPlugin ConfigureServices"); context.Services.AddKeyedSingleton(nameof(StandardProtocolPlugin)); //RegisterProtocolAnalysis(context.Services); LoadAnalysisStrategy(context.Services); @@ -29,11 +27,17 @@ namespace JiShe.CollectBus.Protocol public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) { - Console.WriteLine("StandardProtocolPlugin Initialization"); + Console.WriteLine("StandardProtocolPlugin OnApplicationInitializationAsync"); var standardProtocol = context.ServiceProvider.GetRequiredKeyedService(nameof(StandardProtocolPlugin)); await standardProtocol.LoadAsync(); } + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + Console.WriteLine("StandardProtocolPlugin OnApplicationShutdown"); + base.OnApplicationShutdown(context); + } + public void LoadAnalysisStrategy(IServiceCollection services) { var assembly = Assembly.GetExecutingAssembly(); diff --git a/services/JiShe.CollectBus.Application/Samples/TestAppService.cs b/services/JiShe.CollectBus.Application/Samples/TestAppService.cs index b054779..f5f2e6b 100644 --- a/services/JiShe.CollectBus.Application/Samples/TestAppService.cs +++ b/services/JiShe.CollectBus.Application/Samples/TestAppService.cs @@ -23,11 +23,13 @@ using JiShe.CollectBus.IotSystems.MessageReceiveds; using Volo.Abp.Domain.Repositories; using System.Diagnostics; using System.Linq; +using System.Reflection; using Cassandra; using JiShe.CollectBus.Interceptors; using JiShe.CollectBus.IotSystems.Protocols; using TouchSocket.Core; using Volo.Abp.Modularity; +using JiShe.CollectBus.DynamicModule; namespace JiShe.CollectBus.Samples; @@ -39,18 +41,20 @@ public class TestAppService : CollectBusAppService private readonly ICassandraProvider _cassandraProvider; private readonly IProtocolService _protocolService; private readonly IServiceProvider _serviceProvider; + private readonly IDynamicModuleManager _dynamicModuleManager; public TestAppService( ILogger logger, ICassandraRepository messageReceivedCassandraRepository, - ICassandraProvider cassandraProvider, IProtocolService protocolService,IServiceProvider serviceProvider) + ICassandraProvider cassandraProvider, IProtocolService protocolService,IServiceProvider serviceProvider, IDynamicModuleManager dynamicModuleManager) { _logger = logger; _messageReceivedCassandraRepository = messageReceivedCassandraRepository; _cassandraProvider = cassandraProvider; _protocolService = protocolService; _serviceProvider = serviceProvider; + _dynamicModuleManager = dynamicModuleManager; } public async Task AddMessageOfCassandra() { @@ -146,6 +150,9 @@ public class TestAppService : CollectBusAppService // 重新加载插件方法 public async Task ReloadPluginsAsync() { - //_serviceProvider.GetService<>() + var aa = Assembly.LoadFile( + @"D:\Codes\CollectBusV5\JiShe.CollectBus\web\JiShe.CollectBus.Host\bin\Debug\net8.0\Plugins\JiShe.CollectBus.Protocol.Test.dll"); + var module = aa.GetTypes().First(a=> typeof(IAbpModule).IsAssignableFrom(a)); + await _dynamicModuleManager.ReinitializeModuleAsync(module); } } diff --git a/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/DynamicModuleManager.cs b/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/DynamicModuleManager.cs new file mode 100644 index 0000000..6bbc8e7 --- /dev/null +++ b/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/DynamicModuleManager.cs @@ -0,0 +1,182 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Modularity; + +namespace JiShe.CollectBus.DynamicModule +{ + /// + /// 动态模块管理器的实现 + /// + public class DynamicModuleManager : IDynamicModuleManager, ISingletonDependency + { + private readonly IModuleContainer _moduleContainer; + private readonly IServiceProvider _serviceProvider; + + public DynamicModuleManager( + IModuleContainer moduleContainer, + IServiceProvider serviceProvider) + { + _moduleContainer = moduleContainer; + _serviceProvider = serviceProvider; + } + + public Type[] GetRegisteredModuleTypes() + { + return _moduleContainer.Modules.Select(m => m.Type).ToArray(); + } + + public async Task InitializeModuleAsync(Type moduleType) + { + //if (!typeof(IAbpModule).IsAssignableFrom(moduleType)) + //{ + // throw new ArgumentException($"指定的类型 {moduleType.FullName} 不是有效的ABP模块类型", nameof(moduleType)); + //} + + //var module = (IAbpModule)Activator.CreateInstance(moduleType); + + //// 配置服务 + //var configureServicesMethod = moduleType.GetMethod("ConfigureServices", + // BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + + //if (configureServicesMethod != null) + //{ + // var serviceConfigurationContext = CreateServiceConfigurationContext(); + // configureServicesMethod.Invoke(module, new object[] { serviceConfigurationContext }); + //} + + //await CallModuleMethodAsync(module, "OnApplicationInitializationAsync", new object[] { new ApplicationInitializationContext(_serviceProvider) }); + + + } + + public async Task ReinitializeModuleAsync(Type moduleType) + { + if (!typeof(IAbpModule).IsAssignableFrom(moduleType)) + { + throw new ArgumentException($"指定的类型 {moduleType.FullName} 不是有效的ABP模块类型", nameof(moduleType)); + } + + var moduleDescriptor = _moduleContainer.Modules.FirstOrDefault(m => m.Type.Name == moduleType.Name); + if (moduleDescriptor == null) + { + throw new InvalidOperationException($"找不到类型为 {moduleType.FullName} 的模块"); + } + + var module = moduleDescriptor.Instance; + + await CallModuleMethodAsync(module, "OnApplicationShutdown", new object[] { new ApplicationShutdownContext(_serviceProvider) }); + //var configureServicesMethod = moduleType.GetMethod("ConfigureServices", + // BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + + //if (configureServicesMethod != null) + //{ + // var serviceConfigurationContext = CreateServiceConfigurationContext(); + // configureServicesMethod.Invoke(module, new object[] { serviceConfigurationContext }); + //} + await CallModuleMethodAsync(module, "OnApplicationInitializationAsync", new object[] { new ApplicationInitializationContext(_serviceProvider) }); + } + + public async Task UnloadModuleAsync(Type moduleType) + { + if (!typeof(IAbpModule).IsAssignableFrom(moduleType)) + { + throw new ArgumentException($"指定的类型 {moduleType.FullName} 不是有效的ABP模块类型", nameof(moduleType)); + } + + var moduleDescriptor = _moduleContainer.Modules.FirstOrDefault(m => m.Type.Name == moduleType.Name); + if (moduleDescriptor == null) + { + throw new InvalidOperationException($"找不到类型为 {moduleType.FullName} 的模块"); + } + + var module = moduleDescriptor.Instance; + + await CallModuleMethodAsync(module, "OnApplicationShutdown", new object[] { new ApplicationShutdownContext(_serviceProvider) }); + moduleDescriptor.GetType().GetProperty("IsUnloaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)? + .SetValue(moduleDescriptor, true); + } + + public bool IsModuleLoaded(Type moduleType) + { + var moduleDescriptor = _moduleContainer.Modules.FirstOrDefault(m => m.Type == moduleType); + if (moduleDescriptor == null) + { + return false; + } + + // 检查模块是否已被标记为卸载 + var isUnloaded = (bool?)moduleDescriptor.GetType().GetProperty("IsUnloaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)? + .GetValue(moduleDescriptor) ?? false; + + return !isUnloaded; + } + + public Type GetModuleTypeByName(string moduleName) + { + return _moduleContainer.Modules + .FirstOrDefault(m => m.Type.Name == moduleName || m.Type.FullName == moduleName)? + .Type; + } + + private async Task CallModuleMethodAsync(IAbpModule module, string methodName, object[] parameters) + { + var method = module.GetType().GetMethod(methodName); + if (method == null) + { + return; + } + + if (method.ReturnType == typeof(Task)) + { + await (Task)method.Invoke(module, parameters); + } + else + { + method.Invoke(module, parameters); + } + } + + private ServiceConfigurationContext CreateServiceConfigurationContext() + { + // 反射获取内部构造函数 + var contextType = typeof(ServiceConfigurationContext); + var constructor = contextType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(); + + if (constructor != null) + { + // 创建新的服务集合并添加现有服务 + var services = new ServiceCollection(); + foreach (var service in ((IServiceCollection)_serviceProvider.GetService()) ?? new ServiceCollection()) + { + services.Add(service); + } + + return (ServiceConfigurationContext)constructor.Invoke(new object[] { services }); + } + + // 如果反射失败,使用备选方案 + try + { + var bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance; + var services = new ServiceCollection(); + + var context = Activator.CreateInstance(contextType, bindingFlags, null, null, null); + + var servicesProperty = contextType.GetProperty("Services"); + servicesProperty?.SetValue(context, services); + + return (ServiceConfigurationContext)context; + } + catch + { + throw new InvalidOperationException("无法创建ServiceConfigurationContext。这可能是由于与ABP框架版本不兼容造成的。"); + } + } + } +} diff --git a/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/IDynamicModuleManager.cs b/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/IDynamicModuleManager.cs new file mode 100644 index 0000000..3f87820 --- /dev/null +++ b/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/IDynamicModuleManager.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.DynamicModule +{ + /// + /// 提供动态管理ABP模块的功能 + /// + public interface IDynamicModuleManager + { + /// + /// 获取已注册的模块类型 + /// + /// 当前应用程序中已注册的所有模块类型 + Type[] GetRegisteredModuleTypes(); + + /// + /// 重新初始化指定的模块 + /// + /// 要重新初始化的模块类型 + /// 表示异步操作的任务 + Task ReinitializeModuleAsync(Type moduleType); + + /// + /// 卸载指定的模块 + /// + /// 要卸载的模块类型 + /// 表示异步操作的任务 + Task UnloadModuleAsync(Type moduleType); + + /// + /// 检查模块是否已加载 + /// + /// 要检查的模块类型 + /// 如果模块已加载,则为true;否则为false + bool IsModuleLoaded(Type moduleType); + + /// + /// 根据模块名称获取模块类型 + /// + /// 模块名称 + /// 模块类型,如果找不到则为null + Type GetModuleTypeByName(string moduleName); + } +} \ No newline at end of file