From 4eb76190868cd16e07786647083f6f0ba0091606 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Fri, 6 Jun 2025 14:15:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Directory.Build.JiShe.targets | 103 ++ Directory.Build.Microsoft.targets | 42 + Directory.Build.Volo.targets | 102 ++ Directory.Build.props | 12 + Directory.Build.targets | 155 +++ Dockerfile | 10 +- JiShe.CollectBus.Main.sln | 65 +- JiShe.CollectBus.sln | 154 +-- NuGet.Config | 9 +- icon.png | Bin 0 -> 5207 bytes .../ComplexTypeSourceAnalyzers.cs | 734 ----------- .../JiShe.CollectBus.Analyzers.csproj | 21 - .../CassandraConfig.cs | 64 - .../CassandraProvider.cs | 154 --- .../CassandraRepository.cs | 86 -- .../CollectBusCassandraModule.cs | 25 - .../Extensions/ServiceCollectionExtensions.cs | 25 - .../Extensions/SessionExtension.cs | 98 -- .../ICassandraProvider.cs | 26 - .../ICassandraRepository.cs | 23 - .../JiShe.CollectBus.Cassandra.csproj | 22 - .../CollectBusFreeRedisModule.cs | 25 - .../FreeRedisProvider.cs | 42 - .../IFreeRedisProvider.cs | 14 - .../JiShe.CollectBus.FreeRedis.csproj | 15 - .../Options/FreeRedisOptions.cs | 26 - .../CollectBusFreeSqlModule.cs | 14 - modules/JiShe.CollectBus.FreeSql/DbEnum.cs | 23 - .../FreeSqlProvider.cs | 37 - .../IFreeSqlProvider.cs | 12 - .../JiShe.CollectBus.FreeSql.csproj | 13 - .../Attributes/ATTRIBUTEColumnAttribute.cs | 10 - .../Attributes/FIELDColumnAttribute.cs | 10 - .../Attributes/IgnoreInitTableAttribute.cs | 14 - .../Attributes/SingleMeasuringAttribute.cs | 16 - .../Attributes/TAGColumnAttribute.cs | 10 - .../TableNameOrTreePathAttribute.cs | 17 - .../CollectBusIoTDBModule.cs | 22 - .../Context/IoTDBRuntimeContext.cs | 33 - .../Exceptions/IoTException.cs | 22 - .../Interface/IIoTDBProvider.cs | 75 -- .../Interface/IIoTDBSessionFactory.cs | 10 - .../Interface/IIoTDBSessionPool.cs | 43 - .../JiShe.CollectBus.IoTDB.csproj | 19 - .../JiShe.CollectBus.IoTDB/Model/IoTEntity.cs | 72 -- .../Model/TableModelSingleMeasuringEntity.cs | 19 - .../Model/TreeModelSingleMeasuringEntity.cs | 18 - .../Options/IoTDBOptions.cs | 56 - .../Options/IoTDBQueryOptions.cs | 28 - .../Options/QueryCondition.cs | 64 - .../Provider/DeviceMetadata.cs | 97 -- .../Provider/DevicePathBuilder.cs | 46 - .../Provider/IoTDBProvider.cs | 1133 ----------------- .../Provider/IoTDBSessionFactory.cs | 53 - .../Provider/SessionPoolAdapter.cs | 105 -- .../Provider/TableSessionPoolAdapter.cs | 104 -- .../SourceEntityAccessorFactory3.cs | 52 - .../ConsoleApplicationBuilder.cs | 72 -- .../JiShe.CollectBus.Kafka.Test.csproj | 44 - .../KafkaProduceBenchmark.cs | 121 -- .../KafkaSubscribeTest.cs | 70 - .../Lib/JiShe.CollectBus.Common.dll | Bin 171008 -> 0 bytes .../Lib/JiShe.CollectBus.Kafka.dll | Bin 59904 -> 0 bytes .../JiShe.CollectBus.Kafka.Test/Program.cs | 197 --- .../Properties/launchSettings.json | 8 - .../appsettings.json | 155 --- .../AdminClient/AdminClientService.cs | 187 --- .../AdminClient/IAdminClientService.cs | 63 - .../Attributes/KafkaSubscribeAttribute.cs | 60 - .../Attributes/TopicAttribute.cs | 22 - .../CollectBusKafkaModule.cs | 70 - .../Consumer/ConsumerService.cs | 718 ----------- .../Consumer/IConsumerService.cs | 50 - .../JiShe.CollectBus.Kafka/HostedService.cs | 43 - .../Internal/HeadersFilter.cs | 22 - .../Internal/IKafkaSubscribe.cs | 11 - .../Internal/ISubscribeAck.cs | 14 - .../Internal/KafkaOptionConfig.cs | 59 - .../Internal/KafkaPollyPipeline.cs | 105 -- .../Internal/KafkaTaskScheduler.cs | 169 --- .../Internal/ReflectionHelper.cs | 103 -- .../Internal/SubscribeResult.cs | 62 - .../JiShe.CollectBus.Kafka.csproj | 21 - .../KafkaSubscribeExtensions.cs | 435 ------- .../Producer/IProducerService.cs | 15 - .../Producer/ProducerService.cs | 265 ---- .../Serialization/JsonSerializer.cs | 139 -- .../JiShe.CollectBus.MongoDB/FodyWeavers.xml | 3 - .../JiShe.CollectBus.MongoDB.abppkg | 3 - .../JiShe.CollectBus.MongoDB.csproj | 23 - .../MongoDB/CollectBusDbSchemaMigrator.cs | 52 - .../MongoDB/CollectBusMongoDbContext.cs | 94 -- .../CollectBusMongoDbContextExtensions.cs | 13 - .../MongoDB/CollectBusMongoDbModule.cs | 54 - .../MongoDB/ICollectBusMongoDbContext.cs | 12 - .../LogRecord/ILogRecordRepository.cs | 57 - .../LogRecord/LogRecordRepository.cs | 166 --- .../IMeterReadingRecordRepository.cs | 60 - .../MeterReadingRecordRepository.cs | 173 --- .../ShardingStrategy/DayShardingStrategy.cs | 61 - .../ShardingStrategy/HourShardingStrategy.cs | 58 - .../ShardingStrategy/IHourShardingStrategy.cs | 12 - .../ShardingStrategy/IShardingStrategy.cs | 36 - .../ProtocolPools/IPluginContainer.cs | 6 - .../ProtocolPools/PluginContainer.cs | 26 - .../GlobalUsings.cs | 9 + .../JiShe.CollectBus.Protocol.T1882018.csproj | 8 +- .../AnalysisData/AFN_00H/AFN0_F1_Analysis.cs | 6 +- .../AnalysisData/AFN_00H/AFN0_F2_Analysis.cs | 4 +- .../AnalysisData/AFN_02H/AFN2_F1_Analysis.cs | 4 +- .../AnalysisData/AFN_02H/AFN2_F2_Analysis.cs | 4 +- .../AnalysisData/AFN_02H/AFN2_F3_Analysis.cs | 4 +- .../AnalysisData/AFN_09H/AFN9_F1_Analysis.cs | 6 +- .../AnalysisData/AFN_09H/AFN9_F9_Analysis.cs | 4 +- .../AFN_0AH/AFN10_F10_Analysis.cs | 4 +- .../AFN_0AH/AFN10_F66_Analysis.cs | 4 +- .../AFN_0AH/AFN10_F68_Analysis.cs | 4 +- .../AFN_0CH/AFN12_F129_Analysis.cs | 11 +- .../AFN_0CH/AFN12_F130_Analysis.cs | 4 +- .../AFN_0CH/AFN12_F131_Analysis.cs | 4 +- .../AFN_0CH/AFN12_F132_Analysis.cs | 6 +- .../AFN_0CH/AFN12_F145_Analysis.cs | 4 +- .../AFN_0CH/AFN12_F149_Analysis.cs | 4 +- .../AFN_0CH/AFN12_F188_Analysis.cs | 4 +- .../AFN_0CH/AFN12_F25_Analysis.cs | 4 +- .../AnalysisData/AFN_0CH/AFN12_F2_Analysis.cs | 4 +- .../AFN_0CH/AFN12_F33_Analysis.cs | 4 +- .../AFN_0CH/AFN12_F49_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F100_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F101_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F102_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F103_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F104_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F105_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F106_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F107_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F108_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F11_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F145_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F146_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F147_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F148_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F161_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F162_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F163_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F164_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F165_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F166_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F167_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F168_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F177_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F178_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F179_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F180_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F181_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F182_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F183_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F184_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F189_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F190_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F193_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F195_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F19_Analysis.cs | 4 +- .../AnalysisData/AFN_0DH/AFN13_F3_Analysis.cs | 4 +- .../AnalysisData/AFN_0DH/AFN13_F4_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F81_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F82_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F83_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F84_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F85_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F86_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F87_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F88_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F89_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F90_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F91_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F92_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F93_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F94_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F95_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F97_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F98_Analysis.cs | 4 +- .../AFN_0DH/AFN13_F99_Analysis.cs | 4 +- .../AnalysisData/AFN_0EH/AFN14_F1_Analysis.cs | 4 +- .../AnalysisData/AFN_10H/AFN16_F1_Analysis.cs | 4 +- .../AFN_10H/Ammeter/AFN16_F101_Analysis.cs | 4 +- .../AFN_10H/Ammeter/AFN16_F97_Analysis.cs | 7 +- .../AFN_10H/Ammeter/AFN16_F98_Analysis.cs | 4 +- .../AFN_10H/Watermeter/ES190DC_Analysis.cs | 4 +- .../AnalysisData/DataStorage.cs | 85 +- .../GlobalUsings.cs | 9 + ...JiShe.CollectBus.Protocol.T37612012.csproj | 8 +- .../T37612012ProtocolPlugin.cs | 39 +- .../GlobalUsings.cs | 9 + .../JiShe.CollectBus.Protocol.T6452007.csproj | 6 +- .../JiShe.CollectBus.Protocol.Test.csproj | 6 +- .../JiSheCollectBusTestProtocolModule.cs | 2 + .../TestServer.cs | 27 + .../Abstracts/ProtocolPlugin.cs | 12 +- .../JiShe.CollectBus.Protocol/GlobalUsings.cs | 9 + .../JiShe.CollectBus.Protocol.csproj | 11 +- .../Services/ProtocolService.cs | 4 +- .../CollectBusApplicationContractsModule.cs | 2 + .../DataChannels/IDataChannelManageService.cs | 2 +- .../GlobalUsings.cs | 13 + ...he.CollectBus.Application.Contracts.csproj | 13 +- .../Permissions/CollectBusPermissions.cs | 2 +- .../RedisDataCache/IRedisDataCacheService.cs | 174 --- .../IScheduledMeterReadingService.cs | 40 +- ...ceCommunicationChannelSubscriberService.cs | 22 + .../Subscribers/ISubscriberAppService.cs | 8 +- .../IWorkerSubscriberAppService.cs | 10 +- .../CollectBusAppService.cs | 15 +- .../CollectBusApplicationModule.cs | 71 +- .../DataChannels/DataChannelManage.cs | 1 + .../DataChannels/DataChannelManageService.cs | 161 +-- .../EnergySystem/CacheAppService.cs | 16 +- .../EnergySystem/EnergySystemAppService.cs | 83 +- .../GlobalUsings.cs | 17 + .../JiShe.CollectBus.Application.csproj | 33 +- .../Mappers/CollectBusMapping.cs | 17 - .../Plugins/TcpMonitor.cs | 97 +- .../RedisDataCache/RedisDataCacheService.cs | 994 --------------- .../Samples/SampleAppService.cs | 255 +--- .../Samples/TestAppService.cs | 223 ++-- .../BasicScheduledMeterReadingService.cs | 336 +---- ...nergySystemScheduledMeterReadingService.cs | 82 +- ...ceCommunicationChannelSubscriberService.cs | 91 ++ .../SubscriberAnalysisAppService.cs | 51 +- .../Subscribers/SubscriberAppService.cs | 172 --- .../Subscribers/WorkerSubscriberAppService.cs | 39 +- .../Workers/CreateToBeIssueTaskWorker.cs | 43 - .../DataDetectionFifteenMinuteWorker.cs | 39 - .../Workers/EpiCollectWorker.cs | 39 - ...eduledMeterReadingBackGroundWorkService.cs | 34 + .../Workers/SubscriberFifteenMinuteWorker.cs | 48 - .../Workers/SubscriberFiveMinuteWorker.cs | 42 - .../Workers/SubscriberOneMinuteWorker.cs | 44 - .../CollectBusDbMigratorModule.cs | 4 +- .../DbMigratorHostedService.cs | 11 +- .../JiShe.CollectBus.DbMigrator.csproj | 17 +- .../appsettings.json | 10 + .../Ammeters/ElectricityMeter.cs | 41 - .../Ammeters/ElectricityMeterTreeModel.cs | 38 - .../CollectBusDomainModule.cs | 2 + .../JiShe.CollectBus.Domain/GlobalUsings.cs | 8 + .../AmmeterAutoValveControlSetting.cs | 1 + .../IotSystems/Ammeters/AmmeterInfo.cs | 4 +- .../IotSystems/Devices/DeviceInfo.cs | 174 --- .../Devices/DeviceTableModelDataInfo.cs | 23 - .../Devices/DeviceTreeModelDataInfo.cs | 22 - ...ScheduledMeterReadingIssuedEventMessage.cs | 38 - .../MeterReadingRecords.cs | 1 + .../MeterReadingTelemetryPacketInfo.cs | 191 --- .../IotSystems/Watermeter/WatermeterInfo.cs | 2 + .../JiShe.CollectBus.Domain.csproj | 21 +- .../Protocol3761/Dto/AnalysisBaseDto.cs | 3 +- .../CollectBusDbContext.cs | 39 - .../CollectBusDbContextFactory.cs | 33 - ...CollectBusEfCoreEntityExtensionMappings.cs | 44 - .../CollectBusEntityFrameworkCoreModule.cs | 50 - ...FrameworkCoreCollectBusDbSchemaMigrator.cs | 33 - .../FodyWeavers.xml | 3 - ...iShe.CollectBus.EntityFrameworkCore.abppkg | 3 - ...iShe.CollectBus.EntityFrameworkCore.csproj | 40 - .../Properties/AssemblyInfo.cs | 2 - ...tBusMigrationApplicationContractsModule.cs | 15 - .../CollectBusMigrationRemoteServiceConsts.cs | 8 - .../DataMigration/IDataMigrationService.cs | 20 - .../Options/DataMigrationOptions.cs | 39 - .../FodyWeavers.xml | 3 - .../ICollectWorker.cs | 12 - ...Bus.Migration.Application.Contracts.csproj | 34 - .../CollectBusMigrationAppService.cs | 20 - .../CollectBusMigrationApplicationModule.cs | 70 - ...ctBusMigrationlicationAutoMapperProfile.cs | 13 - .../DataMigration/DataMigrationService.cs | 152 --- .../FodyWeavers.xml | 3 - ...he.CollectBus.Migration.Application.csproj | 35 - ...CollectBusEPOApplicationContractsModule.cs | 16 - .../CollectBusEPODtoExtensions.cs | 25 - .../Files/FileUploadOutputDto.cs | 7 - .../GlobalUsings.cs | 11 - ...CollectBusEPO.Application.Contracts.csproj | 17 - .../Jobs/IRecurringJob.cs | 11 - ...llectBusEPOPermissionDefinitionProvider.cs | 17 - .../Permissions/CollectBusEPOPermissions.cs | 15 - .../CollectBusEPOAppService.cs | 30 - .../CollectBusEPOApplicationModule.cs | 32 - .../EntityHandler.cs | 21 - .../GlobalUsings.cs | 9 - .../JiShe.CollectBusEPO.Application.csproj | 17 - .../Properties/AssemblyInfo.cs | 2 - .../EntityMemberInfo.cs | 60 - .../EntityTypeEnum.cs | 27 - .../ISourceEntityAccessor.cs | 46 - .../JiShe.CollectBus.Analyzers.Shared.csproj | 7 - .../SourceAnalyzersAttribute.cs | 19 - .../SourceEntityAccessorFactory2.cs | 1 - .../Consts/CommonConst.cs | 41 - .../Consts/IOTDBDataTypeConst.cs | 39 - .../Consts/MeteringPortConst.cs | 24 - .../Consts/ProtocolConst.cs | 179 --- .../Consts/RedisConst.cs | 76 -- .../DeviceGroupBalanceControl.cs | 431 ------- .../Encrypt/EncryptUtil.cs | 65 - .../Enums/MeterTypeEnum.cs | 58 - .../Extensions/DateTimeOffsetExtensions.cs | 46 - .../Extensions/EnumExtensions.cs | 83 -- .../Extensions/EnumerableExtensions.cs | 114 -- .../Extensions/LockExtensions.cs | 68 - .../Extensions/ProtocolConstExtensions.cs | 69 - .../Helpers/BusJsonSerializer.cs | 204 --- .../Helpers/CommonHelper.cs | 936 -------------- .../Helpers/SelectResult.cs | 35 - .../Helpers/TimestampHelper.cs | 68 - .../JiShe.CollectBus.Common.csproj | 27 +- .../Models/DeviceCacheBasicModel.cs | 56 - .../Models/ServerApplicationOptions.cs | 53 - .../CollectBusDomainSharedModule.cs | 2 + .../DynamicModule/DynamicModuleManager.cs | 28 +- .../DynamicModule/IDynamicModuleManager.cs | 2 + .../JiShe.CollectBus.Domain.Shared.csproj | 16 +- .../CollectBusHostModule.Configure.cs | 55 +- .../CollectBusHostModule.cs | 26 +- .../HealthChecks/CassandraHealthCheck.cs | 47 +- .../HealthChecks/IoTDBHealthCheck.cs | 5 - .../JiShe.CollectBus.Host.csproj | 60 +- .../appsettings.Production.json | 92 +- web/JiShe.CollectBus.Host/appsettings.json | 79 +- .../JiShe.CollectBus.HttpApi.csproj | 6 +- .../CollectBusMigrationHostConst.cs | 40 - ...CollectBusMigrationHostModule.Configure.cs | 235 ---- .../CollectBusMigrationHostModule.cs | 110 -- .../Controllers/HomeController.cs | 13 - .../CustomApplicationBuilderExtensions.cs | 10 - .../ServiceCollectionExtensions.cs | 49 - .../HealthChecks/HealthCheckResponse.cs | 25 - .../HealthChecks/IoTDBHealthCheck.cs | 47 - .../JiShe.CollectBus.Migration.Host.csproj | 70 - .../Pages/Monitor.cshtml | 188 --- .../Pages/Monitor.cshtml.cs | 13 - .../Program.cs | 47 - .../Properties/launchSettings.json | 12 - .../Swaggers/EnumSchemaFilter.cs | 24 - .../Swaggers/HiddenAbpDefaultApiFilter.cs | 54 - .../Swaggers/SwaggerConfig.cs | 11 - .../appsettings.Development.json | 8 - .../appsettings.Production.json | 84 -- .../appsettings.json | 158 --- .../wwwroot/images/cap.png | Bin 11113 -> 0 bytes .../wwwroot/images/hangfire.png | Bin 36278 -> 0 bytes .../wwwroot/images/miniprofiler.png | Bin 22147 -> 0 bytes .../wwwroot/images/more.png | Bin 20558 -> 0 bytes .../wwwroot/images/swagger.png | Bin 38571 -> 0 bytes .../libs/bootstrap/css/bootstrap.min.css | 6 - .../CollectBusController.cs | 12 - .../CollectBusMigrationHttpApiModule.cs | 40 - .../FodyWeavers.xml | 3 - .../JiShe.CollectBus.Migration.HttpApi.csproj | 30 - .../.config/dotnet-tools.json | 13 - .../CollectBusEPOHttpApiHostConst.cs | 45 - ...ollectBusEPOHttpApiHostModule.Configure.cs | 179 --- .../CollectBusEPOHttpApiHostModule.cs | 73 -- .../Controllers/HomeController.cs | 31 - .../Docker/Dockerfile | 47 - .../Docker/dockerbuild_dev.ps1 | 26 - .../Docker/dockerbuild_pro.ps1 | 26 - .../Docker/sources.list | 10 - .../Extensions/HealthCheck/Extensions.cs | 88 -- .../HealthCheck/HealthCheckResponse.cs | 25 - .../GlobalUsings.cs | 32 - .../JiShe.CollectBusEPO.HttpApi.Host.csproj | 62 - .../Pages/Monitor.cshtml | 193 --- .../Pages/Monitor.cshtml.cs | 12 - .../Program.cs | 39 - .../Properties/launchSettings.json | 13 - .../Startup.cs | 23 - .../Swaggers/EnumSchemaFilter.cs | 24 - .../Swaggers/HiddenAbpDefaultApiFilter.cs | 54 - .../Swaggers/SwaggerConfig.cs | 11 - .../appsettings.Development.json | 3 - .../appsettings.Production.json | 3 - .../appsettings.json | 104 -- .../tempkey.jwk | 1 - .../tempkey.rsa | 1 - .../wwwroot/TemporaryFiles/.gitkeep | 0 .../wwwroot/images/cap.png | Bin 11113 -> 0 bytes .../wwwroot/images/hangfire.png | Bin 36278 -> 0 bytes .../wwwroot/images/healthchecks.png | Bin 8990 -> 0 bytes .../wwwroot/images/miniprofiler.png | Bin 22147 -> 0 bytes .../wwwroot/images/more.png | Bin 20558 -> 0 bytes .../wwwroot/images/swagger.png | Bin 38571 -> 0 bytes .../CollectBusEPOHttpApiModule.cs | 39 - .../Controllers/CollectBusEPOController.cs | 14 - .../GlobalUsings.cs | 6 - .../JiShe.CollectBusEPO.HttpApi.csproj | 19 - 397 files changed, 1651 insertions(+), 18041 deletions(-) create mode 100644 Directory.Build.JiShe.targets create mode 100644 Directory.Build.Microsoft.targets create mode 100644 Directory.Build.Volo.targets create mode 100644 Directory.Build.props create mode 100644 Directory.Build.targets create mode 100644 icon.png delete mode 100644 modules/JiShe.CollectBus.Analyzers/ComplexTypeSourceAnalyzers.cs delete mode 100644 modules/JiShe.CollectBus.Analyzers/JiShe.CollectBus.Analyzers.csproj delete mode 100644 modules/JiShe.CollectBus.Cassandra/CassandraConfig.cs delete mode 100644 modules/JiShe.CollectBus.Cassandra/CassandraProvider.cs delete mode 100644 modules/JiShe.CollectBus.Cassandra/CassandraRepository.cs delete mode 100644 modules/JiShe.CollectBus.Cassandra/CollectBusCassandraModule.cs delete mode 100644 modules/JiShe.CollectBus.Cassandra/Extensions/ServiceCollectionExtensions.cs delete mode 100644 modules/JiShe.CollectBus.Cassandra/Extensions/SessionExtension.cs delete mode 100644 modules/JiShe.CollectBus.Cassandra/ICassandraProvider.cs delete mode 100644 modules/JiShe.CollectBus.Cassandra/ICassandraRepository.cs delete mode 100644 modules/JiShe.CollectBus.Cassandra/JiShe.CollectBus.Cassandra.csproj delete mode 100644 modules/JiShe.CollectBus.FreeRedis/CollectBusFreeRedisModule.cs delete mode 100644 modules/JiShe.CollectBus.FreeRedis/FreeRedisProvider.cs delete mode 100644 modules/JiShe.CollectBus.FreeRedis/IFreeRedisProvider.cs delete mode 100644 modules/JiShe.CollectBus.FreeRedis/JiShe.CollectBus.FreeRedis.csproj delete mode 100644 modules/JiShe.CollectBus.FreeRedis/Options/FreeRedisOptions.cs delete mode 100644 modules/JiShe.CollectBus.FreeSql/CollectBusFreeSqlModule.cs delete mode 100644 modules/JiShe.CollectBus.FreeSql/DbEnum.cs delete mode 100644 modules/JiShe.CollectBus.FreeSql/FreeSqlProvider.cs delete mode 100644 modules/JiShe.CollectBus.FreeSql/IFreeSqlProvider.cs delete mode 100644 modules/JiShe.CollectBus.FreeSql/JiShe.CollectBus.FreeSql.csproj delete mode 100644 modules/JiShe.CollectBus.IoTDB/Attributes/ATTRIBUTEColumnAttribute.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Attributes/FIELDColumnAttribute.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Attributes/IgnoreInitTableAttribute.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Attributes/SingleMeasuringAttribute.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Attributes/TAGColumnAttribute.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Attributes/TableNameOrTreePathAttribute.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/CollectBusIoTDBModule.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Context/IoTDBRuntimeContext.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Exceptions/IoTException.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBProvider.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBSessionFactory.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBSessionPool.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj delete mode 100644 modules/JiShe.CollectBus.IoTDB/Model/IoTEntity.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Model/TreeModelSingleMeasuringEntity.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Options/IoTDBOptions.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Options/IoTDBQueryOptions.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Options/QueryCondition.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Provider/DeviceMetadata.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Provider/DevicePathBuilder.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Provider/IoTDBProvider.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Provider/IoTDBSessionFactory.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Provider/SessionPoolAdapter.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/Provider/TableSessionPoolAdapter.cs delete mode 100644 modules/JiShe.CollectBus.IoTDB/SourceEntityAccessorFactory3.cs delete mode 100644 modules/JiShe.CollectBus.Kafka.Test/ConsoleApplicationBuilder.cs delete mode 100644 modules/JiShe.CollectBus.Kafka.Test/JiShe.CollectBus.Kafka.Test.csproj delete mode 100644 modules/JiShe.CollectBus.Kafka.Test/KafkaProduceBenchmark.cs delete mode 100644 modules/JiShe.CollectBus.Kafka.Test/KafkaSubscribeTest.cs delete mode 100644 modules/JiShe.CollectBus.Kafka.Test/Lib/JiShe.CollectBus.Common.dll delete mode 100644 modules/JiShe.CollectBus.Kafka.Test/Lib/JiShe.CollectBus.Kafka.dll delete mode 100644 modules/JiShe.CollectBus.Kafka.Test/Program.cs delete mode 100644 modules/JiShe.CollectBus.Kafka.Test/Properties/launchSettings.json delete mode 100644 modules/JiShe.CollectBus.Kafka.Test/appsettings.json delete mode 100644 modules/JiShe.CollectBus.Kafka/AdminClient/AdminClientService.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/AdminClient/IAdminClientService.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Attributes/KafkaSubscribeAttribute.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Attributes/TopicAttribute.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/CollectBusKafkaModule.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Consumer/ConsumerService.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Consumer/IConsumerService.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/HostedService.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Internal/HeadersFilter.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Internal/IKafkaSubscribe.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Internal/ISubscribeAck.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Internal/KafkaOptionConfig.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Internal/KafkaPollyPipeline.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Internal/KafkaTaskScheduler.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Internal/ReflectionHelper.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Internal/SubscribeResult.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/JiShe.CollectBus.Kafka.csproj delete mode 100644 modules/JiShe.CollectBus.Kafka/KafkaSubscribeExtensions.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Producer/IProducerService.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Producer/ProducerService.cs delete mode 100644 modules/JiShe.CollectBus.Kafka/Serialization/JsonSerializer.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/FodyWeavers.xml delete mode 100644 modules/JiShe.CollectBus.MongoDB/JiShe.CollectBus.MongoDB.abppkg delete mode 100644 modules/JiShe.CollectBus.MongoDB/JiShe.CollectBus.MongoDB.csproj delete mode 100644 modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusDbSchemaMigrator.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContext.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContextExtensions.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbModule.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/MongoDB/ICollectBusMongoDbContext.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/ILogRecordRepository.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/LogRecordRepository.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/IMeterReadingRecordRepository.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/ShardingStrategy/HourShardingStrategy.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IHourShardingStrategy.cs delete mode 100644 modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IShardingStrategy.cs delete mode 100644 protocols/JiShe.CollectBus.Protocol.Contracts/ProtocolPools/IPluginContainer.cs delete mode 100644 protocols/JiShe.CollectBus.Protocol.Contracts/ProtocolPools/PluginContainer.cs create mode 100644 protocols/JiShe.CollectBus.Protocol.T1882018/GlobalUsings.cs create mode 100644 protocols/JiShe.CollectBus.Protocol.T37612012/GlobalUsings.cs create mode 100644 protocols/JiShe.CollectBus.Protocol.T6452007/GlobalUsings.cs create mode 100644 protocols/JiShe.CollectBus.Protocol.Test/TestServer.cs create mode 100644 protocols/JiShe.CollectBus.Protocol/GlobalUsings.cs create mode 100644 services/JiShe.CollectBus.Application.Contracts/GlobalUsings.cs delete mode 100644 services/JiShe.CollectBus.Application.Contracts/RedisDataCache/IRedisDataCacheService.cs create mode 100644 services/JiShe.CollectBus.Application.Contracts/Subscribers/IServiceCommunicationChannelSubscriberService.cs create mode 100644 services/JiShe.CollectBus.Application/GlobalUsings.cs delete mode 100644 services/JiShe.CollectBus.Application/Mappers/CollectBusMapping.cs delete mode 100644 services/JiShe.CollectBus.Application/RedisDataCache/RedisDataCacheService.cs create mode 100644 services/JiShe.CollectBus.Application/Subscribers/ServiceCommunicationChannelSubscriberService.cs delete mode 100644 services/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs delete mode 100644 services/JiShe.CollectBus.Application/Workers/CreateToBeIssueTaskWorker.cs delete mode 100644 services/JiShe.CollectBus.Application/Workers/DataDetectionFifteenMinuteWorker.cs delete mode 100644 services/JiShe.CollectBus.Application/Workers/EpiCollectWorker.cs create mode 100644 services/JiShe.CollectBus.Application/Workers/ScheduledMeterReadingBackGroundWorkService.cs delete mode 100644 services/JiShe.CollectBus.Application/Workers/SubscriberFifteenMinuteWorker.cs delete mode 100644 services/JiShe.CollectBus.Application/Workers/SubscriberFiveMinuteWorker.cs delete mode 100644 services/JiShe.CollectBus.Application/Workers/SubscriberOneMinuteWorker.cs delete mode 100644 services/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs delete mode 100644 services/JiShe.CollectBus.Domain/Ammeters/ElectricityMeterTreeModel.cs create mode 100644 services/JiShe.CollectBus.Domain/GlobalUsings.cs delete mode 100644 services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceInfo.cs delete mode 100644 services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceTableModelDataInfo.cs delete mode 100644 services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceTreeModelDataInfo.cs delete mode 100644 services/JiShe.CollectBus.Domain/IotSystems/MessageIssueds/ScheduledMeterReadingIssuedEventMessage.cs delete mode 100644 services/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingTelemetryPacketInfo.cs delete mode 100644 services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusDbContext.cs delete mode 100644 services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusDbContextFactory.cs delete mode 100644 services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusEfCoreEntityExtensionMappings.cs delete mode 100644 services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusEntityFrameworkCoreModule.cs delete mode 100644 services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/EntityFrameworkCoreCollectBusDbSchemaMigrator.cs delete mode 100644 services/JiShe.CollectBus.EntityFrameworkCore/FodyWeavers.xml delete mode 100644 services/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.abppkg delete mode 100644 services/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.csproj delete mode 100644 services/JiShe.CollectBus.EntityFrameworkCore/Properties/AssemblyInfo.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application.Contracts/CollectBusMigrationApplicationContractsModule.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application.Contracts/CollectBusMigrationRemoteServiceConsts.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application.Contracts/DataMigration/IDataMigrationService.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application.Contracts/DataMigration/Options/DataMigrationOptions.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application.Contracts/FodyWeavers.xml delete mode 100644 services/JiShe.CollectBus.Migration.Application.Contracts/ICollectWorker.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application.Contracts/JiShe.CollectBus.Migration.Application.Contracts.csproj delete mode 100644 services/JiShe.CollectBus.Migration.Application/CollectBusMigrationAppService.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application/CollectBusMigrationApplicationModule.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application/CollectBusMigrationlicationAutoMapperProfile.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application/DataMigration/DataMigrationService.cs delete mode 100644 services/JiShe.CollectBus.Migration.Application/FodyWeavers.xml delete mode 100644 services/JiShe.CollectBus.Migration.Application/JiShe.CollectBus.Migration.Application.csproj delete mode 100644 services/JiShe.CollectBusEPO.Application.Contracts/CollectBusEPOApplicationContractsModule.cs delete mode 100644 services/JiShe.CollectBusEPO.Application.Contracts/CollectBusEPODtoExtensions.cs delete mode 100644 services/JiShe.CollectBusEPO.Application.Contracts/Files/FileUploadOutputDto.cs delete mode 100644 services/JiShe.CollectBusEPO.Application.Contracts/GlobalUsings.cs delete mode 100644 services/JiShe.CollectBusEPO.Application.Contracts/JiShe.CollectBusEPO.Application.Contracts.csproj delete mode 100644 services/JiShe.CollectBusEPO.Application.Contracts/Jobs/IRecurringJob.cs delete mode 100644 services/JiShe.CollectBusEPO.Application.Contracts/Permissions/CollectBusEPOPermissionDefinitionProvider.cs delete mode 100644 services/JiShe.CollectBusEPO.Application.Contracts/Permissions/CollectBusEPOPermissions.cs delete mode 100644 services/JiShe.CollectBusEPO.Application/CollectBusEPOAppService.cs delete mode 100644 services/JiShe.CollectBusEPO.Application/CollectBusEPOApplicationModule.cs delete mode 100644 services/JiShe.CollectBusEPO.Application/EntityHandler.cs delete mode 100644 services/JiShe.CollectBusEPO.Application/GlobalUsings.cs delete mode 100644 services/JiShe.CollectBusEPO.Application/JiShe.CollectBusEPO.Application.csproj delete mode 100644 services/JiShe.CollectBusEPO.Application/Properties/AssemblyInfo.cs delete mode 100644 shared/JiShe.CollectBus.Analyzers.Shared/EntityMemberInfo.cs delete mode 100644 shared/JiShe.CollectBus.Analyzers.Shared/EntityTypeEnum.cs delete mode 100644 shared/JiShe.CollectBus.Analyzers.Shared/ISourceEntityAccessor.cs delete mode 100644 shared/JiShe.CollectBus.Analyzers.Shared/JiShe.CollectBus.Analyzers.Shared.csproj delete mode 100644 shared/JiShe.CollectBus.Analyzers.Shared/SourceAnalyzersAttribute.cs delete mode 100644 shared/JiShe.CollectBus.Analyzers.Shared/SourceEntityAccessorFactory2.cs delete mode 100644 shared/JiShe.CollectBus.Common/Consts/CommonConst.cs delete mode 100644 shared/JiShe.CollectBus.Common/Consts/IOTDBDataTypeConst.cs delete mode 100644 shared/JiShe.CollectBus.Common/Consts/MeteringPortConst.cs delete mode 100644 shared/JiShe.CollectBus.Common/Consts/ProtocolConst.cs delete mode 100644 shared/JiShe.CollectBus.Common/Consts/RedisConst.cs delete mode 100644 shared/JiShe.CollectBus.Common/DeviceBalanceControl/DeviceGroupBalanceControl.cs delete mode 100644 shared/JiShe.CollectBus.Common/Encrypt/EncryptUtil.cs delete mode 100644 shared/JiShe.CollectBus.Common/Enums/MeterTypeEnum.cs delete mode 100644 shared/JiShe.CollectBus.Common/Extensions/DateTimeOffsetExtensions.cs delete mode 100644 shared/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs delete mode 100644 shared/JiShe.CollectBus.Common/Extensions/EnumerableExtensions.cs delete mode 100644 shared/JiShe.CollectBus.Common/Extensions/LockExtensions.cs delete mode 100644 shared/JiShe.CollectBus.Common/Extensions/ProtocolConstExtensions.cs delete mode 100644 shared/JiShe.CollectBus.Common/Helpers/BusJsonSerializer.cs delete mode 100644 shared/JiShe.CollectBus.Common/Helpers/CommonHelper.cs delete mode 100644 shared/JiShe.CollectBus.Common/Helpers/SelectResult.cs delete mode 100644 shared/JiShe.CollectBus.Common/Helpers/TimestampHelper.cs delete mode 100644 shared/JiShe.CollectBus.Common/Models/DeviceCacheBasicModel.cs delete mode 100644 shared/JiShe.CollectBus.Common/Models/ServerApplicationOptions.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostConst.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostModule.Configure.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostModule.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/Controllers/HomeController.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/Extensions/CustomApplicationBuilderExtensions.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/Extensions/ServiceCollections/ServiceCollectionExtensions.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/HealthChecks/HealthCheckResponse.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/HealthChecks/IoTDBHealthCheck.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/JiShe.CollectBus.Migration.Host.csproj delete mode 100644 web/JiShe.CollectBus.Migration.Host/Pages/Monitor.cshtml delete mode 100644 web/JiShe.CollectBus.Migration.Host/Pages/Monitor.cshtml.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/Program.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/Properties/launchSettings.json delete mode 100644 web/JiShe.CollectBus.Migration.Host/Swaggers/EnumSchemaFilter.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/Swaggers/HiddenAbpDefaultApiFilter.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/Swaggers/SwaggerConfig.cs delete mode 100644 web/JiShe.CollectBus.Migration.Host/appsettings.Development.json delete mode 100644 web/JiShe.CollectBus.Migration.Host/appsettings.Production.json delete mode 100644 web/JiShe.CollectBus.Migration.Host/appsettings.json delete mode 100644 web/JiShe.CollectBus.Migration.Host/wwwroot/images/cap.png delete mode 100644 web/JiShe.CollectBus.Migration.Host/wwwroot/images/hangfire.png delete mode 100644 web/JiShe.CollectBus.Migration.Host/wwwroot/images/miniprofiler.png delete mode 100644 web/JiShe.CollectBus.Migration.Host/wwwroot/images/more.png delete mode 100644 web/JiShe.CollectBus.Migration.Host/wwwroot/images/swagger.png delete mode 100644 web/JiShe.CollectBus.Migration.Host/wwwroot/libs/bootstrap/css/bootstrap.min.css delete mode 100644 web/JiShe.CollectBus.Migration.HttpApi/CollectBusController.cs delete mode 100644 web/JiShe.CollectBus.Migration.HttpApi/CollectBusMigrationHttpApiModule.cs delete mode 100644 web/JiShe.CollectBus.Migration.HttpApi/FodyWeavers.xml delete mode 100644 web/JiShe.CollectBus.Migration.HttpApi/JiShe.CollectBus.Migration.HttpApi.csproj delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/.config/dotnet-tools.json delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostConst.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostModule.Configure.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostModule.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Controllers/HomeController.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Docker/Dockerfile delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Docker/dockerbuild_dev.ps1 delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Docker/dockerbuild_pro.ps1 delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Docker/sources.list delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Extensions/HealthCheck/Extensions.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Extensions/HealthCheck/HealthCheckResponse.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/GlobalUsings.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/JiShe.CollectBusEPO.HttpApi.Host.csproj delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Pages/Monitor.cshtml delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Pages/Monitor.cshtml.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Program.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Properties/launchSettings.json delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Startup.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/EnumSchemaFilter.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/HiddenAbpDefaultApiFilter.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/SwaggerConfig.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.Development.json delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.Production.json delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.json delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/tempkey.jwk delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/tempkey.rsa delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/TemporaryFiles/.gitkeep delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/cap.png delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/hangfire.png delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/healthchecks.png delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/miniprofiler.png delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/more.png delete mode 100644 web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/swagger.png delete mode 100644 web/JiShe.CollectBusEPO.HttpApi/CollectBusEPOHttpApiModule.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi/Controllers/CollectBusEPOController.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi/GlobalUsings.cs delete mode 100644 web/JiShe.CollectBusEPO.HttpApi/JiShe.CollectBusEPO.HttpApi.csproj diff --git a/Directory.Build.JiShe.targets b/Directory.Build.JiShe.targets new file mode 100644 index 0000000..c4b9ff0 --- /dev/null +++ b/Directory.Build.JiShe.targets @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Directory.Build.Microsoft.targets b/Directory.Build.Microsoft.targets new file mode 100644 index 0000000..9e2203b --- /dev/null +++ b/Directory.Build.Microsoft.targets @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Directory.Build.Volo.targets b/Directory.Build.Volo.targets new file mode 100644 index 0000000..17d91a7 --- /dev/null +++ b/Directory.Build.Volo.targets @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..64df1da --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,12 @@ + + + + + 1.0.5.08 + + 9.1.1 + + + 9.0.0 + + \ No newline at end of file diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000..4333da3 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,155 @@ + + + + + + + + latest + 1.0.1 + JiShe Service Pro + $(NoWarn);CS1591;CS0436;NU1504 + app + true + 湖南集社电子技术有限公司 + true + icon.png + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 8030845..95839ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,21 @@ -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base WORKDIR /app EXPOSE 8080 EXPOSE 10500 ENV TZ=Asia/Shanghai ENV ASPNETCORE_ENVIRONMENT=Production -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build # WORKDIR /src COPY ["JiShe.CollectBus.Main.sln", "."] COPY ["common.props", "."] +COPY ["Directory.Build.JiShe.targets", "."] +COPY ["Directory.Build.Microsoft.targets", "."] +COPY ["Directory.Build.props", "."] +COPY ["Directory.Build.targets", "."] +COPY ["Directory.Build.Volo.targets", "."] COPY ["NuGet.Config", "."] COPY ["web/", "web/"] -COPY ["modules/", "modules/"] COPY ["services/", "services/"] COPY ["shared/", "shared/"] COPY ["protocols/", "protocols/"] diff --git a/JiShe.CollectBus.Main.sln b/JiShe.CollectBus.Main.sln index 46d2db3..3fa5f95 100644 --- a/JiShe.CollectBus.Main.sln +++ b/JiShe.CollectBus.Main.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Applicatio EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Application", "services\JiShe.CollectBus.Application\JiShe.CollectBus.Application.csproj", "{78040F9E-3501-4A40-82DF-00A597710F35}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.MongoDB", "modules\JiShe.CollectBus.MongoDB\JiShe.CollectBus.MongoDB.csproj", "{F1C58097-4C08-4D88-8976-6B3389391481}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.HttpApi", "web\JiShe.CollectBus.HttpApi\JiShe.CollectBus.HttpApi.csproj", "{077AA5F8-8B61-420C-A6B5-0150A66FDB34}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Host", "web\JiShe.CollectBus.Host\JiShe.CollectBus.Host.csproj", "{35829A15-4127-4F69-8BDE-9405DEAACA9A}" @@ -21,23 +19,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Common", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.DbMigrator", "services\JiShe.CollectBus.DbMigrator\JiShe.CollectBus.DbMigrator.csproj", "{8BA01C3D-297D-42DF-BD63-EF07202A0A67}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.FreeSql", "modules\JiShe.CollectBus.FreeSql\JiShe.CollectBus.FreeSql.csproj", "{FE0457D9-4038-4A17-8808-DCAD06CFC0A0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.FreeRedis", "modules\JiShe.CollectBus.FreeRedis\JiShe.CollectBus.FreeRedis.csproj", "{C06C4082-638F-2996-5FED-7784475766C1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Kafka", "modules\JiShe.CollectBus.Kafka\JiShe.CollectBus.Kafka.csproj", "{F0288175-F0EC-48BD-945F-CF1512850943}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.IoTDB", "modules\JiShe.CollectBus.IoTDB\JiShe.CollectBus.IoTDB.csproj", "{A3F3C092-0A25-450B-BF6A-5983163CBEF5}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Protocol.Test", "protocols\JiShe.CollectBus.Protocol.Test\JiShe.CollectBus.Protocol.Test.csproj", "{A377955E-7EA1-6F29-8CF7-774569E93925}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Cassandra", "modules\JiShe.CollectBus.Cassandra\JiShe.CollectBus.Cassandra.csproj", "{443B4549-0AC0-4493-8F3E-49C83225DD76}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1.Web", "1.Web", "{A02F7D8A-04DC-44D6-94D4-3F65712D6B94}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4.Modules", "4.Modules", "{2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5.Protocols", "5.Protocols", "{3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4.Protocols", "4.Protocols", "{3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2.Services", "2.Services", "{BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}" EndProject @@ -54,6 +40,11 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0.Docs", "0.Docs", "{D8346C4C-55B8-43E8-A6B8-E59D56FE6D92}" ProjectSection(SolutionItems) = preProject .gitignore = .gitignore + Directory.Build.JiShe.targets = Directory.Build.JiShe.targets + Directory.Build.Microsoft.targets = Directory.Build.Microsoft.targets + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + Directory.Build.Volo.targets = Directory.Build.Volo.targets Dockerfile = Dockerfile NuGet.Config = NuGet.Config PackageAndPublish.bat = PackageAndPublish.bat @@ -61,10 +52,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0.Docs", "0.Docs", "{D8346C Temp.Dockerfile = Temp.Dockerfile EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Analyzers.Shared", "shared\JiShe.CollectBus.Analyzers.Shared\JiShe.CollectBus.Analyzers.Shared.csproj", "{DD68F314-BC66-5601-B094-B1A7BE93F4E0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Analyzers", "modules\JiShe.CollectBus.Analyzers\JiShe.CollectBus.Analyzers.csproj", "{EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,10 +74,6 @@ Global {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.Build.0 = Debug|Any CPU {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.ActiveCfg = Release|Any CPU {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.Build.0 = Release|Any CPU - {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.Build.0 = Release|Any CPU {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.Build.0 = Debug|Any CPU {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -107,30 +90,10 @@ Global {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Debug|Any CPU.Build.0 = Debug|Any CPU {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Release|Any CPU.ActiveCfg = Release|Any CPU {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Release|Any CPU.Build.0 = Release|Any CPU - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Release|Any CPU.Build.0 = Release|Any CPU - {C06C4082-638F-2996-5FED-7784475766C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C06C4082-638F-2996-5FED-7784475766C1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C06C4082-638F-2996-5FED-7784475766C1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C06C4082-638F-2996-5FED-7784475766C1}.Release|Any CPU.Build.0 = Release|Any CPU - {F0288175-F0EC-48BD-945F-CF1512850943}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0288175-F0EC-48BD-945F-CF1512850943}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0288175-F0EC-48BD-945F-CF1512850943}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0288175-F0EC-48BD-945F-CF1512850943}.Release|Any CPU.Build.0 = Release|Any CPU - {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Release|Any CPU.Build.0 = Release|Any CPU {A377955E-7EA1-6F29-8CF7-774569E93925}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A377955E-7EA1-6F29-8CF7-774569E93925}.Debug|Any CPU.Build.0 = Debug|Any CPU {A377955E-7EA1-6F29-8CF7-774569E93925}.Release|Any CPU.ActiveCfg = Release|Any CPU {A377955E-7EA1-6F29-8CF7-774569E93925}.Release|Any CPU.Build.0 = Release|Any CPU - {443B4549-0AC0-4493-8F3E-49C83225DD76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {443B4549-0AC0-4493-8F3E-49C83225DD76}.Debug|Any CPU.Build.0 = Debug|Any CPU - {443B4549-0AC0-4493-8F3E-49C83225DD76}.Release|Any CPU.ActiveCfg = Release|Any CPU - {443B4549-0AC0-4493-8F3E-49C83225DD76}.Release|Any CPU.Build.0 = Release|Any CPU {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Debug|Any CPU.Build.0 = Debug|Any CPU {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -147,14 +110,6 @@ Global {75B7D419-C261-577D-58D6-AA3ACED9129F}.Debug|Any CPU.Build.0 = Debug|Any CPU {75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.ActiveCfg = Release|Any CPU {75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.Build.0 = Release|Any CPU - {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Release|Any CPU.Build.0 = Release|Any CPU - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -164,23 +119,15 @@ Global {F2840BC7-0188-4606-9126-DADD0F5ABF7A} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} {BD65D04F-08D5-40C1-8C24-77CA0BACB877} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} {78040F9E-3501-4A40-82DF-00A597710F35} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} - {F1C58097-4C08-4D88-8976-6B3389391481} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} {077AA5F8-8B61-420C-A6B5-0150A66FDB34} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94} {35829A15-4127-4F69-8BDE-9405DEAACA9A} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94} {AD2F1928-4411-4511-B564-5FB996EC08B9} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B} {8BA01C3D-297D-42DF-BD63-EF07202A0A67} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} - {C06C4082-638F-2996-5FED-7784475766C1} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} - {F0288175-F0EC-48BD-945F-CF1512850943} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} - {A3F3C092-0A25-450B-BF6A-5983163CBEF5} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} {A377955E-7EA1-6F29-8CF7-774569E93925} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} - {443B4549-0AC0-4493-8F3E-49C83225DD76} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} {430D298B-377E-49B8-83AA-ADC7C0EBDB0F} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} {8A61DF78-069B-40B5-8811-614E2960443E} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} {E27377CC-E2D3-4237-060F-96EA214D3129} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} {75B7D419-C261-577D-58D6-AA3ACED9129F} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} - {DD68F314-BC66-5601-B094-B1A7BE93F4E0} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B} - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} diff --git a/JiShe.CollectBus.sln b/JiShe.CollectBus.sln index c32791b..ffc3d0d 100644 --- a/JiShe.CollectBus.sln +++ b/JiShe.CollectBus.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Applicatio EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Application", "services\JiShe.CollectBus.Application\JiShe.CollectBus.Application.csproj", "{78040F9E-3501-4A40-82DF-00A597710F35}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.MongoDB", "modules\JiShe.CollectBus.MongoDB\JiShe.CollectBus.MongoDB.csproj", "{F1C58097-4C08-4D88-8976-6B3389391481}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.HttpApi", "web\JiShe.CollectBus.HttpApi\JiShe.CollectBus.HttpApi.csproj", "{077AA5F8-8B61-420C-A6B5-0150A66FDB34}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Host", "web\JiShe.CollectBus.Host\JiShe.CollectBus.Host.csproj", "{35829A15-4127-4F69-8BDE-9405DEAACA9A}" @@ -21,30 +19,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Common", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.DbMigrator", "services\JiShe.CollectBus.DbMigrator\JiShe.CollectBus.DbMigrator.csproj", "{8BA01C3D-297D-42DF-BD63-EF07202A0A67}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.FreeSql", "modules\JiShe.CollectBus.FreeSql\JiShe.CollectBus.FreeSql.csproj", "{FE0457D9-4038-4A17-8808-DCAD06CFC0A0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.FreeRedis", "modules\JiShe.CollectBus.FreeRedis\JiShe.CollectBus.FreeRedis.csproj", "{C06C4082-638F-2996-5FED-7784475766C1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Kafka", "modules\JiShe.CollectBus.Kafka\JiShe.CollectBus.Kafka.csproj", "{F0288175-F0EC-48BD-945F-CF1512850943}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.IoTDB", "modules\JiShe.CollectBus.IoTDB\JiShe.CollectBus.IoTDB.csproj", "{A3F3C092-0A25-450B-BF6A-5983163CBEF5}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Protocol.Test", "protocols\JiShe.CollectBus.Protocol.Test\JiShe.CollectBus.Protocol.Test.csproj", "{A377955E-7EA1-6F29-8CF7-774569E93925}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Cassandra", "modules\JiShe.CollectBus.Cassandra\JiShe.CollectBus.Cassandra.csproj", "{443B4549-0AC0-4493-8F3E-49C83225DD76}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1.Web", "1.Web", "{A02F7D8A-04DC-44D6-94D4-3F65712D6B94}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4.Modules", "4.Modules", "{2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5.Protocols", "5.Protocols", "{3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4.Protocols", "4.Protocols", "{3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2.Services", "2.Services", "{BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3.Shared", "3.Shared", "{EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Kafka.Test", "modules\JiShe.CollectBus.Kafka.Test\JiShe.CollectBus.Kafka.Test.csproj", "{6D6A2A58-7406-9C8C-7B23-3E442CCE3E6B}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol.T1882018", "protocols\JiShe.CollectBus.Protocol.T1882018\JiShe.CollectBus.Protocol.T1882018.csproj", "{430D298B-377E-49B8-83AA-ADC7C0EBDB0F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol.T37612012", "protocols\JiShe.CollectBus.Protocol.T37612012\JiShe.CollectBus.Protocol.T37612012.csproj", "{8A61DF78-069B-40B5-8811-614E2960443E}" @@ -56,44 +40,17 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0.Docs", "0.Docs", "{D8346C4C-55B8-43E8-A6B8-E59D56FE6D92}" ProjectSection(SolutionItems) = preProject .gitignore = .gitignore + Directory.Build.JiShe.targets = Directory.Build.JiShe.targets + Directory.Build.Microsoft.targets = Directory.Build.Microsoft.targets + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + Directory.Build.Volo.targets = Directory.Build.Volo.targets Dockerfile = Dockerfile NuGet.Config = NuGet.Config PackageAndPublish.bat = PackageAndPublish.bat readme.md = readme.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Analyzers.Shared", "shared\JiShe.CollectBus.Analyzers.Shared\JiShe.CollectBus.Analyzers.Shared.csproj", "{DD68F314-BC66-5601-B094-B1A7BE93F4E0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Analyzers", "modules\JiShe.CollectBus.Analyzers\JiShe.CollectBus.Analyzers.csproj", "{EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Migration.Application.Contracts", "services\JiShe.CollectBus.Migration.Application.Contracts\JiShe.CollectBus.Migration.Application.Contracts.csproj", "{E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Migration.Application", "services\JiShe.CollectBus.Migration.Application\JiShe.CollectBus.Migration.Application.csproj", "{B955C5DA-3C20-35D2-0770-8FE473C41C44}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Migration.Host", "web\JiShe.CollectBus.Migration.Host\JiShe.CollectBus.Migration.Host.csproj", "{995D3D91-7221-D4A3-A7B2-FEC202328A18}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Migration.HttpApi", "web\JiShe.CollectBus.Migration.HttpApi\JiShe.CollectBus.Migration.HttpApi.csproj", "{8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MainService", "MainService", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EPOService", "EPOService", "{18207368-3C39-4346-85F7-A8B499B0B458}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MigrationService", "MigrationService", "{A31EC18C-956B-4FD2-AED6-609C33B467E9}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBusEPO.Application.Contracts", "services\JiShe.CollectBusEPO.Application.Contracts\JiShe.CollectBusEPO.Application.Contracts.csproj", "{3B32D838-33BF-C08A-C447-3172CA62D42F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBusEPO.Application", "services\JiShe.CollectBusEPO.Application\JiShe.CollectBusEPO.Application.csproj", "{C71FF348-FB47-67E6-AF0D-6A8E48C5EDEA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBusEPO.HttpApi", "web\JiShe.CollectBusEPO.HttpApi\JiShe.CollectBusEPO.HttpApi.csproj", "{98FE468D-0C34-83D9-31BD-F59C44D00357}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBusEPO.HttpApi.Host", "web\JiShe.CollectBusEPO.HttpApi.Host\JiShe.CollectBusEPO.HttpApi.Host.csproj", "{A2DDA6F0-C760-0633-D576-E23A186504E4}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MainHost", "MainHost", "{A38B5EB3-2F55-44A2-944B-31A73DE91402}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MigrationHost", "MigrationHost", "{C61007F8-51F5-409C-9FAF-DEF6A24DCDED}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EPOHost", "EPOHost", "{8D1FD3D9-3FF0-4581-ACF0-45AF9D595774}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -116,10 +73,6 @@ Global {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.Build.0 = Debug|Any CPU {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.ActiveCfg = Release|Any CPU {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.Build.0 = Release|Any CPU - {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.Build.0 = Release|Any CPU {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.Build.0 = Debug|Any CPU {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -136,34 +89,10 @@ Global {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Debug|Any CPU.Build.0 = Debug|Any CPU {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Release|Any CPU.ActiveCfg = Release|Any CPU {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Release|Any CPU.Build.0 = Release|Any CPU - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Release|Any CPU.Build.0 = Release|Any CPU - {C06C4082-638F-2996-5FED-7784475766C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C06C4082-638F-2996-5FED-7784475766C1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C06C4082-638F-2996-5FED-7784475766C1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C06C4082-638F-2996-5FED-7784475766C1}.Release|Any CPU.Build.0 = Release|Any CPU - {F0288175-F0EC-48BD-945F-CF1512850943}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0288175-F0EC-48BD-945F-CF1512850943}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0288175-F0EC-48BD-945F-CF1512850943}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0288175-F0EC-48BD-945F-CF1512850943}.Release|Any CPU.Build.0 = Release|Any CPU - {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Release|Any CPU.Build.0 = Release|Any CPU {A377955E-7EA1-6F29-8CF7-774569E93925}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A377955E-7EA1-6F29-8CF7-774569E93925}.Debug|Any CPU.Build.0 = Debug|Any CPU {A377955E-7EA1-6F29-8CF7-774569E93925}.Release|Any CPU.ActiveCfg = Release|Any CPU {A377955E-7EA1-6F29-8CF7-774569E93925}.Release|Any CPU.Build.0 = Release|Any CPU - {443B4549-0AC0-4493-8F3E-49C83225DD76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {443B4549-0AC0-4493-8F3E-49C83225DD76}.Debug|Any CPU.Build.0 = Debug|Any CPU - {443B4549-0AC0-4493-8F3E-49C83225DD76}.Release|Any CPU.ActiveCfg = Release|Any CPU - {443B4549-0AC0-4493-8F3E-49C83225DD76}.Release|Any CPU.Build.0 = Release|Any CPU - {6D6A2A58-7406-9C8C-7B23-3E442CCE3E6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6D6A2A58-7406-9C8C-7B23-3E442CCE3E6B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6D6A2A58-7406-9C8C-7B23-3E442CCE3E6B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6D6A2A58-7406-9C8C-7B23-3E442CCE3E6B}.Release|Any CPU.Build.0 = Release|Any CPU {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Debug|Any CPU.Build.0 = Debug|Any CPU {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -180,46 +109,6 @@ Global {75B7D419-C261-577D-58D6-AA3ACED9129F}.Debug|Any CPU.Build.0 = Debug|Any CPU {75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.ActiveCfg = Release|Any CPU {75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.Build.0 = Release|Any CPU - {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Release|Any CPU.Build.0 = Release|Any CPU - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Release|Any CPU.Build.0 = Release|Any CPU - {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}.Release|Any CPU.Build.0 = Release|Any CPU - {B955C5DA-3C20-35D2-0770-8FE473C41C44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B955C5DA-3C20-35D2-0770-8FE473C41C44}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B955C5DA-3C20-35D2-0770-8FE473C41C44}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B955C5DA-3C20-35D2-0770-8FE473C41C44}.Release|Any CPU.Build.0 = Release|Any CPU - {995D3D91-7221-D4A3-A7B2-FEC202328A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {995D3D91-7221-D4A3-A7B2-FEC202328A18}.Debug|Any CPU.Build.0 = Debug|Any CPU - {995D3D91-7221-D4A3-A7B2-FEC202328A18}.Release|Any CPU.ActiveCfg = Release|Any CPU - {995D3D91-7221-D4A3-A7B2-FEC202328A18}.Release|Any CPU.Build.0 = Release|Any CPU - {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}.Release|Any CPU.Build.0 = Release|Any CPU - {3B32D838-33BF-C08A-C447-3172CA62D42F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3B32D838-33BF-C08A-C447-3172CA62D42F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3B32D838-33BF-C08A-C447-3172CA62D42F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3B32D838-33BF-C08A-C447-3172CA62D42F}.Release|Any CPU.Build.0 = Release|Any CPU - {C71FF348-FB47-67E6-AF0D-6A8E48C5EDEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C71FF348-FB47-67E6-AF0D-6A8E48C5EDEA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C71FF348-FB47-67E6-AF0D-6A8E48C5EDEA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C71FF348-FB47-67E6-AF0D-6A8E48C5EDEA}.Release|Any CPU.Build.0 = Release|Any CPU - {98FE468D-0C34-83D9-31BD-F59C44D00357}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {98FE468D-0C34-83D9-31BD-F59C44D00357}.Debug|Any CPU.Build.0 = Debug|Any CPU - {98FE468D-0C34-83D9-31BD-F59C44D00357}.Release|Any CPU.ActiveCfg = Release|Any CPU - {98FE468D-0C34-83D9-31BD-F59C44D00357}.Release|Any CPU.Build.0 = Release|Any CPU - {A2DDA6F0-C760-0633-D576-E23A186504E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A2DDA6F0-C760-0633-D576-E23A186504E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A2DDA6F0-C760-0633-D576-E23A186504E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A2DDA6F0-C760-0633-D576-E23A186504E4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -227,40 +116,17 @@ Global GlobalSection(NestedProjects) = preSolution {D64C1577-4929-4B60-939E-96DE1534891A} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B} {F2840BC7-0188-4606-9126-DADD0F5ABF7A} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} - {BD65D04F-08D5-40C1-8C24-77CA0BACB877} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {78040F9E-3501-4A40-82DF-00A597710F35} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {F1C58097-4C08-4D88-8976-6B3389391481} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} - {077AA5F8-8B61-420C-A6B5-0150A66FDB34} = {A38B5EB3-2F55-44A2-944B-31A73DE91402} - {35829A15-4127-4F69-8BDE-9405DEAACA9A} = {A38B5EB3-2F55-44A2-944B-31A73DE91402} + {BD65D04F-08D5-40C1-8C24-77CA0BACB877} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} + {78040F9E-3501-4A40-82DF-00A597710F35} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} + {077AA5F8-8B61-420C-A6B5-0150A66FDB34} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94} + {35829A15-4127-4F69-8BDE-9405DEAACA9A} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94} {AD2F1928-4411-4511-B564-5FB996EC08B9} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B} {8BA01C3D-297D-42DF-BD63-EF07202A0A67} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} - {FE0457D9-4038-4A17-8808-DCAD06CFC0A0} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} - {C06C4082-638F-2996-5FED-7784475766C1} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} - {F0288175-F0EC-48BD-945F-CF1512850943} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} - {A3F3C092-0A25-450B-BF6A-5983163CBEF5} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} {A377955E-7EA1-6F29-8CF7-774569E93925} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} - {443B4549-0AC0-4493-8F3E-49C83225DD76} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} - {6D6A2A58-7406-9C8C-7B23-3E442CCE3E6B} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} {430D298B-377E-49B8-83AA-ADC7C0EBDB0F} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} {8A61DF78-069B-40B5-8811-614E2960443E} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} {E27377CC-E2D3-4237-060F-96EA214D3129} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} {75B7D419-C261-577D-58D6-AA3ACED9129F} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} - {DD68F314-BC66-5601-B094-B1A7BE93F4E0} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B} - {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59} - {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A} = {A31EC18C-956B-4FD2-AED6-609C33B467E9} - {B955C5DA-3C20-35D2-0770-8FE473C41C44} = {A31EC18C-956B-4FD2-AED6-609C33B467E9} - {995D3D91-7221-D4A3-A7B2-FEC202328A18} = {C61007F8-51F5-409C-9FAF-DEF6A24DCDED} - {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4} = {C61007F8-51F5-409C-9FAF-DEF6A24DCDED} - {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} - {18207368-3C39-4346-85F7-A8B499B0B458} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} - {A31EC18C-956B-4FD2-AED6-609C33B467E9} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23} - {3B32D838-33BF-C08A-C447-3172CA62D42F} = {18207368-3C39-4346-85F7-A8B499B0B458} - {C71FF348-FB47-67E6-AF0D-6A8E48C5EDEA} = {18207368-3C39-4346-85F7-A8B499B0B458} - {98FE468D-0C34-83D9-31BD-F59C44D00357} = {8D1FD3D9-3FF0-4581-ACF0-45AF9D595774} - {A2DDA6F0-C760-0633-D576-E23A186504E4} = {8D1FD3D9-3FF0-4581-ACF0-45AF9D595774} - {A38B5EB3-2F55-44A2-944B-31A73DE91402} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94} - {C61007F8-51F5-409C-9FAF-DEF6A24DCDED} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94} - {8D1FD3D9-3FF0-4581-ACF0-45AF9D595774} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} diff --git a/NuGet.Config b/NuGet.Config index 44f8e7f..370bfce 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,7 +1,8 @@ - - - - \ No newline at end of file + + + + + diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d8e6f12ff83305b508909bb6d53816c4757fcfb7 GIT binary patch literal 5207 zcmV-d6sYToP)WSb^VAvNym1^IbsU_}K~YCX9UV475kWSA2}mR? z2_ZlTA!H|OC%sg6*L~+!RWC`JbV51_Lf@}Rno3pOs{fwzpZ}bDt4Qio6oRdLjSE5$ z1S<$u5Ue0rL9nj+rW)ApwJNx77);H8!Lbk*24Q-z2+-IDr)uF~DST4|t=7v3EHY3! zD>?-3p9c3$fe6Dz_h}Pg^HF$f7nIaqtzZ!XODDp!iy%bTt^FM$yt@bf^flNVR~c9u z75w?fFh8rSeFO*CosD#e;sY8M$&l&}vvtl*&4CVKe1 zSX3=y1bFX3Flw&?u$E1QMPqzyZ?V!N=fx%~6`WAt1{S+?Q4&&!m-~CYnL(BmNq;r> zN|z2XA@IxsAKOhf`hB^$t$=)6%vCfH+5OOhCS}mMSz?qCsLLM_H$kKY)i7r?d12EP z4c77*VAS{sa~qG+*SGL>rgqzFTVPW@*;c?WpD9ir%FK&W$;v1mLd#pkfzs0b0hXF) zTA_2>S|q-(fwS3tA7!<}o7*@i6{lrVkdQ)%@b`g|E1I*W4FzXdJ8nnoD5>d*bOz`wojC$o$uMIWWG6#v zw30lbN^3Sl=><4i4&N5TiK@#UEYyv2N5L)QU`#TTNE(%oIM^d^qMgQIK{oruhx5rEzBMXH)X+? z6zHZ8XC}alcR*e#ytElA8ZR5LrVXJFOrsM9b3Etr8tE6|m`dt!b7*dA6?7d6y^Upj z8Y~zCGlw%`x`Z|%9o}CK&#i;pl7R**Tn|spqf;|zbhw0+7c?wok$8k}e~8`LEu@m7 zSbf5$yLC2Ibau65u&h(cI<TBgJa;8 zrDCKW3_22OAYKr;_=>Xo!A*LN$EN=I9Xz|fx9)~#HE1AI%Z}2M4x>S22!EvO_-7Tiu!Bk9;= zq*3NBQK66)M~q&@YYh)E2Bv8~*$-v){RP%_L-8Z~FVU$bvyy#~NRUyM25&sfV9Gxy zMZ@+UG(H^`_At8Po@g3sSEMl8AompAkWcoOK%?2)6^ZHq%o_o>=gHptP$><1b^JzFJR%NJv@VQM2UMEH8UkwU4XTXN8tayU{vbEgEa8F+hB4=54uBh zKx=ZtT6O{Lf__~{O6z^o6^6er3!%7{JUvI$t4UcMwK`zvAmWYJ(uwfu79~*jEm+yf zG$URjBZ-?h&c)~wVZFJ75!1!mFX#3EDU<@CIz}|I$9-{_{_7z5t_-tl|Ezwx4&Ke> z9-m3kuB)3UVx1D}IV#4;g2{n1eGk@+Bc&MRDllaI%`v zZV1F<6|UYm+G>URRvI0mZ0V*<=@nSv25?Z&5|`si5*f8S!gthP$q6ZcXA#UE;X{a{ z3a@B@xAS1#A%Vh-TR?D96kJpifwj$S*?@a& z`W!5ymIW6i6gFin`5e-%P7>&i7XOE13_=N-5$6f4Xg)iR!zo^xph z{ONSE8;dshKOBwgr>kLJ7K|9g=;=r~Y%gNzZihB39+u?L`J=%}XrmdCV*T21x|4 zPv?d{vM3uhJsm!X&8pV5vcQipaS}|ZNKtd42F56fKk>iW{-po-G|B(G3Tx<<25CcK zMc;!}XJU+AW1@qj-Njz^6YJ^q zyo3~LC7sHH6oS}j?lL#hV7^JE49tZX_0%Hs&h|Z62ugW9;RrR^Xt*Af%b*~NAad~J zJb3tn9-IxG@s@EE)5B;ZV-n<)Dig&nnA1rHg)phwBc)Xl6|d0q*21pn;-Q%|JVZuF zO7W$i8cDp=wD_RNKG9i7G^sIgrd}MCz_ZL=(lKZTm_49gy>AM9nCs^Z=x$5K!jHz$ zp$Q5RH<@WgGlmW-Ih_m^b3MdG@;=N!nW|t&aSBo>!BDAQY!cw1S#*3lbrsd*X6PV>MrVwG>=e-@Y4A6d z7fMi~6p!L0Mf&PC-${O7m1s76&%gp{MZ&QvXIc~&AL@}dc!8fZ6ZnGB-%g-ry#~DWe7Kj?5kOzmCdxS zjmm!`F0d4RKp0Fiqa#u$s$g3o*>a2taUZ369Xviy9G&8xNb*$u!^mXANY7%ZQbcW! z6z}pr{IpnL`*HHsF>rcr6Q4bn`e|*G6@p^6@|Hy1{Ofu~XDWc3@fEQ9tX4q5AAx!6c zKCxbJQV=2Tx|)(AQ@2pBtLOF|=MbhqEuas;j5NxYS2lAK(&?R(#Ds9)NO}JkT{XXq z#fQV^h*cQlj@MteNsdVa4Iy#9O zG##nR?tqguh`&2ds{vOy8tyDTFn$|UuA3ZLew^UC;+saz<1Xu5!re)T?0Wiz>iUY3rxPfG7tPQ>n3?$neu zc!nJF@SjW((qrhRqpAa?{ACB$;xYDH$9h(X#u(`%AM3TMc8X-xy_O-dOl3_U;=E&( zXT<^TWU_V(yWt7ql))Xr5*!d=pk)o7El0(R&eA8DZJG2IoQPp{K$E(qA7>CYZAj~t|Q{o60J|2m|;yezBML{{$7xWU5q z`VLK6v%d1J{HZH{}N>|Y)kbro4 zpMbTaSar6+0~Q9#g`+)AijHFzT!lNz9wGF5!bUTB@Ixgm{NYyXqAcZXuWZwPRj9gu ziv7tsKI{%VmHTywkbB11)3tE@1lA`9)v}V1TYOkMu-@6Lsc}7N$d;$u{NF>Akv@E# zAU`mbo#+{c+sD~Mb@YS%>Tgacw)xiGRySQDlYViG9b*wl?~&>&u+}_mmW6`PhHmKn z-(RiKeSO@|WsJT?yysdU&#$2J(y8II@73oU6*Bjp*+qcd+b>_vTD3=$8SeoLDdx@z zc5i}rTN64+e!2f>Xmr`>W1r|3X4&0#`pi1rmj(Uz?x8;E3E6bB*D8c)V>__^wntNI z>X}n`g@}Qyz6A!XiYC&~Mvz(X`zn|`EcKq{4&F(vPIN_P~y4*87{194Tz~VW0 za;@%xse-D96yX%1DG=2Ag8}QV3HJY4V7uhQ(_^gtrYZ*>ER>Y5ZSVLx5W*Iki#f?+ zVuZr<#WmdSld3kMvkP-qk!9iCuHL8dmY{b}wBIq_;f6_W7$4JP1jGw>E~xxL*3kkS z4N>^~eu))TsL#J@c$hQ7fhjjCqnEd82mDQIJ#B#;;akVr-@ex(BlYbm)h|BNz4fif z;?^8e|8=*f*+NhUAkDqI%;HY*t~#*LUVeMC4S_%nvHU~*W1r}@pHNx7RQLHtf?(bB z7lSM#QDOXfsg>uhKCog#=m9@XiG zE*Be)SG7aGppth3bhLu^2JZJ;v>P8YYgLTD-q@+>x(ky_##!v-yHb8}wgSojF19_t zsGY>~u68^%SNP=(g1g7(H)%UmB?kbkYa-}|Bh0P-aiHTpA1LL2{;6*AU?(Ot9j1KF zHE^$eqfLtuyR)KZ?`ahRik6#qmd|6$bRjEIBt2CAr5AeZM!|(410)@SVD+G(WPj{> zVNf`IbcQfA&cnTt2KtvftzDD@e<+QQnD(1Mol6v<#mZH;5d2xuMC=YSHN%+@PFrnc z-Y7?P3x|5O$wCANW1Gq8&csNHsZv51RdHZ-5Y(u#q132hYxrqp+X-+lArmqo>Xd_*@ zOLNOu2jYdFPxI8sqYqzaU$sYl?<9MAv?w^qtW4)CTQ%}+Pe^q>G+k(7$$%;)irzO# z!0i|!U)Ze0oqsV~D6JAQT=*@?%!wld#<(bELN>pud8YyI7oPu;Di^qXwl-Tnz`4uYp#i) zmm_DPmO;mn-)w|x{*PNVXY0A=7g{r7MU#!7R%o_>*$!bk_J|uIQdGqwlt<3uc66qxk;d+X6hGs$}>%)`?Edn=cRTN3b=74QQ zs_8?VD9Z5ODx0~V&18!^5Kx<4e%hD1-+6~#8683~yF}YW!HJZ#^|%Th4O0kAHn$Wq zR#D4u5hC)-@&YO214i(Ky>vmGp5a8cDy`>G0U{hx2KxC?bzYgO#zb%nVu7wva)GT* z2-mT-2*;~1Qo+Bu zOXH28-?JlCJ$k)>MzJQKD_;7d1r>Zy!3V(#f)xZS2v)EK!3u)aH|_rg7ywx)NotW7 RIxheK002ovPDHLkV1g`|*rxyh literal 0 HcmV?d00001 diff --git a/modules/JiShe.CollectBus.Analyzers/ComplexTypeSourceAnalyzers.cs b/modules/JiShe.CollectBus.Analyzers/ComplexTypeSourceAnalyzers.cs deleted file mode 100644 index c0ce66c..0000000 --- a/modules/JiShe.CollectBus.Analyzers/ComplexTypeSourceAnalyzers.cs +++ /dev/null @@ -1,734 +0,0 @@ -using JiShe.CollectBus.Analyzers.Shared; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Text; - -namespace JiShe.CollectBus.IncrementalGenerator -{ - /// - /// 复杂类型增量源生成器 - /// - [Generator(LanguageNames.CSharp)] - public class ComplexTypeSourceAnalyzers : IIncrementalGenerator - { - private const string AttributeFullName = "JiShe.CollectBus.Analyzers.Shared.SourceAnalyzersAttribute"; - - public void Initialize(IncrementalGeneratorInitializationContext context) - { - //Debugger.Launch(); - - // 步骤1:筛选带有 [SourceAnalyzers] 的类 - var classDeclarations = context.SyntaxProvider - .CreateSyntaxProvider( - predicate: static (s, _) => IsClassWithAttribute(s), - transform: static (ctx, _) => GetClassDeclaration(ctx)) - .Where(static c => c is not null); - - // 步骤2:合并编译信息 - var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect()); - - context.RegisterSourceOutput(compilationAndClasses, (spc, source) => - GenerateCode(source.Left, source.Right!, spc)); - } - - private static bool IsClassWithAttribute(SyntaxNode node) => node is ClassDeclarationSyntax cds && cds.AttributeLists.Count > 0; - - private static ClassDeclarationSyntax GetClassDeclaration(GeneratorSyntaxContext context) - { - var classDecl = (ClassDeclarationSyntax)context.Node; - var semanticModel = context.SemanticModel; - - // 获取类符号 - var classSymbol = semanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol; - if (classSymbol == null) return null; - - // 检查是否包含 SourceAnalyzers 特性 - var sourceAnalyzerAttr = classSymbol.GetAttributes().FirstOrDefault(attr => attr.AttributeClass?.ToDisplayString() == AttributeFullName); - - // 必须包含 EntityType 参数 - if (sourceAnalyzerAttr == null || - sourceAnalyzerAttr.ConstructorArguments.Length == 0) - { - return null; - } - - return classDecl; - - //var classDecl = (ClassDeclarationSyntax)context.Node; - //var attributeType = context.SemanticModel.Compilation.GetTypeByMetadataName(AttributeFullName); - - //foreach (var attribute in classDecl.AttributeLists.SelectMany(al => al.Attributes)) - //{ - // var symbol = context.SemanticModel.GetSymbolInfo(attribute).Symbol; - // if (symbol is IMethodSymbol ctor && - // SymbolEqualityComparer.Default.Equals(ctor.ContainingType, attributeType)) - // { - // return classDecl; - // } - //} - //return null; - } - - /// - /// 递归获取所有层级的属性 - /// - /// - /// - private static IEnumerable GetAllPropertiesInHierarchy(INamedTypeSymbol classSymbol) - { - var currentSymbol = classSymbol; - while (currentSymbol != null) - { - foreach (var prop in currentSymbol.GetMembers().OfType()) - { - yield return prop; - } - currentSymbol = currentSymbol.BaseType; // 向上遍历基类 - } - } - - /// - /// 生成代码 - /// - /// - /// - /// - private static void GenerateCode( - Compilation compilation, - IEnumerable classes, - SourceProductionContext context) - { - var processedTypes = new HashSet(SymbolEqualityComparer.Default); - - if (!classes.Any()) - { - context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor("GEN002", "没有目标类", - "没有找到SourceAnalyzers标记的类", "Debug", DiagnosticSeverity.Warning, true), - Location.None)); - } - - foreach (var classDecl in classes.Distinct()) - { - var model = compilation.GetSemanticModel(classDecl.SyntaxTree); - var classSymbol = model.GetDeclaredSymbol(classDecl) as INamedTypeSymbol; - - if (classSymbol == null || !processedTypes.Add(classSymbol)) - { - context.ReportDiagnostic(Diagnostic.Create( - new DiagnosticDescriptor("GEN003", "无效符号", - $"类名称为{classDecl.Identifier.Text} 符号为空", "Error", DiagnosticSeverity.Error, true), - Location.None)); - continue; - } - - var code3 = BuildAccessorsForSourceEntity(classSymbol, compilation, processedTypes); - context.AddSource($"{classSymbol.Name}Accessor.g.cs", code3); - } - - // 生成工厂注册代码 - context.AddSource("SourceEntityAccessorFactory.g.cs", BuildFactoryCode()); - } - - /// - /// 获取泛型参数 - /// - /// - /// - public static string GetGenericParams(INamedTypeSymbol symbol) - { - if (!symbol.IsGenericType) return ""; - var parameters = symbol.TypeParameters.Select(t => t.Name); - return $"<{string.Join(", ", parameters)}>"; - } - - - /// - /// 生成标准属性的访问器 - /// - /// - /// - /// - private static void GenerateStandardAccessors(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code) - { - var parentType = prop.ContainingType.ToDisplayString(); - code.AppendLine($" public static {propType.ToDisplayString()} Get{prop.Name}({parentType} obj) => obj.{prop.Name};"); - - if (prop.SetMethod != null) - { - code.AppendLine($" public static void Set{prop.Name}({parentType} obj, {propType.ToDisplayString()} value) => obj.{prop.Name} = value;"); - } - } - - - /// - /// 构建实体访问器代码(支持泛型) - /// - private static string BuildAccessorsForSourceEntity( - INamedTypeSymbol classSymbol, - Compilation compilation, - HashSet processedTypes) - { - // 获取 SourceAnalyzers 特性的 EntityType 参数 - var sourceAnalyzerAttr = classSymbol.GetAttributes() - .FirstOrDefault(attr => - attr.AttributeClass?.ToDisplayString() == AttributeFullName); - - // 解析 EntityType 枚举值 - string entityTypeValue = "EntityTypeEnum.Other"; // 默认值 - if (sourceAnalyzerAttr != null && - sourceAnalyzerAttr.ConstructorArguments.Length > 0) - { - var arg = sourceAnalyzerAttr.ConstructorArguments[0]; - if (arg.Kind == TypedConstantKind.Enum && - arg.Type is INamedTypeSymbol enumType) - { - int enumValue = (int)arg.Value!; - entityTypeValue = GetEnumMemberName(enumType, enumValue); - } - } - - var code = new StringBuilder(); - code.AppendLine("// "); - code.AppendLine("#nullable enable"); - code.AppendLine("using System;"); - code.AppendLine("using System.Reflection;"); - code.AppendLine("using System.Collections.Generic;"); - code.AppendLine("using JiShe.CollectBus.Analyzers.Shared;"); - code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};"); - code.AppendLine(); - - // 处理泛型类型名称 - var accessibility = classSymbol.DeclaredAccessibility switch - { - Accessibility.Public => "public", - _ => "internal" - }; - - var genericParams = classSymbol.IsGenericType - ? $"<{string.Join(", ", classSymbol.TypeParameters.Select(t => t.Name))}>" - : ""; - - code.AppendLine( - $"{accessibility} sealed class {classSymbol.Name}Accessor{genericParams} " + // 保留泛型参数 - $": ISourceEntityAccessor<{classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>"); - - code.AppendLine("{"); - - var propList = GetAllPropertiesInHierarchy(classSymbol); - - //类名称 - code.AppendLine($" public string EntityName {{get;}} = \"{classSymbol.Name}\";"); - // 添加 EntityType 属性 - code.AppendLine($" public EntityTypeEnum? EntityType {{ get; }} = {entityTypeValue};"); - - foreach (var prop in propList) - { - // 安全类型转换 - if (prop.Type is not ITypeSymbol propType) continue; - - if (propType is INamedTypeSymbol namedType) - { - GenerateStandardAccessors(prop, namedType, code); - } - - if (propType is INamedTypeSymbol { IsTupleType: true } tupleType) - { - GenerateTupleAccessors(prop, tupleType, code); - } - } - - //生成当前类属性名称集合 - GeneratePropertyListForSourceEntity(propList, code, compilation, classSymbol); - - //生成当前类属性信息集合 - GenerateEntityMemberInfoList(propList, code, compilation, classSymbol); - - - //生成当前类属性访问 - GetGeneratePropertyValueForSourceEntity(propList, code, compilation, classSymbol); - - //生成当前类属性设置 - SetGeneratePropertyValueForSourceEntity(propList, code, compilation, classSymbol); - - code.AppendLine("}"); - return code.ToString(); - } - - /// - /// 生成ValueTuple元组属性访问器 - /// - /// - /// - /// - private static void GenerateTupleAccessors( - IPropertySymbol prop, - INamedTypeSymbol tupleType, - StringBuilder code) - { - var parentType = prop.ContainingType.ToDisplayString(); - var tupleElements = tupleType.TupleElements; - - for (int i = 0; i < tupleElements.Length; i++) - { - var element = tupleElements[i]; - var elementType = element.Type.ToDisplayString(); - var elementName = element.Name; - - // Getter - code.AppendLine($"public static {elementType} Get{prop.Name}_{elementName}({parentType} obj) => obj.{prop.Name}.{elementName};"); - - // Setter - if (prop.SetMethod != null) - { - code.AppendLine($"public static void Set{prop.Name}_{elementName}({parentType} obj, {elementType} value) => obj.{prop.Name} = ({string.Join(", ", GetTupleElements(prop.Name, tupleElements, i))});"); - } - } - } - - private static IEnumerable GetTupleElements( - string propName, - ImmutableArray elements, - int targetIndex) - { - for (int i = 0; i < elements.Length; i++) - { - yield return i == targetIndex - ? "value" - : $"obj.{propName}.{elements[i].Name}"; - } - } - - /// - /// 处理System.Tuple类型的访问器生成 - /// - private static void GenerateSystemTupleAccessors( - IPropertySymbol prop, - INamedTypeSymbol tupleType, - StringBuilder code, - INamedTypeSymbol classSymbol) - { - var parentType = classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var elementTypes = tupleType.TypeArguments; - var tupleTypeName = tupleType.ToDisplayString(); - - for (int i = 0; i < elementTypes.Length; i++) - { - var elementType = elementTypes[i].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var elementName = $"Item{i + 1}"; - - // Getter - code.AppendLine( - $" public static {elementType} Get{prop.Name}_{elementName}" + - $"({parentType} obj) => obj.{prop.Name}.{elementName};"); - - // Setter - if (prop.SetMethod != null) - { - var assignments = elementTypes.Select((_, idx) => - idx == i ? "value" : $"obj.{prop.Name}.Item{idx + 1}" - ).ToList(); - - code.AppendLine( - $" public static void Set{prop.Name}_{elementName}" + - $"({parentType} obj, {elementType} value) => " + - $"obj.{prop.Name} = new {tupleTypeName}({string.Join(", ", assignments)});"); - } - } - } - - /// - /// 增强的工厂类实现 - /// - private static string BuildFactoryCode() - { - return """ - using System; - using System.Collections.Concurrent; - using System.Reflection; - - namespace JiShe.CollectBus.Analyzers.Shared; - - public static class SourceEntityAccessorFactory - { - private static readonly ConcurrentDictionary _accessors = new(); - - public static ISourceEntityAccessor GetAccessor() - { - return (ISourceEntityAccessor)_accessors.GetOrAdd(typeof(T), t => - { - // 获取泛型类型定义信息(如果是泛型类型) - var isGeneric = t.IsGenericType; - var genericTypeDef = isGeneric ? t.GetGenericTypeDefinition() : null; - var arity = isGeneric ? genericTypeDef!.GetGenericArguments().Length : 0; - - // 构建访问器类名 - var typeName = isGeneric - ? $"{t.Namespace}.{genericTypeDef!.Name.Split('`')[0]}Accessor`{arity}" - : $"{t.Namespace}.{t.Name}Accessor"; - - // 尝试从当前程序集加载 - var accessorType = Assembly.GetAssembly(t)!.GetType(typeName) - ?? throw new InvalidOperationException($"Accessor type {typeName} not found"); - - // 处理泛型参数 - if (isGeneric && accessorType.IsGenericTypeDefinition) - { - accessorType = accessorType.MakeGenericType(t.GetGenericArguments()); - } - - return Activator.CreateInstance(accessorType)!; - }); - } - - public static object GetAccessor(Type type) - { - MethodInfo getAccessorMethod = typeof(SourceEntityAccessorFactory) - .GetMethod( - name: nameof(GetAccessor), - bindingAttr: BindingFlags.Public | BindingFlags.Static, - - types: Type.EmptyTypes - ); - - MethodInfo genericMethod = getAccessorMethod.MakeGenericMethod(type); - return genericMethod.Invoke(null, null); - } - } - """; - } - - /// - /// 属性访问生成逻辑 - /// - /// 属性集合 - /// - /// - /// - private static void GetGeneratePropertyValueForSourceEntity( - IEnumerable propList, - StringBuilder code, - Compilation compilation, - INamedTypeSymbol classSymbol) - { - code.AppendLine($" public object GetPropertyValue({classSymbol} targetEntity, string propertyName)"); - code.AppendLine(" {"); - code.AppendLine(" return propertyName switch"); - code.AppendLine(" {"); - - foreach (var prop in propList) - { - code.AppendLine( - $" \"{prop.Name}\" => " + - $"Get{prop.Name}(targetEntity),"); - - if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType) - { - foreach (var element in tupleType.TupleElements) - { - code.AppendLine( - $" \"{prop.Name}.{element.Name}\" => " + - $"Get{prop.Name}_{element.Name}(targetEntity),"); - } - } - - } - - code.AppendLine(" _ => throw new ArgumentException($\"Unknown property: {propertyName}\")"); - code.AppendLine(" };"); - code.AppendLine(" }"); - } - - /// - /// 属性设置生成逻辑 - /// - /// 属性集合 - /// - /// - /// - private static void SetGeneratePropertyValueForSourceEntity( - IEnumerable propList, - StringBuilder code, - Compilation compilation, - INamedTypeSymbol classSymbol) - { - code.AppendLine($" public void SetPropertyValue({classSymbol} targetEntity, string propertyName, object value)"); - code.AppendLine(" {"); - code.AppendLine(" switch (propertyName)"); - code.AppendLine(" {"); - - foreach (var prop in propList) - { - var propType = prop.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - code.AppendLine($" case \"{prop.Name}\":"); - code.AppendLine($" Set{prop.Name}("); - code.AppendLine($" targetEntity, ({propType})value);"); - code.AppendLine(" break;"); - - if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType) - { - foreach (var element in tupleType.TupleElements) - { - var elementType = element.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - code.AppendLine($" case \"{prop.Name}.{element.Name}\":"); - code.AppendLine($" Set{prop.Name}_{element.Name}("); - code.AppendLine($" targetEntity, ({elementType})value);"); - code.AppendLine(" break;"); - } - } - } - - code.AppendLine(" default:"); - code.AppendLine(" throw new ArgumentException($\"Unknown property: {propertyName}\");"); - code.AppendLine(" }"); - code.AppendLine(" }"); - } - - /// - /// 属性名称集合 - /// - /// 属性集合 - /// - /// - /// - private static void GeneratePropertyListForSourceEntity( - IEnumerable propList, - StringBuilder code, - Compilation compilation, - INamedTypeSymbol classSymbol) - { - code.AppendLine(" public List PropertyNameList {get;} = new List()"); - code.AppendLine(" {"); - List tempPropList = new List(); - foreach (var prop in propList) - { - if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType) - { - foreach (var element in tupleType.TupleElements) - { - tempPropList.Add($"\"{prop.Name}.{element.Name}\""); - } - } - else - { - tempPropList.Add($"\"{prop.Name}\""); - } - } - - code.Append(string.Join(",", tempPropList)); - - code.AppendLine(" };"); - } - - - /// - /// 生成当前类属性信息集合 - /// - private static void GenerateEntityMemberInfoList( - IEnumerable propList, - StringBuilder code, - Compilation compilation, - INamedTypeSymbol classSymbol) - { - code.AppendLine(" public List MemberList { get; } = new()"); - code.AppendLine(" {"); - - var initializerLines = new List(); - - foreach (var prop in propList) - { - var entityType = prop.ContainingType.ToDisplayString();//entity 实体类型名称 - var propType = prop.Type;//实体属性的类型 - var propTypeName = propType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - // var declaredTypeName = propType.Name; // 直接获取类型名称(如 "Int32") - // 处理可空类型,获取底层具体类型名称 - var declaredTypeName = propType switch - { - INamedTypeSymbol { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } nullableType => - nullableType.TypeArguments[0].Name, // 提取如 "Int32" - _ => propType.Name - }; - - // 处理主属性 - var propAttributes = prop.GetAttributes() - .Where(a => !IsCompilerGeneratedAttribute(a)) - .ToList(); - - var attributeInitializers = propAttributes - .Select(GenerateAttributeInitializer) - .Where(s => !string.IsNullOrEmpty(s)); - - var mainMember = new StringBuilder(); - mainMember.Append( - $"new EntityMemberInfo(" + - $"\"{prop.Name}\", " + - $"typeof({propTypeName}), " + - $"\"{declaredTypeName}\", " + - $"(e) => Get{prop.Name}(({entityType})e), " + - $"(e, v) => Set{prop.Name}(({entityType})e, ({propTypeName})v))"); - - if (attributeInitializers.Any()) - { - mainMember.AppendLine(); - mainMember.Append(" { CustomAttributes = new List"); - mainMember.Append($" {{ {string.Join(", ", attributeInitializers)} }} }}"); - } - - initializerLines.Add(mainMember.ToString()); - - // 处理元组元素,(暂不需要处理元组元素的特性) - if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType) - { - foreach (var element in tupleType.TupleElements) - { - var elementType = element.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);//元组元素的类型 - var elementName = element.Name;//元组元素名称 - var elementDeclaredName = element.Type.Name;//元组元素类型名称 - - initializerLines.Add( - $"new EntityMemberInfo(" + - $"\"{prop.Name}.{elementName}\", " + - $"typeof({elementType}), " + - $"GetValueTupleElementDeclaredTypeName(typeof({elementType})), " +//$"\"{elementDeclaredName}\", " + - $"(e) => Get{prop.Name}_{elementName}(({entityType})e), " + - $"(e, v) => Set{prop.Name}_{elementName}(({entityType})e, ({elementType})v))"); - } - } - } - - code.AppendLine(string.Join(",\n", initializerLines)); - code.AppendLine(" };"); - - code.AppendLine(GetValueTupleElementName()); - } - - private static string GetValueTupleElementName() - { - return """ - public static string GetValueTupleElementDeclaredTypeName(Type declaredType) - { - string typeName; - // 处理可空类型 - if (declaredType.IsGenericType && declaredType.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - Type underlyingType = Nullable.GetUnderlyingType(declaredType); - typeName = underlyingType.Name; - } - else - { - typeName = declaredType.Name; - } - - return typeName; - } - """; - } - - - private static string GenerateAttributeInitializer(AttributeData attribute) - { - if (attribute.AttributeClass == null) - return string.Empty; - - var attributeClass = attribute.AttributeClass.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var args = attribute.ConstructorArguments; - var namedArgs = attribute.NamedArguments; - - var parameters = new List(); - foreach (var arg in args) - { - parameters.Add(ConvertTypedConstantToCode(arg)); - } - - var constructorArgs = string.Join(", ", parameters); - - var initializer = new StringBuilder(); - initializer.Append($"new {attributeClass}({constructorArgs})"); - - if (namedArgs.Any()) - { - initializer.Append(" { "); - var namedArgsList = namedArgs.Select(n => $"{n.Key} = {ConvertTypedConstantToCode(n.Value)}"); - initializer.Append(string.Join(", ", namedArgsList)); - initializer.Append(" }"); - } - - return initializer.ToString(); - } - - private static string ConvertTypedConstantToCode(TypedConstant constant) - { - if (constant.IsNull) - return "null"; - - switch (constant.Kind) - { - case TypedConstantKind.Array: - var elements = constant.Values.Select(ConvertTypedConstantToCode); - return $"new[] {{ {string.Join(", ", elements)} }}"; - case TypedConstantKind.Type: - var typeSymbol = (ITypeSymbol)constant.Value!; - return $"typeof({typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)})"; - case TypedConstantKind.Enum: - return ConvertEnumTypedConstant(constant); - default: - return ConvertPrimitiveConstant(constant); - } - } - - private static string ConvertEnumTypedConstant(TypedConstant constant) - { - var enumType = constant.Type!; - var enumValue = constant.Value!; - var enumTypeName = enumType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - - foreach (var member in enumType.GetMembers().OfType()) - { - if (member.ConstantValue != null && member.ConstantValue.Equals(enumValue)) - return $"{enumTypeName}.{member.Name}"; - } - - return $"({enumTypeName})({enumValue})"; - } - - private static string ConvertPrimitiveConstant(TypedConstant constant) - { - var value = constant.Value!; - return value switch - { - string s => $"\"{s}\"", - char c => $"'{c}'", - bool b => b ? "true" : "false", - _ => value.ToString() - }; - } - - private static bool IsCompilerGeneratedAttribute(AttributeData attribute) - { - return attribute.AttributeClass?.ToDisplayString() == "System.Runtime.CompilerServices.CompilerGeneratedAttribute"; - } - - /// - /// 获取枚举的参数 - /// - /// - /// - /// - private static string GetEnumMemberName(INamedTypeSymbol enumType, int value) - { - foreach (var member in enumType.GetMembers().OfType()) - { - if (member.ConstantValue is int intValue && intValue == value) - { - return $"{enumType.ToDisplayString()}.{member.Name}"; - } - } - return $"{enumType.ToDisplayString()}.Other"; - } - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Analyzers/JiShe.CollectBus.Analyzers.csproj b/modules/JiShe.CollectBus.Analyzers/JiShe.CollectBus.Analyzers.csproj deleted file mode 100644 index 66b1fcf..0000000 --- a/modules/JiShe.CollectBus.Analyzers/JiShe.CollectBus.Analyzers.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - netstandard2.0 - true - true - false - bin - false - latest - true - true - - - - - - - - - diff --git a/modules/JiShe.CollectBus.Cassandra/CassandraConfig.cs b/modules/JiShe.CollectBus.Cassandra/CassandraConfig.cs deleted file mode 100644 index c14171e..0000000 --- a/modules/JiShe.CollectBus.Cassandra/CassandraConfig.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Cassandra -{ - public class CassandraConfig - { - public Node[] Nodes { get; set; } - public string Username { get; set; } - public string Password { get; set; } - public string Keyspace { get; set; } - public string ConsistencyLevel { get; set; } - public Pooling PoolingOptions { get; set; } - public Socket SocketOptions { get; set; } - public Query QueryOptions { get; set; } - - public ReplicationStrategy ReplicationStrategy { get; set; } - } - - public class Pooling - { - public int CoreConnectionsPerHost { get; set; } - public int MaxConnectionsPerHost { get; set; } - public int MaxRequestsPerConnection { get; set; } - } - - public class Socket - { - public int ConnectTimeoutMillis { get; set; } - public int ReadTimeoutMillis { get; set; } - } - - public class Query - { - public string ConsistencyLevel { get; set; } - public string SerialConsistencyLevel { get; set; } - public bool DefaultIdempotence { get; set; } - } - - public class ReplicationStrategy - { - public string Class { get; set; } - public DataCenter[] DataCenters { get; set; } - } - - public class DataCenter - { - public string Name { get; set; } - public int ReplicationFactor { get; set; } - public string Strategy { get; set; } - } - - public class Node - { - public string Host { get; set; } - public int Port { get; set; } - public string DataCenter { get; set; } - public string Rack { get; set; } - } - -} diff --git a/modules/JiShe.CollectBus.Cassandra/CassandraProvider.cs b/modules/JiShe.CollectBus.Cassandra/CassandraProvider.cs deleted file mode 100644 index dc3e0ee..0000000 --- a/modules/JiShe.CollectBus.Cassandra/CassandraProvider.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Text; -using Cassandra; -using Cassandra.Mapping; -using Cassandra.Data.Linq; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; -using JiShe.CollectBus.Common.Attributes; - -namespace JiShe.CollectBus.Cassandra -{ - public class CassandraProvider : IDisposable, ICassandraProvider, ISingletonDependency - { - private readonly ILogger _logger; - - public Cluster Instance { get; set; } - - public ISession Session { get; set; } - - public CassandraConfig CassandraConfig { get; set; } - /// - /// - /// - /// - /// - public CassandraProvider( - IOptions options, - ILogger logger) - { - CassandraConfig = options.Value; - _logger = logger; - } - - public Task InitClusterAndSessionAsync() - { - InitClusterAndSession(); - return Task.CompletedTask; - } - - public void InitClusterAndSession() - { - GetCluster((keyspace) => - { - GetSession(keyspace); - }); - } - - public Cluster GetCluster(Action? callback=null) - { - var clusterBuilder = Cluster.Builder(); - - // 添加多个节点 - foreach (var node in CassandraConfig.Nodes) - { - clusterBuilder.AddContactPoint(node.Host) - .WithPort(node.Port); - } - - clusterBuilder.WithCredentials(CassandraConfig.Username, CassandraConfig.Password); - - // 优化连接池配置 - var poolingOptions = new PoolingOptions() - .SetCoreConnectionsPerHost(HostDistance.Local, CassandraConfig.PoolingOptions.CoreConnectionsPerHost) - .SetMaxConnectionsPerHost(HostDistance.Local, CassandraConfig.PoolingOptions.MaxConnectionsPerHost) - .SetMaxRequestsPerConnection(CassandraConfig.PoolingOptions.MaxRequestsPerConnection) - .SetHeartBeatInterval(30000); // 30秒心跳 - - clusterBuilder.WithPoolingOptions(poolingOptions); - - // 优化Socket配置 - var socketOptions = new SocketOptions() - .SetConnectTimeoutMillis(CassandraConfig.SocketOptions.ConnectTimeoutMillis) - .SetReadTimeoutMillis(CassandraConfig.SocketOptions.ReadTimeoutMillis) - .SetTcpNoDelay(true) // 启用Nagle算法 - .SetKeepAlive(true) // 启用TCP保活 - .SetReceiveBufferSize(32768) // 32KB接收缓冲区 - .SetSendBufferSize(32768); // 32KB发送缓冲区 - - clusterBuilder.WithSocketOptions(socketOptions); - - // 优化查询选项 - var queryOptions = new QueryOptions() - .SetConsistencyLevel((ConsistencyLevel)Enum.Parse(typeof(ConsistencyLevel), CassandraConfig.QueryOptions.ConsistencyLevel)) - .SetSerialConsistencyLevel((ConsistencyLevel)Enum.Parse(typeof(ConsistencyLevel), CassandraConfig.QueryOptions.SerialConsistencyLevel)) - .SetDefaultIdempotence(CassandraConfig.QueryOptions.DefaultIdempotence) - .SetPageSize(5000); // 增加页面大小 - - clusterBuilder.WithQueryOptions(queryOptions); - - // 启用压缩 - clusterBuilder.WithCompression(CompressionType.LZ4); - - // 配置重连策略 - clusterBuilder.WithReconnectionPolicy(new ExponentialReconnectionPolicy(1000, 10 * 60 * 1000)); - Instance = clusterBuilder.Build(); - callback?.Invoke(null); - return Instance; - } - - public ISession GetSession(string? keyspace = null) - { - if (string.IsNullOrEmpty(keyspace)) - { - keyspace = CassandraConfig.Keyspace; - } - Session = Instance.Connect(); - var replication = GetReplicationStrategy(); - Session.CreateKeyspaceIfNotExists(keyspace, replication); - Session.ChangeKeyspace(keyspace); - return Session; - } - - private Dictionary GetReplicationStrategy() - { - var strategy = CassandraConfig.ReplicationStrategy.Class; - var dataCenters = CassandraConfig.ReplicationStrategy.DataCenters; - - switch (strategy) - { - case "NetworkTopologyStrategy": - var networkDic = new Dictionary { { "class", "NetworkTopologyStrategy" } }; - foreach (var dataCenter in dataCenters) - { - networkDic.Add(dataCenter.Name, dataCenter.ReplicationFactor.ToString()); - } - return networkDic; - case "SimpleStrategy": - var dic = new Dictionary { { "class", "SimpleStrategy" } }; - if (dataCenters.Length >= 1) - { - dic.Add("replication_factor", dataCenters[0].ReplicationFactor.ToString()); - } - else - { - _logger.LogError("SimpleStrategy 不支持多个数据中心!"); - } - return dic; - default: - throw new ArgumentNullException($"Strategy", "Strategy配置错误!"); - } - } - - public void Dispose() - { - Instance.Dispose(); - Session.Dispose(); - } - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Cassandra/CassandraRepository.cs b/modules/JiShe.CollectBus.Cassandra/CassandraRepository.cs deleted file mode 100644 index 1d6cc3c..0000000 --- a/modules/JiShe.CollectBus.Cassandra/CassandraRepository.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Linq.Expressions; -using Cassandra.Mapping; -using JiShe.CollectBus.Cassandra.Extensions; -using Volo.Abp.Domain.Entities; - -namespace JiShe.CollectBus.Cassandra -{ - public class CassandraRepository - : ICassandraRepository - where TEntity : class, ICassandraEntity - { - private readonly ICassandraProvider _cassandraProvider; - public CassandraRepository(ICassandraProvider cassandraProvider, MappingConfiguration mappingConfig) - { - _cassandraProvider = cassandraProvider; - Mapper = new Mapper(cassandraProvider.Session, mappingConfig); - cassandraProvider.Session.CreateTable(cassandraProvider.CassandraConfig.Keyspace); - } - - public readonly IMapper Mapper; - - public virtual async Task GetAsync(TKey id) - { - return await GetAsync("WHERE id = ?", id); - } - - public virtual async Task GetAsync(string cql, params object[] args) - { - return await Mapper.SingleAsync(cql, args); - } - - - public virtual async Task FirstOrDefaultAsync(TKey id) - { - return await FirstOrDefaultAsync("WHERE id = ?", id); - } - - public virtual async Task FirstOrDefaultAsync(string cql, params object[] args) - { - return await Mapper.FirstOrDefaultAsync(cql, args); - } - - - public virtual async Task?> GetListAsync(string? cql = null, params object[] args) - { - return cql.IsNullOrWhiteSpace() ? (await Mapper.FetchAsync()).ToList() : (await Mapper.FetchAsync(cql, args)).ToList(); - } - - public virtual async Task InsertAsync(TEntity entity) - { - await Mapper.InsertAsync(entity); - return entity; - } - - public virtual async Task UpdateAsync(TEntity entity) - { - await Mapper.UpdateAsync(entity); - return entity; - } - - public virtual async Task DeleteAsync(TEntity entity) - { - await Mapper.DeleteAsync(entity); - } - - public virtual async Task DeleteAsync(TKey id) - { - await Mapper.DeleteAsync("WHERE id = ?", id); - } - - public virtual async Task> GetPagedListAsync( - int skipCount, - int maxResultCount, - string sorting) - { - var cql = $"SELECT * FROM {typeof(TEntity).Name.ToLower()}"; - if (!string.IsNullOrWhiteSpace(sorting)) - { - cql += $" ORDER BY {sorting}"; - } - cql += $" LIMIT {maxResultCount} OFFSET {skipCount}"; - - return (await Mapper.FetchAsync(cql)).ToList(); - } - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Cassandra/CollectBusCassandraModule.cs b/modules/JiShe.CollectBus.Cassandra/CollectBusCassandraModule.cs deleted file mode 100644 index bf57bce..0000000 --- a/modules/JiShe.CollectBus.Cassandra/CollectBusCassandraModule.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp; -using Volo.Abp.Autofac; -using Volo.Abp.Modularity; - -namespace JiShe.CollectBus.Cassandra -{ - [DependsOn( - typeof(AbpAutofacModule) - )] - public class CollectBusCassandraModule : AbpModule - { - public override Task ConfigureServicesAsync(ServiceConfigurationContext context) - { - Configure(context.Services.GetConfiguration().GetSection("Cassandra")); - context.AddCassandra(); - return Task.CompletedTask; - } - - public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) - { - await context.UseCassandra(); - } - } -} diff --git a/modules/JiShe.CollectBus.Cassandra/Extensions/ServiceCollectionExtensions.cs b/modules/JiShe.CollectBus.Cassandra/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index c9ec0de..0000000 --- a/modules/JiShe.CollectBus.Cassandra/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using JiShe.CollectBus.Cassandra; -using Volo.Abp; -using Volo.Abp.Modularity; - -// ReSharper disable once CheckNamespace -namespace Microsoft.Extensions.DependencyInjection -{ - public static class ApplicationInitializationContextExtensions - { - public static async Task UseCassandra(this ApplicationInitializationContext context) - { - var service = context.ServiceProvider; - var cassandraProvider = service.GetRequiredService(); - await cassandraProvider.InitClusterAndSessionAsync(); - } - } - - public static class ServiceCollectionExtensions - { - public static void AddCassandra(this ServiceConfigurationContext context) - { - context.Services.AddTransient(typeof(ICassandraRepository<,>), typeof(CassandraRepository<,>)); - } - } -} diff --git a/modules/JiShe.CollectBus.Cassandra/Extensions/SessionExtension.cs b/modules/JiShe.CollectBus.Cassandra/Extensions/SessionExtension.cs deleted file mode 100644 index dd0ff66..0000000 --- a/modules/JiShe.CollectBus.Cassandra/Extensions/SessionExtension.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Reflection; -using System.Text; -using Cassandra; -using System.ComponentModel.DataAnnotations; -using JiShe.CollectBus.Common.Attributes; -using Volo.Abp.Data; - -namespace JiShe.CollectBus.Cassandra.Extensions -{ - public static class SessionExtension - { - public static void CreateTable(this ISession session,string? defaultKeyspace=null) where TEntity : class - { - var type = typeof(TEntity); - var tableAttribute = type.GetCustomAttribute(); - var tableName = tableAttribute?.Name ?? type.Name.ToLower(); - var tableKeyspace = session.Keyspace; - - var properties = type.GetProperties(); - - // 分区键设置 - var primaryKey = properties.FirstOrDefault(p => p.GetCustomAttribute() != null); - - if (primaryKey == null) - { - throw new InvalidOperationException($"No primary key defined for type {type.Name}"); - } - - // 集群键设置 - var clusteringKeys = properties.Where(p => p.GetCustomAttribute() != null).Select(a=>a.Name).ToList(); - var clusteringKeyCql = string.Empty; - if (clusteringKeys.Any()) - { - clusteringKeyCql = $", {string.Join(", ", clusteringKeys)}"; - } - - var cql = new StringBuilder(); - cql.Append($"CREATE TABLE IF NOT EXISTS {tableKeyspace}.{tableName} ("); - - foreach (var prop in properties) - { - var ignoreAttribute = prop.GetCustomAttribute(); - if (ignoreAttribute != null) continue; - var columnName = prop.Name.ToLower(); - var cqlType = GetCassandraType(prop.PropertyType); - - cql.Append($"{columnName} {cqlType}, "); - } - cql.Length -= 2; // Remove last comma and space - cql.Append($", PRIMARY KEY (({primaryKey.Name.ToLower()}){clusteringKeyCql}))"); - - session.Execute(cql.ToString()); - } - - private static string GetCassandraType(Type type) - { - // 基础类型处理 - switch (Type.GetTypeCode(type)) - { - case TypeCode.String: return "text"; - case TypeCode.Int32: return "int"; - case TypeCode.Int64: return "bigint"; - case TypeCode.Boolean: return "boolean"; - case TypeCode.DateTime: return "timestamp"; - case TypeCode.Byte: return "tinyint"; - } - - if (type == typeof(Guid)) return "uuid"; - if (type == typeof(DateTimeOffset)) return "timestamp"; - if (type == typeof(Byte[])) return "blob"; - if (type == typeof(ExtraPropertyDictionary)) return "map"; - - // 处理集合类型 - if (type.IsGenericType) - { - var genericType = type.GetGenericTypeDefinition(); - var elementType = type.GetGenericArguments()[0]; - - if (genericType == typeof(List<>)) - return $"list<{GetCassandraType(elementType)}>"; - if (genericType == typeof(HashSet<>)) - return $"set<{GetCassandraType(elementType)}>"; - if (genericType == typeof(Nullable<>)) - return GetCassandraType(elementType); - if (genericType == typeof(Dictionary<,>)) - { - var keyType = type.GetGenericArguments()[0]; - var valueType = type.GetGenericArguments()[1]; - return $"map<{GetCassandraType(keyType)}, {GetCassandraType(valueType)}>"; - } - } - - throw new NotSupportedException($"不支持的类型: {type.Name}"); - } - - - } -} diff --git a/modules/JiShe.CollectBus.Cassandra/ICassandraProvider.cs b/modules/JiShe.CollectBus.Cassandra/ICassandraProvider.cs deleted file mode 100644 index 8b1f87a..0000000 --- a/modules/JiShe.CollectBus.Cassandra/ICassandraProvider.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Cassandra; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Cassandra -{ - public interface ICassandraProvider - { - Cluster Instance { get;} - - ISession Session { get;} - - CassandraConfig CassandraConfig { get; } - - ISession GetSession(string? keyspace = null); - - Cluster GetCluster(Action? callback = null); - - void InitClusterAndSession(); - - Task InitClusterAndSessionAsync(); - } -} diff --git a/modules/JiShe.CollectBus.Cassandra/ICassandraRepository.cs b/modules/JiShe.CollectBus.Cassandra/ICassandraRepository.cs deleted file mode 100644 index 5f3b862..0000000 --- a/modules/JiShe.CollectBus.Cassandra/ICassandraRepository.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Volo.Abp.Domain.Entities; - -namespace JiShe.CollectBus.Cassandra -{ - public interface ICassandraRepository where TEntity : class - { - Task GetAsync(TKey id); - Task GetAsync(string cql, params object[] args); - Task FirstOrDefaultAsync(TKey id); - Task FirstOrDefaultAsync(string cql, params object[] args); - Task?> GetListAsync(string? cql = null, params object[] args); - Task InsertAsync(TEntity entity); - Task UpdateAsync(TEntity entity); - Task DeleteAsync(TEntity entity); - Task DeleteAsync(TKey id); - Task> GetPagedListAsync(int skipCount, int maxResultCount, string sorting); - } -} diff --git a/modules/JiShe.CollectBus.Cassandra/JiShe.CollectBus.Cassandra.csproj b/modules/JiShe.CollectBus.Cassandra/JiShe.CollectBus.Cassandra.csproj deleted file mode 100644 index a3360e3..0000000 --- a/modules/JiShe.CollectBus.Cassandra/JiShe.CollectBus.Cassandra.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - - - - diff --git a/modules/JiShe.CollectBus.FreeRedis/CollectBusFreeRedisModule.cs b/modules/JiShe.CollectBus.FreeRedis/CollectBusFreeRedisModule.cs deleted file mode 100644 index fb86fdd..0000000 --- a/modules/JiShe.CollectBus.FreeRedis/CollectBusFreeRedisModule.cs +++ /dev/null @@ -1,25 +0,0 @@ -using JiShe.CollectBus.FreeRedis.Options; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Modularity; - -namespace JiShe.CollectBus.FreeRedis -{ - public class CollectBusFreeRedisModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - var configuration = context.Services.GetConfiguration(); - - Configure(options => - { - configuration.GetSection("Redis").Bind(options); - }); - - - } - } -} - - - diff --git a/modules/JiShe.CollectBus.FreeRedis/FreeRedisProvider.cs b/modules/JiShe.CollectBus.FreeRedis/FreeRedisProvider.cs deleted file mode 100644 index b2cf9e5..0000000 --- a/modules/JiShe.CollectBus.FreeRedis/FreeRedisProvider.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Diagnostics; -using FreeRedis; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.FreeRedis.Options; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; - -namespace JiShe.CollectBus.FreeRedis -{ - - public class FreeRedisProvider : IFreeRedisProvider, ISingletonDependency - { - - private readonly FreeRedisOptions _option; - - /// - /// FreeRedis - /// - public FreeRedisProvider(IOptions options) - { - _option = options.Value; - GetInstance(); - } - - public RedisClient Instance { get; set; } = new(string.Empty); - - /// - /// 获取 FreeRedis 客户端 - /// - /// - public IRedisClient GetInstance() - { - - var connectionString = $"{_option.Configuration},defaultdatabase={_option.DefaultDB},MaxPoolSize={_option.MaxPoolSize}"; - Instance = new RedisClient(connectionString); - Instance.Serialize = obj => BusJsonSerializer.Serialize(obj); - Instance.Deserialize = (json, type) => BusJsonSerializer.Deserialize(json, type); - Instance.Notice += (s, e) => Trace.WriteLine(e.Log); - return Instance; - } - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.FreeRedis/IFreeRedisProvider.cs b/modules/JiShe.CollectBus.FreeRedis/IFreeRedisProvider.cs deleted file mode 100644 index e9e8a40..0000000 --- a/modules/JiShe.CollectBus.FreeRedis/IFreeRedisProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FreeRedis; - -namespace JiShe.CollectBus.FreeRedis -{ - public interface IFreeRedisProvider - { - /// - /// 获取客户端 - /// - /// - RedisClient Instance { get; set; } - } -} - diff --git a/modules/JiShe.CollectBus.FreeRedis/JiShe.CollectBus.FreeRedis.csproj b/modules/JiShe.CollectBus.FreeRedis/JiShe.CollectBus.FreeRedis.csproj deleted file mode 100644 index 92b3223..0000000 --- a/modules/JiShe.CollectBus.FreeRedis/JiShe.CollectBus.FreeRedis.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - diff --git a/modules/JiShe.CollectBus.FreeRedis/Options/FreeRedisOptions.cs b/modules/JiShe.CollectBus.FreeRedis/Options/FreeRedisOptions.cs deleted file mode 100644 index 331da88..0000000 --- a/modules/JiShe.CollectBus.FreeRedis/Options/FreeRedisOptions.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace JiShe.CollectBus.FreeRedis.Options -{ - public class FreeRedisOptions - { - /// - /// 连接字符串 - /// - public string? Configuration { get; set; } - - /// - /// 最大连接数 - /// - public string? MaxPoolSize { get; set; } - - /// - /// 默认数据库 - /// - public string? DefaultDB { get; set; } - - /// - /// HangfireDB - /// - public string? HangfireDB { get; set; } - } -} - diff --git a/modules/JiShe.CollectBus.FreeSql/CollectBusFreeSqlModule.cs b/modules/JiShe.CollectBus.FreeSql/CollectBusFreeSqlModule.cs deleted file mode 100644 index 309e22c..0000000 --- a/modules/JiShe.CollectBus.FreeSql/CollectBusFreeSqlModule.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FreeSql; -using FreeSql.SqlServer; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Modularity; - -namespace JiShe.CollectBus.FreeSql -{ - public class CollectBusFreeSqlModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - } - } -} diff --git a/modules/JiShe.CollectBus.FreeSql/DbEnum.cs b/modules/JiShe.CollectBus.FreeSql/DbEnum.cs deleted file mode 100644 index 81bb094..0000000 --- a/modules/JiShe.CollectBus.FreeSql/DbEnum.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace JiShe.CollectBus.FreeSql -{ - /// - /// 数据库枚举 - /// - public enum DbEnum - { - /// - /// 微服务默认数据库,MySQL - /// - Default, - - /// - /// 预付费数据库,SQLSERVER - /// - PrepayDB, - - /// - /// 能耗数据库,SQLSERVER - /// - EnergyDB - } -} diff --git a/modules/JiShe.CollectBus.FreeSql/FreeSqlProvider.cs b/modules/JiShe.CollectBus.FreeSql/FreeSqlProvider.cs deleted file mode 100644 index e7e7191..0000000 --- a/modules/JiShe.CollectBus.FreeSql/FreeSqlProvider.cs +++ /dev/null @@ -1,37 +0,0 @@ -using FreeSql; -using Microsoft.Extensions.Configuration; -using Volo.Abp.DependencyInjection; - -namespace JiShe.CollectBus.FreeSql -{ - public class FreeSqlProvider : IFreeSqlProvider, ISingletonDependency - { - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - public FreeSqlProvider(IConfiguration configuration) - { - GetInstance(configuration); - } - - public FreeSqlCloud Instance { get; set; } = new(); - public FreeSqlCloud GetInstance(IConfiguration configuration) - { - Instance = new FreeSqlCloud - { - DistributeTrace = log => Console.WriteLine(log.Split('\n')[0].Trim()) - }; - - Instance.Register(DbEnum.EnergyDB, () => new FreeSqlBuilder() - .UseConnectionString(DataType.SqlServer, configuration.GetConnectionString(DbEnum.EnergyDB.ToString())) - .Build()); - - Instance.Register(DbEnum.PrepayDB, () => new FreeSqlBuilder() - .UseConnectionString(DataType.SqlServer, configuration.GetConnectionString(DbEnum.PrepayDB.ToString())) - .Build()); - return Instance; - } - - } -} diff --git a/modules/JiShe.CollectBus.FreeSql/IFreeSqlProvider.cs b/modules/JiShe.CollectBus.FreeSql/IFreeSqlProvider.cs deleted file mode 100644 index e4250d6..0000000 --- a/modules/JiShe.CollectBus.FreeSql/IFreeSqlProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FreeSql; - -namespace JiShe.CollectBus.FreeSql -{ - /// - /// FreeSqlProvider服务 - /// - public interface IFreeSqlProvider - { - FreeSqlCloud Instance { get; set; } - } -} diff --git a/modules/JiShe.CollectBus.FreeSql/JiShe.CollectBus.FreeSql.csproj b/modules/JiShe.CollectBus.FreeSql/JiShe.CollectBus.FreeSql.csproj deleted file mode 100644 index 9b40553..0000000 --- a/modules/JiShe.CollectBus.FreeSql/JiShe.CollectBus.FreeSql.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - diff --git a/modules/JiShe.CollectBus.IoTDB/Attributes/ATTRIBUTEColumnAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/ATTRIBUTEColumnAttribute.cs deleted file mode 100644 index 7ef13f3..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Attributes/ATTRIBUTEColumnAttribute.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace JiShe.CollectBus.IoTDB.Attributes -{ - /// - /// Column分类标记特性(ATTRIBUTE字段),也就是属性字段 - /// - [AttributeUsage(AttributeTargets.Property)] - public class ATTRIBUTEColumnAttribute : System.Attribute - { - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Attributes/FIELDColumnAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/FIELDColumnAttribute.cs deleted file mode 100644 index 43d699f..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Attributes/FIELDColumnAttribute.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace JiShe.CollectBus.IoTDB.Attributes -{ - /// - /// Column分类标记特性(FIELD字段),数据列字段 - /// - [AttributeUsage(AttributeTargets.Property)] - public class FIELDColumnAttribute : System.Attribute - { - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Attributes/IgnoreInitTableAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/IgnoreInitTableAttribute.cs deleted file mode 100644 index bd6006c..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Attributes/IgnoreInitTableAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace JiShe.CollectBus.IoTDB.Attributes -{ - /// - /// 需要忽略表模型初始化,有此特性无需初始化 - /// - [AttributeUsage(AttributeTargets.Class)] - public class IgnoreInitTableAttribute : System.Attribute - { - - public IgnoreInitTableAttribute() - { - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Attributes/SingleMeasuringAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/SingleMeasuringAttribute.cs deleted file mode 100644 index 481bfa2..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Attributes/SingleMeasuringAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace JiShe.CollectBus.IoTDB.Attributes -{ - /// - /// 用于标识当前实体为单侧点模式,单侧点模式只有一个Filed标识字段,类型是Tuple,Item1=>测点名称,Item2=>测点值,泛型 - /// - [AttributeUsage(AttributeTargets.Property)] - public class SingleMeasuringAttribute : System.Attribute - { - public string FieldName { get; set;} - - public SingleMeasuringAttribute(string fieldName) - { - FieldName = fieldName; - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Attributes/TAGColumnAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/TAGColumnAttribute.cs deleted file mode 100644 index 48a3830..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Attributes/TAGColumnAttribute.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace JiShe.CollectBus.IoTDB.Attributes -{ - /// - /// Column分类标记特性(TAG字段),标签字段 - /// - [AttributeUsage(AttributeTargets.Property)] - public class TAGColumnAttribute : System.Attribute - { - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Attributes/TableNameOrTreePathAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/TableNameOrTreePathAttribute.cs deleted file mode 100644 index 5f986b5..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Attributes/TableNameOrTreePathAttribute.cs +++ /dev/null @@ -1,17 +0,0 @@ - -namespace JiShe.CollectBus.IoTDB.Attributes -{ - /// - /// IoTDB实体存储路径或表名称,一般用于已经明确的存储路径或表名称,例如日志存储 - /// - [AttributeUsage(AttributeTargets.Class)] - public class TableNameOrTreePathAttribute : System.Attribute - { - public string TableNameOrTreePath { get; } - - public TableNameOrTreePathAttribute(string tableNameOrTreePath) - { - TableNameOrTreePath = tableNameOrTreePath; - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/CollectBusIoTDBModule.cs b/modules/JiShe.CollectBus.IoTDB/CollectBusIoTDBModule.cs deleted file mode 100644 index 6d26bdc..0000000 --- a/modules/JiShe.CollectBus.IoTDB/CollectBusIoTDBModule.cs +++ /dev/null @@ -1,22 +0,0 @@ -using JiShe.CollectBus.IoTDB.Context; -using JiShe.CollectBus.IoTDB.Options; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Modularity; - -namespace JiShe.CollectBus.IoTDB; - -/// -/// CollectBusIoTDBModule -/// -public class CollectBusIoTDbModule : AbpModule -{ - public override void ConfigureServices(ServiceConfigurationContext context) - { - var configuration = context.Services.GetConfiguration(); - Configure(options => { configuration.GetSection(nameof(IoTDbOptions)).Bind(options); }); - - //// 注册上下文为Scoped - //context.Services.AddScoped(); - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.IoTDB/Context/IoTDBRuntimeContext.cs b/modules/JiShe.CollectBus.IoTDB/Context/IoTDBRuntimeContext.cs deleted file mode 100644 index 7384716..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Context/IoTDBRuntimeContext.cs +++ /dev/null @@ -1,33 +0,0 @@ -using JiShe.CollectBus.IoTDB.Options; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; - -namespace JiShe.CollectBus.IoTDB.Context -{ - /// - /// IoTDB SessionPool 运行时上下文 - /// - public class IoTDBRuntimeContext: IScopedDependency//ITransientDependency - { - private readonly bool _defaultValue; - - public IoTDBRuntimeContext(IOptions options) - { - _defaultValue = options.Value.UseTableSessionPoolByDefault; - UseTableSessionPool = _defaultValue; - } - - /// - /// 存储模型切换标识,是否使用table模型存储, 默认为false,标识tree模型存储 - /// - public bool UseTableSessionPool { get; set; } - - /// - /// 重置为默认值 - /// - public void ResetToDefault() - { - UseTableSessionPool = _defaultValue; - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Exceptions/IoTException.cs b/modules/JiShe.CollectBus.IoTDB/Exceptions/IoTException.cs deleted file mode 100644 index 93bed4f..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Exceptions/IoTException.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.IoTDB.Exceptions -{ - /// - /// IoTDB异常 - /// - public class IoTException : Exception - { - public int ErrorCode { get; } - - public IoTException(string message, int errorCode) - : base($"{message} (Code: {errorCode})") - { - ErrorCode = errorCode; - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBProvider.cs b/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBProvider.cs deleted file mode 100644 index 08454b2..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBProvider.cs +++ /dev/null @@ -1,75 +0,0 @@ -using JiShe.CollectBus.Common.Models; -using JiShe.CollectBus.IoTDB.Model; -using JiShe.CollectBus.IoTDB.Options; -using JiShe.CollectBus.IoTDB.Provider; - -namespace JiShe.CollectBus.IoTDB.Interface -{ - /// - /// IoTDB数据源,数据库能同时存多个时序模型,但数据是完全隔离的,不能跨时序模型查询,通过连接字符串配置 - /// - public interface IIoTDbProvider - { - /// - /// 切换 SessionPool - /// - /// 是否使用表模型 - /// - IIoTDbProvider GetSessionPool(bool sessionpolType); - - /// - /// 插入数据 - /// - /// - /// - /// - Task InsertAsync(T entity) where T : IoTEntity; - - /// - /// 批量插入数据 - /// - /// - /// - /// - Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity; - - /// - /// 批量插入数据 - /// - /// - /// 设备元数据 - /// - /// - Task BatchInsertAsync(DeviceMetadata deviceMetadata,IEnumerable entities) where T : IoTEntity; - - - /// - /// 删除数据 - /// - /// - /// - /// - Task DeleteAsync(IoTDBQueryOptions options) where T : IoTEntity; - - /// - /// 获取设备元数据 - /// - /// - /// - Task GetMetadata() where T : IoTEntity; - - /// - /// 查询数据 - /// - /// - /// - /// - Task> QueryAsync(IoTDBQueryOptions options) where T : IoTEntity, new(); - - /// - /// 初始化表模型 - /// - /// - Task InitTableSessionModelAsync(); - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBSessionFactory.cs b/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBSessionFactory.cs deleted file mode 100644 index c2337ea..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBSessionFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace JiShe.CollectBus.IoTDB.Interface -{ - /// - /// Session 工厂接口 - /// - public interface IIoTDbSessionFactory:IDisposable - { - IIoTDbSessionPool GetSessionPool(bool useTableSession); - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBSessionPool.cs b/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBSessionPool.cs deleted file mode 100644 index d183099..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBSessionPool.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Apache.IoTDB.DataStructure; - -namespace JiShe.CollectBus.IoTDB.Interface -{ - /// - /// Session 连接池 - /// - public interface IIoTDbSessionPool : IDisposable - { - /// - /// 打开连接池 - /// - /// - Task OpenAsync(); - - /// - /// 关闭连接池 - /// - /// - Task CloseAsync(); - - /// - /// 插入数据 - /// - /// - /// - Task InsertAsync(Tablet tablet); - - /// - /// 查询数据 - /// - /// - /// - Task ExecuteQueryStatementAsync(string sql); - - /// - /// 执行无返回结果SQL - /// - /// - /// - Task ExecuteNonQueryStatementAsync(string sql); - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj b/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj deleted file mode 100644 index 78b81e3..0000000 --- a/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - diff --git a/modules/JiShe.CollectBus.IoTDB/Model/IoTEntity.cs b/modules/JiShe.CollectBus.IoTDB/Model/IoTEntity.cs deleted file mode 100644 index a8163d8..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Model/IoTEntity.cs +++ /dev/null @@ -1,72 +0,0 @@ -using JiShe.CollectBus.Common.Attributes; -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.IoTDB.Attributes; -using Volo.Abp.Domain.Entities; - -namespace JiShe.CollectBus.IoTDB.Model -{ - /// - /// IoT实体基类,此类适用于多个数据测点记录场景,单个测点请使用子类 SingleMeasuring,新增字段只能现有字段末尾添加,否则会导致数据写入失败。 - /// - public abstract class IoTEntity - { - /// - /// 系统名称 - /// - [TAGColumn] - public string SystemName { get; set; } - - /// - /// 项目编码 - /// - [TAGColumn] - public string ProjectId { get; set; } - - /// - /// 数据类型 - /// - [TAGColumn] - public string DataType { get; set; } = IOTDBDataTypeConst.Data; - - /// - /// 设备类型集中器、电表、水表、流量计、传感器等 - /// - [TAGColumn] - public string DeviceType { get; set; } - - /// - /// 设备ID,数据生成者,例如集中器ID,电表ID、水表ID、流量计ID、传感器ID等 - /// - [TAGColumn] - public string DeviceId { get; set; } - - /// - /// 时标,也就是业务时间戳,单位毫秒,必须通过DateTimeOffset获取 - /// - public long Timestamps { get; set; } = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - - /// - /// 设备路径 - /// - private string _devicePath; - /// - /// 设备路径,树模型使用,表模型会在数据插入的时候直接获取继承类的名称作为表明 - /// - public virtual string DevicePath - { - get - { - // 如果未手动设置路径,则自动生成 - if (string.IsNullOrWhiteSpace(_devicePath)) - { - return $"root.{SystemName.ToLower()}.`{ProjectId}`.`{DeviceType}`.{DataType}.`{DeviceId}`"; - } - return _devicePath; - } - set - { - _devicePath = value; // 直接赋值给支持字段,避免递归 - } - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs b/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs deleted file mode 100644 index 89ea088..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs +++ /dev/null @@ -1,19 +0,0 @@ -using JiShe.CollectBus.Analyzers.Shared; -using JiShe.CollectBus.IoTDB.Attributes; - -namespace JiShe.CollectBus.IoTDB.Model -{ - /// - /// Table模型单项数据实体 - /// - [SourceAnalyzers(EntityTypeEnum.TableModel)] - [IgnoreInitTable] - public class TableModelSingleMeasuringEntity : IoTEntity - { - /// - /// 单项数据键值对 - /// - [SingleMeasuring(nameof(SingleColumn))] - public required ValueTuple SingleColumn { get; set; } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Model/TreeModelSingleMeasuringEntity.cs b/modules/JiShe.CollectBus.IoTDB/Model/TreeModelSingleMeasuringEntity.cs deleted file mode 100644 index 56a6c54..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Model/TreeModelSingleMeasuringEntity.cs +++ /dev/null @@ -1,18 +0,0 @@ -using JiShe.CollectBus.Analyzers.Shared; -using JiShe.CollectBus.IoTDB.Attributes; - -namespace JiShe.CollectBus.IoTDB.Model -{ - /// - /// Tree模型单项数据实体 - /// - [SourceAnalyzers(EntityTypeEnum.TreeModel)] - public class TreeModelSingleMeasuringEntity : IoTEntity - { - /// - /// 单项数据键值对 - /// - [SingleMeasuring(nameof(SingleMeasuring))] - public required ValueTuple SingleMeasuring { get; set; } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Options/IoTDBOptions.cs b/modules/JiShe.CollectBus.IoTDB/Options/IoTDBOptions.cs deleted file mode 100644 index 68b2770..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Options/IoTDBOptions.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace JiShe.CollectBus.IoTDB.Options -{ - /// - /// IOTDB配置 - /// - public class IoTDbOptions - { - /// - /// 数据库名称,表模型才有,树模型为空 - /// - public string DataBaseName { get; set; } - - /// - /// 集群列表 - /// - public List ClusterList { get; set; } - /// - /// 用户名 - /// - public string UserName { get; set; } - /// - /// 密码 - /// - public string Password { get; set; } - - /// - /// 连接池大小 - /// - public int PoolSize { get; set; } = 8; - - /// - /// 查询时,每次查询的数据量,默认1024 - /// - public int FetchSize { get; set; } = 1024; - - /// - /// 是否开启调试模式,生产环境请关闭,因为底层的实现方式,可能会导致内存持续增长。 - /// - public bool OpenDebugMode { get; set;} - - /// - /// 是否使用表模型存储, 默认false,使用tree模型存储 - /// - public bool UseTableSessionPoolByDefault { get; set; } = false; - - /// - /// 时区,默认为:"UTC+08:00" - /// - public string ZoneId { get; set; } = "UTC+08:00"; - - /// - /// 请求超时时间,单位毫秒,默认为:50000 - /// - public long Timeout { get; set; } = 50000; - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Options/IoTDBQueryOptions.cs b/modules/JiShe.CollectBus.IoTDB/Options/IoTDBQueryOptions.cs deleted file mode 100644 index bff4641..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Options/IoTDBQueryOptions.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace JiShe.CollectBus.IoTDB.Options -{ - /// - /// 查询条件 - /// - public class IoTDBQueryOptions - { - /// - /// 表模型的表名称或者树模型的设备路径 - /// - public required string TableNameOrTreePath { get; set; } - - /// - /// 分页 - /// - public int PageIndex { get; set; } - - /// - /// 分页大小 - /// - public int PageSize { get; set; } - - /// - /// 查询条件 - /// - public List Conditions { get; set; } = new(); - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Options/QueryCondition.cs b/modules/JiShe.CollectBus.IoTDB/Options/QueryCondition.cs deleted file mode 100644 index bccf017..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Options/QueryCondition.cs +++ /dev/null @@ -1,64 +0,0 @@ -using JiShe.CollectBus.Common.Extensions; -using JiShe.CollectBus.Common.Helpers; - -namespace JiShe.CollectBus.IoTDB.Options -{ - /// - /// 查询条件 - /// - public class QueryCondition - { - /// - /// 字段 - /// - public string Field { get; set; } - - /// - /// 操作符,>,=,< - /// - public string Operator { get; set; } - - /// - /// 是否数值,如果是数值,则进行数值比较,否则进行字符串比较 - /// - public bool IsNumber { get; set; } = false; - - private object _rawValue; - /// - /// 值 - /// - public object Value - { - get => ApplyValueConversion(_rawValue); - set => _rawValue = value; - } - - /// - /// 值转换 - /// - /// - /// - private object ApplyValueConversion(object rawValue) - { - string declaredTypeName = rawValue.GetType().Name; - - Func converter = GetQueryConditionValue(declaredTypeName); - return converter(rawValue); - } - - /// - /// 查询条件值转换委托 - /// - /// - /// - private Func GetQueryConditionValue(string declaredTypeName) - { - return declaredTypeName?.ToUpper() switch - { - "DATETIME" => v => v != null ? ((DateTime)v).ToUniversalTime().Ticks : null, - "STRING" => v => v != null ? $"'{v}'" : "''", - _ => v => v - }; - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Provider/DeviceMetadata.cs b/modules/JiShe.CollectBus.IoTDB/Provider/DeviceMetadata.cs deleted file mode 100644 index a93ba35..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Provider/DeviceMetadata.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Apache.IoTDB; -using JiShe.CollectBus.Analyzers.Shared; - -namespace JiShe.CollectBus.IoTDB.Provider -{ - /// - /// 设备元数据 - /// - public sealed class DeviceMetadata - { - /// - /// 实体类名称 - /// - public string EntityName { get; set; } - - /// - /// 设备表名或树路径,如果实体没有添加TableNameOrTreePath,此处为空 - /// - public string TableNameOrTreePath { get; set; } - - /// - /// 实体类型枚举 - /// - public EntityTypeEnum? EntityType { get; set; } - - /// - /// 是否有单测量值 - /// - public bool IsSingleMeasuring { get; set; } - - /// - /// 测量值集合,用于构建Table的测量值,也就是columnNames参数 - /// - public List ColumnNames { get; set; } = new(); - - /// - /// 列类型集合,用于构建Table的列类型,也就是columnCategories参数 - /// - public List ColumnCategories { get; } = new(); - - /// - /// 值类型集合,用于构建Table的值类型,也就是dataTypes参数 - /// - public List DataTypes { get; set; } = new(); - - /// - /// 列处理信息集合 - /// - public List Processors { get; } = new List(); - } - - /// - /// 列处理信息结构 - /// - public struct ColumnProcessor - { - /// - /// 列名 - /// - public string ColumnName; - - /// - /// 数据类型 - /// - public TSDataType TSDataType { get; set;} - - /// - /// 值获取委托(参数:实体对象) - /// - public Func ValueGetter; - - /// - /// 值设置委托(参数:实体对象,新值) - /// - public Action ValueSetter; - - /// - /// 类型转换委托 - /// - public Func GetConverter; - - /// - /// 类型转换委托 - /// - public Func SetConverter; - - /// - /// 是否单测点 - /// - public bool IsSingleMeasuring; - - /// - /// 单测点名称委托 - /// - public Func SingleMeasuringNameGetter; - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Provider/DevicePathBuilder.cs b/modules/JiShe.CollectBus.IoTDB/Provider/DevicePathBuilder.cs deleted file mode 100644 index 57e1b09..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Provider/DevicePathBuilder.cs +++ /dev/null @@ -1,46 +0,0 @@ -using JiShe.CollectBus.IoTDB.Model; - -namespace JiShe.CollectBus.IoTDB.Provider -{ - /// - /// 设备路径构建器 - /// - public static class DevicePathBuilder - { - /// - /// 构建设备路径,由于路径的层级约束规范不能是纯数字字符,所以需要做特殊处理。 - /// - /// - /// - /// - public static string GetDevicePath(T entity) where T : IoTEntity - { - return $"root.{entity.SystemName.ToLower()}.`{entity.ProjectId}`.`{entity.DeviceType}`.{entity.DataType}.`{entity.DeviceId}`"; - } - - - /// - /// 获取表名称 - /// - /// - /// - /// - public static string GetTableName() where T : IoTEntity - { - var type = typeof(T); - return $"{type.Name.ToLower()}"; - } - - /// - /// 获取表名称,用作单侧点表模型特殊处理。 - /// - /// - /// - /// - public static string GetDeviceTableName(T entity) where T : IoTEntity - { - return $"{entity.SystemName.ToLower()}.`{entity.ProjectId}`.`{entity.DeviceType}`.`{entity.DeviceId}`"; - } - } - -} diff --git a/modules/JiShe.CollectBus.IoTDB/Provider/IoTDBProvider.cs b/modules/JiShe.CollectBus.IoTDB/Provider/IoTDBProvider.cs deleted file mode 100644 index 1d22c33..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Provider/IoTDBProvider.cs +++ /dev/null @@ -1,1133 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; -using System.Reflection; -using System.Reflection.Metadata.Ecma335; -using System.Text; -using System.Threading.Tasks; -using Apache.IoTDB; -using Apache.IoTDB.DataStructure; -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Extensions; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.Common.Models; -using JiShe.CollectBus.IoTDB.Attributes; -using JiShe.CollectBus.IoTDB.Context; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IoTDB.Model; -using JiShe.CollectBus.IoTDB.Options; -using Microsoft.Extensions.Logging; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Domain.Entities; -using JiShe.CollectBus.Analyzers.Shared; -using JiShe.CollectBus.IoTDB.Exceptions; -using System.Diagnostics.Metrics; -using Newtonsoft.Json.Linq; -using static System.Runtime.InteropServices.JavaScript.JSType; -using System.Text.RegularExpressions; -using System.Xml.Linq; -using System.Linq; - -namespace JiShe.CollectBus.IoTDB.Provider -{ - /// - /// IoTDB数据源 - /// - public class IoTDbProvider : IIoTDbProvider, ITransientDependency - { - private static readonly ConcurrentDictionary MetadataCache = new(); - private readonly ILogger _logger; - private readonly IIoTDbSessionFactory _sessionFactory; - - /// - /// 存储模型切换标识,是否使用table模型存储, 默认为false,标识tree模型存储 - /// - public bool UseTableSessionPool { get; set; } - - private IIoTDbSessionPool CurrentSession { get; set; } - - public IIoTDbProvider GetSessionPool(bool useTableSessionPool) - { - CurrentSession = _sessionFactory.GetSessionPool(useTableSessionPool); - UseTableSessionPool = useTableSessionPool; - return this; - } - - - /// - /// IoTDbProvider - /// - /// - /// - /// - public IoTDbProvider( - ILogger logger, - IIoTDbSessionFactory sessionFactory) - { - _logger = logger; - _sessionFactory = sessionFactory; - - } - - /// - /// 插入数据 - /// - /// - /// - /// - public async Task InsertAsync(T entity) where T : IoTEntity - { - try - { - var metadata = await GetMetadata(); - - var tablet = BuildTablet(new[] { entity }, metadata); - if (tablet == null || tablet.Count <= 0) - { - _logger.LogError($"{nameof(InsertAsync)} IoTDB插入{typeof(T).Name}的数据时 tablet 为null"); - return; - } - _logger.LogWarning($"{nameof(InsertAsync)} IoTDB插入{typeof(T).Name}的数据时 路径为 {tablet.First().InsertTargetName}"); - - await CurrentSession.InsertAsync(tablet.First()); - } - catch (Exception ex) - { - _logger.LogError(ex, $"{nameof(InsertAsync)} IoTDB插入{typeof(T).Name}的数据时发生异常"); - throw; - } - } - - /// - /// 批量插入数据 - /// - /// - /// - public async Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity - { - try - { - if (entities == null || entities.Count() <= 0) - { - _logger.LogError($"{nameof(BatchInsertAsync)} 参数异常,-101"); - return; - } - var metadata = await GetMetadata(); - - var batchSize = 1000; - var batches = entities.Chunk(batchSize); - - foreach (var batch in batches) - { - var tablet = BuildTablet(batch, metadata); - if (tablet == null || tablet.Count <= 0) - { - _logger.LogError($"{nameof(InsertAsync)} IoTDB插入{typeof(T).Name}的数据时 tablet 为null"); - return; - } - - foreach (var item in tablet) - { - _logger.LogWarning($"{nameof(InsertAsync)} IoTDB插入{typeof(T).Name}的数据时 路径为 {item.InsertTargetName}"); - - await CurrentSession.InsertAsync(item); - } - } - } - catch (Exception ex) - { - _logger.LogError(ex, $"{nameof(BatchInsertAsync)} IoTDB批量插入{typeof(T).Name}的数据时发生异常"); - throw; - } - } - - /// - /// 批量插入数据 - /// - /// - /// 设备元数据 - /// - /// - public async Task BatchInsertAsync(DeviceMetadata deviceMetadata, IEnumerable entities) where T : IoTEntity - { - try - { - var batchSize = 1000; - var batches = entities.Chunk(batchSize); - - foreach (var batch in batches) - { - var tablet = BuildTablet(batch, deviceMetadata); - if (tablet == null || tablet.Count <= 0) - { - _logger.LogError($"{nameof(InsertAsync)} IoTDB插入{typeof(T).Name}的数据时 tablet 为null"); - return; - } - foreach (var item in tablet) - { - await CurrentSession.InsertAsync(item); - } - } - } - catch (Exception ex) - { - _logger.LogError(ex, $"{nameof(BatchInsertAsync)} IoTDB批量插入{typeof(T).Name}的数据时发生异常"); - throw; - } - } - - - /// - /// 删除数据 - /// - /// - /// - /// - public async Task DeleteAsync(IoTDBQueryOptions options) where T : IoTEntity - { - try - { - var query = await BuildDeleteSQL(options); - var result = await CurrentSession.ExecuteQueryStatementAsync(query); - - if (result == null) - { - return 0; - } - - if (!result.HasNext()) - { - _logger.LogWarning($"{typeof(T).Name} IoTDB删除{typeof(T).Name}的数据时,没有返回受影响记录数量。"); - return 0; - } - - //获取唯一结果行 - var row = result.Next(); - await result.Close(); - var dataResult = row.Values[0]; - return dataResult; - } - catch (Exception ex) - { - _logger.LogError(ex, $"{nameof(DeleteAsync)} IoTDB删除{typeof(T).Name}的数据时发生异常"); - throw; - } - } - - /// - /// 获取设备元数据 - /// - /// - /// - public async Task GetMetadata() where T : IoTEntity - { - var accessor = SourceEntityAccessorFactory.GetAccessor(); - - var columns = CollectColumnMetadata(accessor); - var tmpMetadata = BuildDeviceMetadata(columns, accessor); - - string tableNameOrTreePath = string.Empty; - var tableNameOrTreePathAttribute = typeof(T).GetCustomAttribute(); - if (tableNameOrTreePathAttribute != null) - { - tableNameOrTreePath = tableNameOrTreePathAttribute.TableNameOrTreePath; - } - tmpMetadata.EntityName = accessor.EntityName; - tmpMetadata.EntityType = accessor.EntityType; - tmpMetadata.TableNameOrTreePath = tableNameOrTreePath; - - var metaData = MetadataCache.AddOrUpdate( - typeof(T), - addValueFactory: t => tmpMetadata, // 如果键不存在,用此值添加 - updateValueFactory: (t, existingValue) => - { - var columns = CollectColumnMetadata(accessor); - var metadata = BuildDeviceMetadata(columns, accessor); - - //对现有值 existingValue 进行修改,返回新值 - string tableNameOrTreePath = string.Empty; - var tableNameOrTreePathAttribute = typeof(T).GetCustomAttribute(); - if (tableNameOrTreePathAttribute != null) - { - tableNameOrTreePath = tableNameOrTreePathAttribute.TableNameOrTreePath; - } - existingValue.ColumnNames = metadata.ColumnNames; - existingValue.DataTypes = metadata.DataTypes; - return existingValue; - } - ); - - //var metaData = MetadataCache.GetOrAdd(typeof(T), type => - //{ - // var columns = CollectColumnMetadata(accessor); - // var metadata = BuildDeviceMetadata(columns, accessor); - // string tableNameOrTreePath = string.Empty; - // var tableNameOrTreePathAttribute = typeof(T).GetCustomAttribute(); - // if (tableNameOrTreePathAttribute != null) - // { - // tableNameOrTreePath = tableNameOrTreePathAttribute.TableNameOrTreePath; - // } - // metadata.EntityName = accessor.EntityName; - // metadata.EntityType = accessor.EntityType; - // metadata.TableNameOrTreePath = tableNameOrTreePath; - // return metadata; - //}); - - return await Task.FromResult(metaData); - } - - /// - /// 查询数据 - /// - /// - /// - /// - public async Task> QueryAsync(IoTDBQueryOptions options) where T : IoTEntity, new() - { - try - { - var stopwatch2 = Stopwatch.StartNew(); - - var query = await BuildQuerySQL(options); - var sessionDataSet = await CurrentSession.ExecuteQueryStatementAsync(query); - - - var result = new BusPagedResult - { - TotalCount = await GetTotalCount(options), - Items = await ParseResults(sessionDataSet, options.PageSize), - PageIndex = options.PageIndex, - PageSize = options.PageSize, - - }; - stopwatch2.Stop(); - - //int totalPageCount = (int)Math.Ceiling((double)result.TotalCount / options.PageSize); - - if (result.Items.Count() < result.PageSize) - { - result.HasNext = false; - } - else - { - result.HasNext = true; - } - - //result.HasNext = result.Items.Count() > 0 ? result.Items.Count() < result.PageSize : false; - - return result; - } - catch (Exception ex) - { - CurrentSession.Dispose(); - _logger.LogError(ex, $"{nameof(QueryAsync)} IoTDB查询{typeof(T).Name}的数据时发生异常"); - throw; - } - } - - /// - /// 构建Tablet - /// - /// - /// 表实体 - /// 设备元数据 - /// - private List BuildTablet(IEnumerable entities, DeviceMetadata metadata) where T : IoTEntity - { - var entitiyList = entities.ToList(); - if (entitiyList == null || entitiyList.Count <= 0) - { - return null; - } - - - if (metadata.EntityType == null) - { - throw new ArgumentException($"{nameof(BuildTablet)} 构建存储结构{nameof(Tablet)}时 {nameof(T)}的EntityType 没有指定,属于异常情况,-101"); - } - - if (metadata.EntityType == EntityTypeEnum.Other) - { - throw new ArgumentException($"{nameof(BuildTablet)} 构建存储结构{nameof(Tablet)}时 {nameof(T)}的EntityType 不属于IoTDB数据模型实体,属于异常情况,-102"); - } - - if (metadata.EntityType == EntityTypeEnum.TreeModel && UseTableSessionPool == true) - { - throw new ArgumentException($"{nameof(BuildTablet)} 构建存储结构{nameof(Tablet)}时 tree模型不能使用table模型Session连接,属于异常情况,-103"); - } - else if (metadata.EntityType == EntityTypeEnum.TableModel && UseTableSessionPool == false) - { - throw new Exception($"{nameof(BuildTablet)} 构建存储结构{nameof(Tablet)}时 table模型不能使用tree模型Session连接,属于异常情况,-104"); - } - string tableNameOrTreePath = string.Empty; - if ( UseTableSessionPool)//表模型 - { - //如果指定了路径 - if (!string.IsNullOrWhiteSpace(metadata.TableNameOrTreePath)) - { - tableNameOrTreePath = metadata.TableNameOrTreePath; - } - else if(metadata.IsSingleMeasuring && string.IsNullOrWhiteSpace(metadata.TableNameOrTreePath) == true)//单侧点时,且路径没有指定,取默认实体名称和第一个列名组合。 - { - tableNameOrTreePath = $"{metadata.EntityName}_{metadata.ColumnNames.First()}"; - } - else - { - tableNameOrTreePath = DevicePathBuilder.GetTableName(); - } - - return new List() { BuildTablet(entitiyList, metadata, tableNameOrTreePath) }; - } - else - { - //树模型的时候,实体的设备Id可能会不同,因此需要根据不同路径进行存储。 - var tabletList = new List(); - var groupEntities = entitiyList.GroupBy(d => d.DevicePath).ToList(); - foreach (var group in groupEntities) - { - tabletList.Add(BuildTablet(group.ToList(), metadata, group.Key)); - } - - return tabletList; - } - } - - private Tablet BuildTablet(List entities, DeviceMetadata metadata, string tableNameOrTreePath) where T : IoTEntity - { - // 预分配内存结构 - var rowCount = entities.Count; - var timestamps = new long[rowCount]; - var values = new object[rowCount][]; - for (var i = 0; i < values.Length; i++) - { - values[i] = new object[metadata.ColumnNames.Count]; - } - - List tempColumnNames = new List(); - tempColumnNames.AddRange(metadata.ColumnNames); - - // 顺序处理数据(保证线程安全) - for (var row = 0; row < rowCount; row++) - { - var entity = entities[row]; - timestamps[row] = entity.Timestamps; - - for (int i = 0; i < metadata.ColumnNames.Count; i++) - { - var processor = metadata.Processors[i]; - if (processor.IsSingleMeasuring) - { - tempColumnNames[i] = (string)processor.SingleMeasuringNameGetter(entity); - } - - // 获取并转换值 - values[row][i] = processor.ValueGetter(entity); - } - } - - return UseTableSessionPool - ? BuildTableSessionTablet(metadata, tableNameOrTreePath, tempColumnNames, values.Select(d => d.ToList()).ToList(), timestamps.ToList()) - : BuildSessionTablet(metadata, tableNameOrTreePath, tempColumnNames, values.Select(d => d.ToList()).ToList(), timestamps.ToList()); - } - - - /// - /// 构建tree模型的Tablet - /// - /// 已解析的设备数据元数据 - /// 设备路径 - /// 数据列集合 - /// 数据集合 - /// 时间戳集合 - /// - private Tablet BuildSessionTablet(DeviceMetadata metadata, string devicePath, List columns, List> values, List timestamps) - { - //todo 树模型需要去掉TAG类型和ATTRIBUTE类型的字段,只需要保留FIELD类型字段即可 - - return new Tablet( - devicePath, - columns, - metadata.DataTypes, - values, - timestamps - ); - } - - /// - /// 构建表模型的Tablet - /// - /// 已解析的设备数据元数据 - /// 表名称 - /// 数据列集合 - /// 数据集合 - /// 时间戳集合 - /// - private Tablet BuildTableSessionTablet(DeviceMetadata metadata, string tableName, List columns, List> values, List timestamps) - { - var tablet = new Tablet( - tableName, - columns, - metadata.ColumnCategories, - metadata.DataTypes, - values, - timestamps - ); - - return tablet; - } - - /// - /// 构建查询语句 - /// - /// - /// - /// - private async Task BuildQuerySQL(IoTDBQueryOptions options) where T : IoTEntity - { - var metadata = await GetMetadata(); - var sb = new StringBuilder("SELECT "); - sb.AppendJoin(", ", metadata.ColumnNames); - sb.Append($" FROM {options.TableNameOrTreePath}"); - - if (options.Conditions.Any()) - { - sb.Append(" WHERE "); - sb.AppendJoin(" AND ", options.Conditions.Select(TranslateCondition)); - } - - sb.Append($" LIMIT {options.PageSize} OFFSET {options.PageIndex * options.PageSize}"); - return sb.ToString(); - } - - /// - /// 构建删除语句 - /// - /// - /// - /// - private async Task BuildDeleteSQL(IoTDBQueryOptions options) where T : IoTEntity - { - var metadata = await GetMetadata(); - var sb = new StringBuilder(); - - if (!UseTableSessionPool) - { - sb.Append("DELETE "); - } - else - { - sb.Append("DROP "); - } - - sb.Append($" FROM {options.TableNameOrTreePath}"); - - sb.AppendJoin(", ", metadata.ColumnNames); - - if (options.Conditions.Any()) - { - sb.Append(" WHERE "); - sb.AppendJoin(" AND ", options.Conditions.Select(TranslateCondition)); - } - - return sb.ToString(); - } - - /// - /// 将查询条件转换为SQL语句 - /// - /// - /// - /// - private string TranslateCondition(QueryCondition condition) - { - return condition.Operator switch - { - ">" => $"{condition.Field} > {condition.Value}", - "<" => $"{condition.Field} < {condition.Value}", - "=" => $"{condition.Field} = {condition.Value}", - _ => throw new NotSupportedException($"{nameof(TranslateCondition)} 将查询条件转换为SQL语句时操作符 {condition.Operator} 属于异常情况") - }; - } - - /// - /// 获取查询条件的总数量 - /// - /// - /// - /// - private async Task GetTotalCount(IoTDBQueryOptions options) where T : IoTEntity - { - var countQuery = $"SELECT COUNT(*) FROM {options.TableNameOrTreePath}"; - if (options.Conditions.Any()) - { - countQuery += " WHERE " + string.Join(" AND ", options.Conditions.Select(TranslateCondition)); - } - - var result = await CurrentSession.ExecuteQueryStatementAsync(countQuery); - if (result == null) - { - return 0; - } - - if (!result.HasNext()) - { - return 0; - } - - - var count = Convert.ToInt32(result.Next().Values[0]); - await result.Close(); - - return count; - } - - /// - /// 解析查询结果 - /// - /// - /// - /// - /// - private async Task> ParseResults(SessionDataSet dataSet, int pageSize) where T : IoTEntity, new() - { - var results = new List(); - var metadata = await GetMetadata(); - - var accessor = SourceEntityAccessorFactory.GetAccessor(); - var memberCache = BuildMemberCache(accessor); - - - while (dataSet.HasNext() && results.Count < pageSize) - { - var record = dataSet.Next(); - var entity = new T - { - Timestamps = record.Timestamps - }; - - for (int i = 0; i < metadata.Processors.Count; i++) - { - var value = record.Values[i]; - if (!(value is System.DBNull)) - { - metadata.Processors[i].ValueSetter(entity, value); - } - } - - results.Add(entity); - - } - await dataSet.Close(); - return results; - } - - /// - /// 获取设备元数据的列 - /// - /// - /// - private List CollectColumnMetadata(ISourceEntityAccessor accessor) - { - var columns = new List(); - var memberCache = BuildMemberCache(accessor); - - foreach (var member in accessor.MemberList) - { - // 过滤元组子项 - if (member.NameOrPath.Contains(".Item")) continue; - - // 特性查询优化 - var attributes = member.CustomAttributes ?? Enumerable.Empty(); - var tagAttr = attributes.OfType().FirstOrDefault(); - var attrColumn = attributes.OfType().FirstOrDefault(); - var fieldColumn = attributes.OfType().FirstOrDefault(); - var singleMeasuringAttr = attributes.OfType().FirstOrDefault(); - - // 构建ColumnInfo - ColumnInfo? column = null; - if (tagAttr != null) - { - column = new ColumnInfo(member.NameOrPath, ColumnCategory.TAG, GetDataTypeFromTypeName(member.DeclaredTypeName), false, member.DeclaredTypeName); - } - else if (attrColumn != null) - { - column = new ColumnInfo(member.NameOrPath, ColumnCategory.ATTRIBUTE, GetDataTypeFromTypeName(member.DeclaredTypeName), false, member.DeclaredTypeName); - } - else if (fieldColumn != null) - { - column = new ColumnInfo(member.NameOrPath, ColumnCategory.FIELD, GetDataTypeFromTypeName(member.DeclaredTypeName), false, member.DeclaredTypeName); - } - - // 单测模式处理 - if (singleMeasuringAttr != null && column == null) - { - var tupleItemKey = $"{member.NameOrPath}.Item2"; - if (!memberCache.TryGetValue(tupleItemKey, out var tupleMember)) - { - throw new Exception($"{nameof(CollectColumnMetadata)} {accessor.EntityName} {member.NameOrPath} 单侧点属性解析异常"); - } - column = new ColumnInfo(member.NameOrPath, ColumnCategory.FIELD, GetDataTypeFromTypeName(tupleMember.DeclaredTypeName), true, tupleMember.DeclaredTypeName); - } - - if (column.HasValue) columns.Add(column.Value); - } - return columns; - } - - /// - /// 构建设备元数据 - /// - /// 待解析的类 - /// 已处理好的数据列 - /// - private DeviceMetadata BuildDeviceMetadata(List columns, ISourceEntityAccessor accessor) where T : IoTEntity - { - var metadata = new DeviceMetadata(); - - //先检查是不是单侧点模型 - if (columns.Any(c => c.IsSingleMeasuring)) - { - metadata.IsSingleMeasuring = true; - } - - //按业务逻辑顺序处理(TAG -> ATTRIBUTE -> FIELD) - var groupedColumns = columns - .GroupBy(c => c.Category) - .ToDictionary(g => g.Key, g => g.ToList()); - - ProcessCategory(groupedColumns, ColumnCategory.TAG, metadata); - ProcessCategory(groupedColumns, ColumnCategory.ATTRIBUTE, metadata); - ProcessCategory(groupedColumns, ColumnCategory.FIELD, metadata); - - // 新增处理器初始化 - foreach (var item in metadata.ColumnNames) - { - ColumnInfo column = columns.FirstOrDefault(d => d.Name == item); - - var processor = new ColumnProcessor - { - ColumnName = column.Name, - IsSingleMeasuring = column.IsSingleMeasuring, - GetConverter = GetterConverter(column.DeclaredTypeName.ToUpper()), - SetConverter = SetterConverter(column.Name.ToUpper()), - TSDataType = column.DataType, - }; - - // 处理单测点 - if (column.IsSingleMeasuring) - { - var item1Member = accessor.MemberList - .First(m => m.NameOrPath == $"{column.Name}.Item1"); - - processor.SingleMeasuringNameGetter = (obj) => - { - // 获取原始值并转为字符串 - object rawValue = item1Member.Getter(obj); - string value = rawValue?.ToString(); - - ValidateSingleMeasuringName(value); - - return value; - }; - - var item2Member = accessor.MemberList - .First(m => m.NameOrPath == $"{column.Name}.Item2"); - processor.ValueGetter = (obj) => - { - object rawValue = item2Member.Getter(obj); - return processor.GetConverter(rawValue); - }; - } - else - { - // 获取对应的成员访问器 - var member = accessor.MemberList.First(m => m.NameOrPath == column.Name); - processor.ValueGetter = (obj) => - { - object rawValue = member.Getter(obj); - return processor.GetConverter(rawValue); - }; - - //对应的属性成员进行赋值 - processor.ValueSetter = (obj, value) => - { - dynamic tempValue = GetTSDataValue(processor.TSDataType, value); - var rawValue = processor.SetConverter(value); - member.Setter(obj, rawValue); - }; - } - - metadata.Processors.Add(processor); - } - - return metadata; - } - - /// - /// 验证单测点名称格式 - /// - private void ValidateSingleMeasuringName(string value) - { - if (string.IsNullOrWhiteSpace(value)) - { - return; - } - - // 规则1: 严格检查ASCII字母和数字(0-9, A-Z, a-z) - bool hasInvalidChars = value.Any(c => - !((c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9'))); - - // 规则2: 首字符不能是数字 - bool startsWithDigit = value[0] >= '0' && value[0] <= '9'; - - // 规则3: 全字符串不能都是数字 - bool allDigits = value.All(c => c >= '0' && c <= '9'); - - // 按优先级抛出具体异常 - if (hasInvalidChars) - { - throw new InvalidOperationException( - $"SingleMeasuring name '{value}' 包含非法字符,只允许字母和数字"); - } - else if (startsWithDigit) - { - throw new InvalidOperationException( - $"SingleMeasuring name '{value}' 不能以数字开头"); - } - else if (allDigits) - { - throw new InvalidOperationException( - $"SingleMeasuring name '{value}' 不能全为数字"); - } - } - - /// - /// 取值的处理器 - /// - /// - /// - private Func GetterConverter(string declaredTypeName) - { - return declaredTypeName switch - { - "DATETIME" => value => value != null ? Convert.ToDateTime(value).GetDateTimeOffset().ToUnixTimeNanoseconds() : null, - "DECIMAL" => value => value != null ? Convert.ToDouble( value) : null, - _ => value => value - }; - } - - /// - /// 设置值的处理 - /// - /// - /// - private Func SetterConverter(string columnName) => - columnName.ToLower().EndsWith("time") - ? value => value != null ? TimestampHelper.ConvertToDateTime(Convert.ToInt64(value), TimestampUnit.Nanoseconds) : null - : value => value; - - /// - /// 处理不同列类型的逻辑 - /// - /// - /// - /// - private void ProcessCategory(IReadOnlyDictionary> groupedColumns, ColumnCategory category, DeviceMetadata metadata) - { - if (groupedColumns.TryGetValue(category, out var cols)) - { - metadata.ColumnNames.AddRange(cols.Select(c => c.Name)); - metadata.ColumnCategories.AddRange(cols.Select(c => c.Category)); - metadata.DataTypes.AddRange(cols.Select(c => c.DataType)); - } - } - - /// - /// 处理不同列类型的逻辑 - /// - /// - /// - private string ProcessCategory(IReadOnlyDictionary> groupedColumns, ColumnCategory category) - { - if (groupedColumns.TryGetValue(category, out var cols)) - { - List tempColumnInfos = new List(); - foreach (var item in cols) - { - tempColumnInfos.Add($" {item.Name} {item.DataType} {item.Category}"); - } - return string.Join(",", tempColumnInfos); - } - return string.Empty; - } - - /// - /// 数据列结构 - /// - private readonly struct ColumnInfo - { - /// - /// 列名 - /// - public string Name { get; } - - /// - /// 声明的类型的名称 - /// - public string DeclaredTypeName { get; } - - /// - /// 是否是单测点 - /// - public bool IsSingleMeasuring { get; } - - /// - /// 列类型 - /// - public ColumnCategory Category { get; } - - /// - /// 数据类型 - /// - public TSDataType DataType { get; } - - public ColumnInfo(string name, ColumnCategory category, TSDataType dataType, bool isSingleMeasuring, string declaredTypeName) - { - Name = name; - Category = category; - DataType = dataType; - IsSingleMeasuring = isSingleMeasuring; - DeclaredTypeName = declaredTypeName; - } - } - - /// - /// 根据类型名称获取对应的 IoTDB 数据类型 - /// - /// 类型名称(不区分大小写) - /// 对应的 TSDataType,默认返回 TSDataType.STRING - private TSDataType GetDataTypeFromTypeName(string typeName) - { - if (string.IsNullOrWhiteSpace(typeName)) - return TSDataType.STRING; - - return DataTypeMap.TryGetValue(typeName.Trim(), out var dataType) - ? dataType - : TSDataType.STRING; - } - - /// - /// 根据类型名称获取 IoTDB 数据类型 - /// - private readonly IReadOnlyDictionary DataTypeMap = - new Dictionary(StringComparer.OrdinalIgnoreCase) - { - ["BOOLEAN"] = TSDataType.BOOLEAN, - ["INT32"] = TSDataType.INT32, - ["INT64"] = TSDataType.INT64, - ["FLOAT"] = TSDataType.FLOAT, - ["DOUBLE"] = TSDataType.DOUBLE, - ["TEXT"] = TSDataType.TEXT, - ["NULLTYPE"] = TSDataType.NONE, - ["DATETIME"] = TSDataType.TIMESTAMP, - ["DATE"] = TSDataType.DATE, - ["BLOB"] = TSDataType.BLOB, - ["DECIMAL"] = TSDataType.DOUBLE, - ["STRING"] = TSDataType.STRING - }; - - /// - /// 根据类型名称获取 IoTDB 数据默认值 - /// - private readonly IReadOnlyDictionary DataTypeDefaultValueMap = - new Dictionary(StringComparer.OrdinalIgnoreCase) - { - ["BOOLEAN"] = false, - ["INT32"] = 0, - ["INT64"] = 0, - ["FLOAT"] = 0.0f, - ["DOUBLE"] = 0.0d, - ["TEXT"] = string.Empty, - ["NULLTYPE"] = null, - ["DATETIME"] = null, - ["DATE"] = null, - ["BLOB"] = null, - ["DECIMAL"] = "0.0", - ["STRING"] = string.Empty - }; - - /// - /// IoTDB 数据类型与.net类型映射 - /// - /// - /// - /// - private dynamic GetTSDataValue(TSDataType tSDataType, object value) => - tSDataType switch - { - TSDataType.BOOLEAN => Convert.ToBoolean(value), - TSDataType.INT32 => Convert.ToInt32(value), - TSDataType.INT64 => Convert.ToInt64(value), - TSDataType.FLOAT => Convert.ToSingle(value), - TSDataType.DOUBLE => Convert.ToDouble(value), - TSDataType.TEXT => Convert.ToString(value), - TSDataType.NONE => null, - TSDataType.TIMESTAMP => Convert.ToInt64(value), - TSDataType.DATE => Convert.ToDateTime(value), - TSDataType.BLOB => Convert.ToByte(value), - TSDataType.STRING => Convert.ToString(value), - _ => Convert.ToString(value) - }; - - /// - /// 缓存实体属性信息 - /// - /// - /// - /// - private Dictionary BuildMemberCache(ISourceEntityAccessor accessor) - { - var cache = new Dictionary(StringComparer.Ordinal); - foreach (var member in accessor.MemberList) - { - cache[member.NameOrPath] = member; - } - return cache; - } - - - - /// - /// 初始化表模型 - /// - /// - public async Task InitTableSessionModelAsync() - { - //获取JiShe.CollectBus.IoTDB程序集和JiShe.CollectBus.Domain程序集中的所有 [SourceAnalyzers(EntityTypeEnum.TableModel)] 的实体 - var assemblyNames = new[] { "JiShe.CollectBus.IoTDB", "JiShe.CollectBus.Domain" }; - var assemblies = CommonHelper.LoadAssemblies(assemblyNames); - - var targetTypes = CollectTargetTypes(assemblies); - if (targetTypes == null || targetTypes.Count <= 0) - { - _logger.LogError($"{nameof(InitTableSessionModelAsync)} 初始化表模型时没有找到对应的实体类信息。"); - return; - } - -// @"CREATE TABLE table1( -// time TIMESTAMP TIME, -// region STRING TAG, -// plant_id STRING TAG, -// device_id STRING TAG, -// model_id STRING ATTRIBUTE, -// maintenance STRING ATTRIBUTE, -// temperature FLOAT FIELD, -// humidity FLOAT FIELD, -// status Boolean FIELD, -// arrival_time TIMESTAMP FIELD -//) COMMENT 'table1' WITH(TTL = 31536000000);"; - - - foreach (var item in targetTypes) - { - var accessor = SourceEntityAccessorFactory.GetAccessor(item); - //通过 dynamic 简化操作 - dynamic dynamicAccessor = accessor; - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.Append("CREATE TABLE IF NOT EXISTS "); - stringBuilder.Append(dynamicAccessor.EntityName); - stringBuilder.Append("( time TIMESTAMP TIME, "); - var columns = new List(); - foreach (var member in dynamicAccessor.MemberList) - { - // 过滤元组子项 - if (member.NameOrPath.Contains(".Item")) continue; - - // 特性查询优化 - var attributes = (IEnumerable)(member.CustomAttributes ?? Enumerable.Empty()); - var tagAttr = attributes.OfType().FirstOrDefault(); - var attrColumn = attributes.OfType().FirstOrDefault(); - var fieldColumn = attributes.OfType().FirstOrDefault(); - - // 构建ColumnInfo - ColumnInfo? column = null; - if (tagAttr != null) - { - column = new ColumnInfo(member.NameOrPath, ColumnCategory.TAG, GetDataTypeFromTypeName(member.DeclaredTypeName), false, member.DeclaredTypeName); - } - else if (attrColumn != null) - { - column = new ColumnInfo(member.NameOrPath, ColumnCategory.ATTRIBUTE, GetDataTypeFromTypeName(member.DeclaredTypeName), false, member.DeclaredTypeName); - } - else if (fieldColumn != null) - { - column = new ColumnInfo(member.NameOrPath, ColumnCategory.FIELD, GetDataTypeFromTypeName(member.DeclaredTypeName), false, member.DeclaredTypeName); - } - - if (!column.HasValue) - { - _logger.LogError($"{nameof(InitTableSessionModelAsync)} 初始化表模型时实体类{dynamicAccessor.EntityName}的{member.NameOrPath}列的ColumnInfo构建失败。"); - continue; - } - columns.Add(column.Value); - } - - //按业务逻辑顺序处理(TAG -> ATTRIBUTE -> FIELD) - var groupedColumns = columns - .GroupBy(c => c.Category) - .ToDictionary(g => g.Key, g => g.ToList()); - List tempColumInfos = new List(); - - tempColumInfos.Add( ProcessCategory(groupedColumns, ColumnCategory.TAG)); - tempColumInfos.Add(ProcessCategory(groupedColumns, ColumnCategory.ATTRIBUTE)); - tempColumInfos.Add(ProcessCategory(groupedColumns, ColumnCategory.FIELD)); - stringBuilder.Append(string.Join(",", tempColumInfos.Where(d => !string.IsNullOrWhiteSpace(d)))); - stringBuilder.Append($" ) COMMENT '{item.Name}' "); - - _logger.LogWarning($"{dynamicAccessor.EntityName} 初始化语句:{stringBuilder.ToString()}"); - - await CurrentSession.ExecuteNonQueryStatementAsync($"{stringBuilder.ToString()}"); - } - - } - - - /// - /// 获取程序集中的所有 [SourceAnalyzers(EntityTypeEnum.TableModel)] 的实体 - /// - /// - /// - private List CollectTargetTypes(List assemblies) - { - var targetTypes = new List(); - foreach (var assembly in assemblies) - { - try - { - foreach (var type in assembly.GetExportedTypes()) - { - //获取表模型特性的类 - var sourceAnalyzersAttribute = type.GetCustomAttribute(); - - //需要忽略表模型初始化,有此特性无需初始化 - var ignoreInitTableAttribute = type.GetCustomAttribute(); - - if (sourceAnalyzersAttribute?.EntityType == EntityTypeEnum.TableModel && ignoreInitTableAttribute == null) - { - if (type.GetConstructor(Type.EmptyTypes) != null) - targetTypes.Add(type); - } - } - } - catch (ReflectionTypeLoadException ex) - { - _logger.LogError($"加载 {assembly} 失败: {string.Join(", ", ex.LoaderExceptions.Select(e => e.Message))}"); - } - } - return targetTypes; - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Provider/IoTDBSessionFactory.cs b/modules/JiShe.CollectBus.IoTDB/Provider/IoTDBSessionFactory.cs deleted file mode 100644 index 27b9200..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Provider/IoTDBSessionFactory.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Concurrent; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IoTDB.Options; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; - -namespace JiShe.CollectBus.IoTDB.Provider -{ - - /// - /// 实现带缓存的Session工厂 - /// - public class IoTDbSessionFactory : IIoTDbSessionFactory, ISingletonDependency - { - private readonly IoTDbOptions _options; - private readonly ConcurrentDictionary _pools = new(); - private bool _disposed; - - /// - /// IoTDbSessionFactory - /// - /// - public IoTDbSessionFactory(IOptions options) - { - _options = options.Value; - } - - public IIoTDbSessionPool GetSessionPool(bool useTableSession) - { - if (_disposed) throw new ObjectDisposedException(nameof(IoTDbSessionFactory)); - - return _pools.GetOrAdd(useTableSession, key => - { - var pool = key - ? (IIoTDbSessionPool)new TableSessionPoolAdapter(_options) - : new SessionPoolAdapter(_options); - - pool.OpenAsync().ConfigureAwait(false).GetAwaiter().GetResult(); ; - return pool; - }); - } - - public void Dispose() - { - foreach (var pool in _pools.Values) - { - pool.Dispose(); - } - _pools.Clear(); - _disposed = true; - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Provider/SessionPoolAdapter.cs b/modules/JiShe.CollectBus.IoTDB/Provider/SessionPoolAdapter.cs deleted file mode 100644 index a6d2cf5..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Provider/SessionPoolAdapter.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Apache.IoTDB; -using Apache.IoTDB.DataStructure; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IoTDB.Options; -using Microsoft.Extensions.Logging; - -namespace JiShe.CollectBus.IoTDB.Provider -{ - /// - /// 树模型连接池 - /// - public class SessionPoolAdapter : IIoTDbSessionPool - { - private readonly SessionPool _sessionPool; - private readonly IoTDbOptions _options; - - /// - /// SessionPoolAdapter - /// - /// - public SessionPoolAdapter(IoTDbOptions options) - { - _options = options; - _sessionPool = new SessionPool.Builder() - .SetNodeUrl(options.ClusterList) - .SetUsername(options.UserName) - .SetPassword(options.Password) - .SetZoneId(options.ZoneId) - .SetFetchSize(options.FetchSize) - .SetPoolSize(options.PoolSize) - .Build(); - } - - /// - /// 打开连接池 - /// - /// - public async Task OpenAsync() - { - await _sessionPool.Open(false); - if (_options.OpenDebugMode) - { - _sessionPool.OpenDebugMode(builder => - { - builder.AddConsole(); - }); - } - } - - /// - /// 关闭连接池 - /// - /// - public async Task CloseAsync() - { - if (_sessionPool == null) - { - return; - } - await _sessionPool.Close(); - } - - /// - /// 批量插入对齐时间序列数据 - /// - /// - /// - public async Task InsertAsync(Tablet tablet) - { - var result = await _sessionPool.InsertAlignedTabletAsync(tablet); - if (result != 0) - { - throw new Exception($"{nameof(SessionPoolAdapter)} Tree模型数据入库没有成功,返回结果为:{result},请检查IoTEntity继承子类属性索引是否有变动。"); - } - return result; - } - - /// - /// 查询数据 - /// - /// - /// - public async Task ExecuteQueryStatementAsync(string sql) - { - var result = await _sessionPool.ExecuteQueryStatementAsync(sql, _options.Timeout); - return result; - } - - /// - /// 执行无返回结果SQL - /// - /// - /// - public async Task ExecuteNonQueryStatementAsync(string sql) - { - var result = await _sessionPool.ExecuteNonQueryStatementAsync(sql); - return result; - } - - public void Dispose() - { - _sessionPool?.Close().ConfigureAwait(false).GetAwaiter().GetResult(); - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/Provider/TableSessionPoolAdapter.cs b/modules/JiShe.CollectBus.IoTDB/Provider/TableSessionPoolAdapter.cs deleted file mode 100644 index 5708de6..0000000 --- a/modules/JiShe.CollectBus.IoTDB/Provider/TableSessionPoolAdapter.cs +++ /dev/null @@ -1,104 +0,0 @@ -using Apache.IoTDB; -using Apache.IoTDB.DataStructure; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IoTDB.Options; -using Microsoft.Extensions.Logging; - -namespace JiShe.CollectBus.IoTDB.Provider -{ - /// - /// 表模型Session连接池 - /// - public class TableSessionPoolAdapter : IIoTDbSessionPool - { - private readonly TableSessionPool _sessionPool; - private readonly IoTDbOptions _options; - - /// - /// TableSessionPoolAdapter - /// - /// - public TableSessionPoolAdapter(IoTDbOptions options) - { - _options = options; - _sessionPool = new TableSessionPool.Builder() - .SetNodeUrls(options.ClusterList) - .SetUsername(options.UserName) - .SetPassword(options.Password) - .SetZoneId(options.ZoneId) - .SetFetchSize(options.FetchSize) - .SetPoolSize(options.PoolSize) - .SetDatabase(options.DataBaseName) - .Build(); - } - - /// - /// 打开连接池 - /// - /// - public async Task OpenAsync() - { - await _sessionPool.Open(false); - if (_options.OpenDebugMode) - { - _sessionPool.OpenDebugMode(builder => builder.AddConsole()); - } - } - - /// - /// 关闭连接池 - /// - /// - public async Task CloseAsync() - { - if (_sessionPool == null) - { - return; - } - await _sessionPool.Close(); - } - - /// - /// 批量插入 - /// - /// - /// - public async Task InsertAsync(Tablet tablet) - { - var result = await _sessionPool.InsertAsync(tablet); - if (result != 0) - { - throw new Exception($"{nameof(TableSessionPoolAdapter)} table模型数据入库没有成功,返回结果为:{result},请检查IoTEntity继承子类属性索引是否有变动。"); - } - - return result; - } - - /// - /// 查询数据 - /// - /// - /// - public async Task ExecuteQueryStatementAsync(string sql) - { - var result = await _sessionPool.ExecuteQueryStatementAsync(sql,_options.Timeout); - return result; - } - - /// - /// 执行无返回结果SQL - /// - /// - /// - public async Task ExecuteNonQueryStatementAsync(string sql) - { - var result = await _sessionPool.ExecuteNonQueryStatementAsync(sql); - return result; - } - - public void Dispose() - { - _sessionPool?.Close().ConfigureAwait(false).GetAwaiter().GetResult(); - } - } -} diff --git a/modules/JiShe.CollectBus.IoTDB/SourceEntityAccessorFactory3.cs b/modules/JiShe.CollectBus.IoTDB/SourceEntityAccessorFactory3.cs deleted file mode 100644 index 4712b67..0000000 --- a/modules/JiShe.CollectBus.IoTDB/SourceEntityAccessorFactory3.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Reflection; - -namespace JiShe.CollectBus.Analyzers.Shared; - -public static class SourceEntityAccessorFactory3 -{ - private static readonly ConcurrentDictionary _accessors = new(); - - public static ISourceEntityAccessor GetAccessor() - { - return (ISourceEntityAccessor)_accessors.GetOrAdd(typeof(T), t => - { - // 获取泛型类型定义信息(如果是泛型类型) - var isGeneric = t.IsGenericType; - var genericTypeDef = isGeneric ? t.GetGenericTypeDefinition() : null; - var arity = isGeneric ? genericTypeDef!.GetGenericArguments().Length : 0; - - // 构建访问器类名 - var typeName = isGeneric - ? $"{t.Namespace}.{genericTypeDef!.Name.Split('`')[0]}Accessor`{arity}" - : $"{t.Namespace}.{t.Name}Accessor"; - - // 尝试从当前程序集加载 - var accessorType = Assembly.GetAssembly(t)!.GetType(typeName) - ?? throw new InvalidOperationException($"Accessor type {typeName} not found"); - - // 处理泛型参数 - if (isGeneric && accessorType.IsGenericTypeDefinition) - { - accessorType = accessorType.MakeGenericType(t.GetGenericArguments()); - } - - return Activator.CreateInstance(accessorType)!; - }); - } - - public static object GetAccessor(Type type) - { - MethodInfo getAccessorMethod = typeof(SourceEntityAccessorFactory) - .GetMethod( - name: nameof(GetAccessor), - bindingAttr: BindingFlags.Public | BindingFlags.Static, - - types: Type.EmptyTypes - ); - - MethodInfo genericMethod = getAccessorMethod.MakeGenericMethod(type); - return genericMethod.Invoke(null, null); - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka.Test/ConsoleApplicationBuilder.cs b/modules/JiShe.CollectBus.Kafka.Test/ConsoleApplicationBuilder.cs deleted file mode 100644 index f6b0891..0000000 --- a/modules/JiShe.CollectBus.Kafka.Test/ConsoleApplicationBuilder.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Kafka.Test -{ - public class ConsoleApplicationBuilder: IApplicationBuilder - { - public IServiceProvider ApplicationServices { get; set; } - public IDictionary Properties { get; set; } = new Dictionary(); - - public IFeatureCollection ServerFeatures => throw new NotImplementedException(); - - private readonly List> _middlewares = new(); - - public IApplicationBuilder Use(Func middleware) - { - _middlewares.Add(middleware); - return this; - } - - public RequestDelegate Build() - { - RequestDelegate app = context => Task.CompletedTask; - foreach (var middleware in _middlewares) - { - app = middleware(app); - } - return app; - } - - public IApplicationBuilder New() - { - return new ConsoleApplicationBuilder - { - ApplicationServices = this.ApplicationServices, - Properties = new Dictionary(this.Properties) - }; - } - } - - - public static class HostBuilderExtensions - { - public static IHostBuilder ConfigureConsoleAppBuilder( - this IHostBuilder hostBuilder, - Action configure) - { - hostBuilder.ConfigureServices((context, services) => - { - // 注册 ConsoleApplicationBuilder 到 DI 容器 - services.AddSingleton(provider => - { - var appBuilder = new ConsoleApplicationBuilder - { - ApplicationServices = provider // 注入服务提供者 - }; - configure(appBuilder); // 执行配置委托 - return appBuilder; - }); - }); - return hostBuilder; - } - } -} diff --git a/modules/JiShe.CollectBus.Kafka.Test/JiShe.CollectBus.Kafka.Test.csproj b/modules/JiShe.CollectBus.Kafka.Test/JiShe.CollectBus.Kafka.Test.csproj deleted file mode 100644 index c312740..0000000 --- a/modules/JiShe.CollectBus.Kafka.Test/JiShe.CollectBus.Kafka.Test.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - - - Always - true - PreserveNewest - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/JiShe.CollectBus.Kafka.Test/KafkaProduceBenchmark.cs b/modules/JiShe.CollectBus.Kafka.Test/KafkaProduceBenchmark.cs deleted file mode 100644 index 4152a33..0000000 --- a/modules/JiShe.CollectBus.Kafka.Test/KafkaProduceBenchmark.cs +++ /dev/null @@ -1,121 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Confluent.Kafka; -using JiShe.CollectBus.Common; -using JiShe.CollectBus.Kafka.AdminClient; -using JiShe.CollectBus.Kafka.Consumer; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Kafka.Producer; - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Serilog; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Kafka.Test -{ - [SimpleJob(RuntimeMoniker.Net80)] - //[SimpleJob(RuntimeMoniker.NativeAot80)] - [RPlotExporter] - public class KafkaProduceBenchmark - { - - // 每批消息数量 - [Params(1000, 10000, 100000, 1000000)] - public int N; - public ServiceProvider _serviceProvider; - public IConsumerService _consumerService; - public IProducerService _producerService; - public string topic = "test-topic1"; - - [GlobalSetup] - public void Setup() - { - // 构建配置 - var config = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json") - .Build(); - // 直接读取配置项 - var greeting = config["Kafka:ServerTagName"]; - Console.WriteLine(greeting); // 输出: Hello, World! - // 创建服务容器 - var services = new ServiceCollection(); - // 注册 IConfiguration 实例 - services.AddSingleton(config); - - services.Configure(options => - { - config.GetSection("Kafka").Bind(options); - }); - services.Configure(options => - { - config.GetSection(nameof(ServerApplicationOptions)).Bind(options); - }); - - // 初始化日志 - Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(config) // 从 appsettings.json 读取配置 - .CreateLogger(); - - // 配置日志系统 - services.AddLogging(logging => - { - logging.ClearProviders(); - logging.AddSerilog(); - }); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddTransient(); - - // 构建ServiceProvider - _serviceProvider = services.BuildServiceProvider(); - - // 获取日志记录器工厂 - var loggerFactory = _serviceProvider.GetRequiredService(); - var logger = loggerFactory.CreateLogger(); - logger.LogInformation("程序启动"); - - var adminClientService = _serviceProvider.GetRequiredService(); - - - //await adminClientService.DeleteTopicAsync(topic); - // 创建 topic - //adminClientService.CreateTopicAsync(topic, 3, 3).ConfigureAwait(false).GetAwaiter(); - - _consumerService = _serviceProvider.GetRequiredService(); - - _producerService = _serviceProvider.GetRequiredService(); - } - - [Benchmark] - public async Task UseAsync() - { - List tasks = new(); - for (int i = 0; i < N; ++i) - { - var task = _producerService.ProduceAsync(topic, i.ToString()); - tasks.Add(task); - } - await Task.WhenAll(tasks); - } - - [Benchmark] - public async Task UseLibrd() - { - List tasks = new(); - for (int i = 0; i < N; ++i) - { - var task = _producerService.ProduceAsync(topic, i.ToString(), null); - } - await Task.WhenAll(tasks); - } - } -} diff --git a/modules/JiShe.CollectBus.Kafka.Test/KafkaSubscribeTest.cs b/modules/JiShe.CollectBus.Kafka.Test/KafkaSubscribeTest.cs deleted file mode 100644 index c7ad309..0000000 --- a/modules/JiShe.CollectBus.Kafka.Test/KafkaSubscribeTest.cs +++ /dev/null @@ -1,70 +0,0 @@ -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Models; -using JiShe.CollectBus.IotSystems.MessageReceiveds; -using JiShe.CollectBus.Kafka.Attributes; -using JiShe.CollectBus.Kafka.Internal; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using Volo.Abp.Timing; - -namespace JiShe.CollectBus.Kafka.Test -{ - public class KafkaSubscribeTest: IKafkaSubscribe - { - [KafkaSubscribe(ProtocolConst.TESTTOPIC, EnableBatch = false, BatchSize = 10)] - - public async Task KafkaSubscribeAsync(TestTopic obj) - //public async Task KafkaSubscribeAsync(IEnumerable obj) - { - Console.WriteLine($"收到订阅消息: {JsonSerializer.Serialize(obj)}"); - return SubscribeAck.Success(); - } - - - //[KafkaSubscribe(ProtocolConst.SubscriberLoginIssuedEventName)] - ////[CapSubscribe(ProtocolConst.SubscriberLoginIssuedEventName)] - //public async Task LoginIssuedEvent(IssuedEventMessage issuedEventMessage) - //{ - // Console.WriteLine($"收到订阅消息: {JsonSerializer.Serialize(issuedEventMessage)}"); - // return SubscribeAck.Success(); - //} - - //[KafkaSubscribe(ProtocolConst.SubscriberHeartbeatIssuedEventName)] - ////[CapSubscribe(ProtocolConst.SubscriberHeartbeatIssuedEventName)] - //public async Task HeartbeatIssuedEvent(IssuedEventMessage issuedEventMessage) - //{ - // Console.WriteLine($"收到订阅消息: {JsonSerializer.Serialize(issuedEventMessage)}"); - // return SubscribeAck.Success(); - //} - - //[KafkaSubscribe(ProtocolConst.SubscriberReceivedEventName)] - ////[CapSubscribe(ProtocolConst.SubscriberReceivedEventName)] - //public async Task ReceivedEvent(MessageReceived receivedMessage) - //{ - // Console.WriteLine($"收到订阅消息: {JsonSerializer.Serialize(receivedMessage)}"); - // return SubscribeAck.Success(); - //} - - //[KafkaSubscribe(ProtocolConst.SubscriberHeartbeatReceivedEventName)] - ////[CapSubscribe(ProtocolConst.SubscriberHeartbeatReceivedEventName)] - //public async Task ReceivedHeartbeatEvent(MessageReceivedHeartbeat receivedHeartbeatMessage) - //{ - // Console.WriteLine($"收到订阅消息: {JsonSerializer.Serialize(receivedHeartbeatMessage)}"); - // return SubscribeAck.Success(); - //} - - //[KafkaSubscribe(ProtocolConst.SubscriberLoginReceivedEventName)] - ////[CapSubscribe(ProtocolConst.SubscriberLoginReceivedEventName)] - //public async Task ReceivedLoginEvent(MessageReceivedLogin receivedLoginMessage) - //{ - // Console.WriteLine($"收到订阅消息: {JsonSerializer.Serialize(receivedLoginMessage)}"); - // return SubscribeAck.Success(); - //} - } -} diff --git a/modules/JiShe.CollectBus.Kafka.Test/Lib/JiShe.CollectBus.Common.dll b/modules/JiShe.CollectBus.Kafka.Test/Lib/JiShe.CollectBus.Common.dll deleted file mode 100644 index 5d9bd8d55de07a0ca6198b496f63f3c3194da2d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171008 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

IdoP*dxLH(c)gV<6*!B8)O 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� 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 diff --git a/modules/JiShe.CollectBus.Kafka.Test/Program.cs b/modules/JiShe.CollectBus.Kafka.Test/Program.cs deleted file mode 100644 index d932806..0000000 --- a/modules/JiShe.CollectBus.Kafka.Test/Program.cs +++ /dev/null @@ -1,197 +0,0 @@ -// See https://aka.ms/new-console-template for more information -using JiShe.CollectBus.Common; -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Kafka; -using JiShe.CollectBus.Kafka.AdminClient; -using JiShe.CollectBus.Kafka.Consumer; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Kafka.Producer; -using JiShe.CollectBus.Kafka.Test; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Serilog; - -#region 基准测试 -//var summary = BenchmarkRunner.Run(); -//Console.WriteLine("压测完成"); -//return; -#endregion 基准测试 - - -var host = Host.CreateDefaultBuilder(args) - .ConfigureServices(services => - { - // 构建配置 - var config = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json") - .Build(); - // 直接读取配置项 - var greeting = config["ServerApplicationOptions:ServerTagName"]; - Console.WriteLine(greeting); // 输出: Hello, World! - - - // 创建服务容器 - //var services = new ServiceCollection(); - // 注册 IConfiguration 实例 - services.AddSingleton(config); - - // 初始化日志 - Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(config) // 从 appsettings.json 读取配置 - .CreateLogger(); - - // 配置日志系统 - services.AddLogging(logging => - { - logging.ClearProviders(); - logging.AddSerilog(); - }); - //services.Configure(config.GetSection("Kafka")); - //services.Configure(config.GetSection("ServerApplicationOptions")); - var dss = config.GetSection("Kafka"); - - services.Configure(options => - { - config.GetSection("Kafka").Bind(options); - }); - services.Configure(options => - { - config.GetSection(nameof(ServerApplicationOptions)).Bind(options); - }); - - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddTransient(); - - }) - .ConfigureConsoleAppBuilder(appBuilder => - { - - }) - .Build(); - - - await host.StartAsync(); - var appBuilder = host.Services.GetRequiredService(); - appBuilder.ApplicationServices.UseKafkaSubscribe(); - - -// 构建ServiceProvider -//var serviceProvider = services.BuildServiceProvider(); - -// 获取日志记录器工厂 -var loggerFactory = host.Services.GetRequiredService(); -var logger = loggerFactory.CreateLogger(); -logger.LogInformation("程序启动"); - - -var _kafkaPollyPipeline = host.Services.GetRequiredService(); -if (_kafkaPollyPipeline == null) -{ - logger.LogInformation("KafkaPollyPipeline未注册!"); -} - - -var adminClientService = host.Services.GetRequiredService(); -var configuration = host.Services.GetRequiredService(); - -var kafkaOptionConfig=host.Services.GetRequiredService>(); - -string topic = ProtocolConst.TESTTOPIC; -//await adminClientService.DeleteTopicAsync(topic); -// 创建 topic -//await adminClientService.CreateTopicAsync(topic, configuration.GetValue(CommonConst.NumPartitions), 3); - -var consumerService = host.Services.GetRequiredService(); -var producerService = host.Services.GetRequiredService(); -//var kafkaOptions = host.Services.GetRequiredService>(); -//await consumerService.SubscribeAsync(topic, (message) => -//{ -// try -// { -// logger.LogInformation($"消费消息:{message}"); -// return Task.FromResult(true); - -// } -// catch (ConsumeException ex) -// { -// // 处理消费错误 -// logger.LogError($"kafka消费异常:{ex.Message}"); -// } -// return Task.FromResult(false); -//}, "default"); - -//Stopwatch stopwatch = Stopwatch.StartNew(); - -//for (int i = 0; i < 3; i++) -//{ -//await consumerService.SubscribeBatchAsync(topic, (message) => -//{ -// try -// { -// int index = 0; -// logger.LogInformation($"消费消息_{index}消费总数:{message.Count()}:{JsonSerializer.Serialize(message)}"); -// return Task.FromResult(true); - -// } -// catch (ConsumeException ex) -// { -// // 处理消费错误 -// logger.LogError($"kafka消费异常:{ex.Message}"); -// } -// return Task.FromResult(false); -//}); -//} -//stopwatch.Stop(); -//Console.WriteLine($"耗时: {stopwatch.ElapsedMilliseconds} 毫秒,{stopwatch.ElapsedMilliseconds/1000} 秒"); - - -int num = 1; -while (num <= 6) -{ - await producerService.ProduceAsync(topic, new TestTopic { Topic = topic, Val = num }); - num++; -} - -//int num = 2; -//while (num <= 4) -//{ -// await producerService.ProduceAsync(topic, num.ToString()); -// num++; -//} -//await Task.Factory.StartNew(async() => { -// int num = 0; -// while (true) -// { -// //await producerService.ProduceAsync(topic, new TestTopic { Topic = topic, Val = i }); -// await producerService.ProduceAsync(topic, num.ToString()); -// num++; -// } -//}); -Console.WriteLine("\n按Esc键退出"); -while (true) -{ - var key = Console.ReadKey(intercept: true); // intercept:true 隐藏按键显示 - - if (key.Key == ConsoleKey.Escape) - { - await host.StopAsync(); - Console.WriteLine("\n程序已退出"); - break; - } -} -(host.Services as IDisposable)?.Dispose(); - - -public class TestTopic -{ - public string Topic { get; set; } - public int Val { get; set; } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka.Test/Properties/launchSettings.json b/modules/JiShe.CollectBus.Kafka.Test/Properties/launchSettings.json deleted file mode 100644 index 33504c9..0000000 --- a/modules/JiShe.CollectBus.Kafka.Test/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "WSL": { - "commandName": "WSL2", - "distributionName": "" - } - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka.Test/appsettings.json b/modules/JiShe.CollectBus.Kafka.Test/appsettings.json deleted file mode 100644 index 0734e89..0000000 --- a/modules/JiShe.CollectBus.Kafka.Test/appsettings.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "Serilog": { - "Using": [ - "Serilog.Sinks.Console", - "Serilog.Sinks.File" - ], - "MinimumLevel": { - "Default": "Information", - "Override": { - "Microsoft": "Warning", - "Volo.Abp": "Warning", - "Hangfire": "Warning", - "DotNetCore.CAP": "Warning", - "Serilog.AspNetCore": "Information", - "Microsoft.EntityFrameworkCore": "Warning", - "Microsoft.AspNetCore": "Warning", - "Microsoft.AspNetCore.Diagnostics.HealthChecks": "Warning" - } - }, - "WriteTo": [ - { - "Name": "Console" - }, - { - "Name": "File", - "Args": { - "path": "logs/logs-.txt", - "rollingInterval": "Day" - } - } - ] - }, - "App": { - "SelfUrl": "http://localhost:44315", - "CorsOrigins": "http://localhost:4200,http://localhost:3100" - }, - "ConnectionStrings": { - "Default": "mongodb://mongo_PmEeF3:lixiao1980@192.168.1.9:27017/JiSheCollectBus?authSource=admin&maxPoolSize=400&minPoolSize=10&waitQueueTimeoutMS=5000", - "Kafka": "192.168.1.9:29092,192.168.1.9:39092,192.168.1.9:49092", - "PrepayDB": "server=118.190.144.92;database=jishe.sysdb;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False", - "EnergyDB": "server=118.190.144.92;database=db_energy;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False" - }, - "Redis": { - "Configuration": "192.168.1.9:6380,password=1q2w3e!@#,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", - "MaxPoolSize": "50", - "DefaultDB": "14", - "HangfireDB": "13" - }, - "Jwt": { - "Audience": "JiShe.CollectBus", - "SecurityKey": "dzehzRz9a8asdfasfdadfasdfasdfafsdadfasbasdf=", - "Issuer": "JiShe.CollectBus", - "ExpirationTime": 2 - }, - "HealthChecks": { - "IsEnable": true, - "HealthCheckDatabaseName": "HealthChecks", - "EvaluationTimeInSeconds": 10, - "MinimumSecondsBetweenFailureNotifications": 60 - }, - "SwaggerConfig": [ - { - "GroupName": "Basic", - "Title": "【后台管理】基础模块", - "Version": "V1" - }, - { - "GroupName": "Business", - "Title": "【后台管理】业务模块", - "Version": "V1" - } - ], - "Kafka": { - "BootstrapServers": "192.168.1.9:29092,192.168.1.9:39092,192.168.1.9:49092", - "EnableFilter": true, - "EnableAuthorization": false, - "SecurityProtocol": "SaslPlaintext", - "SaslMechanism": "Plain", - "SaslUserName": "lixiao", - "SaslPassword": "lixiao1980", - "KafkaReplicationFactor": 3, - "NumPartitions": 30, - "FirstCollectionTime": "2025-04-22 16:07:00" - }, - "IoTDBOptions": { - "UserName": "root", - "Password": "root", - "ClusterList": [ "192.168.1.9:6667" ], - "PoolSize": 32, - "DataBaseName": "energy", - "OpenDebugMode": true, - "UseTableSessionPoolByDefault": false - }, - "Cassandra": { - "ReplicationStrategy": { - "Class": "NetworkTopologyStrategy", //策略为NetworkTopologyStrategy时才会有多个数据中心,SimpleStrategy用在只有一个数据中心的情况下 - "DataCenters": [ - { - "Name": "dc1", - "ReplicationFactor": 3 - } - ] - }, - "Nodes": [ - { - "Host": "192.168.1.9", - "Port": 9042, - "DataCenter": "dc1", - "Rack": "RAC1" - }, - { - "Host": "192.168.1.9", - "Port": 9043, - "DataCenter": "dc1", - "Rack": "RAC2" - }, - { - "Host": "192.168.1.9", - "Port": 9044, - "DataCenter": "dc1", - "Rack": "RAC2" - } - ], - "Username": "admin", - "Password": "lixiao1980", - "Keyspace": "jishecollectbus", - "ConsistencyLevel": "Quorum", - "PoolingOptions": { - "CoreConnectionsPerHost": 4, - "MaxConnectionsPerHost": 8, - "MaxRequestsPerConnection": 2000 - }, - "SocketOptions": { - "ConnectTimeoutMillis": 10000, - "ReadTimeoutMillis": 20000 - }, - "QueryOptions": { - "ConsistencyLevel": "Quorum", - "SerialConsistencyLevel": "Serial", - "DefaultIdempotence": true - } - }, - "ServerApplicationOptions": { - "ServerTagName": "JiSheCollectBus4", - "SystemType": "Energy", - "FirstCollectionTime": "2025-04-28 15:07:00", - "AutomaticVerificationTime": "16:07:00", - "AutomaticTerminalVersionTime": "17:07:00", - "AutomaticTelematicsModuleTime": "17:30:00", - "AutomaticDayFreezeTime": "02:30:00", - "AutomaticMonthFreezeTime": "03:30:00", - "DefaultProtocolPlugin": "T37612012ProtocolPlugin" - }, - "PlugInFolder": "" -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/AdminClient/AdminClientService.cs b/modules/JiShe.CollectBus.Kafka/AdminClient/AdminClientService.cs deleted file mode 100644 index 3ec24ef..0000000 --- a/modules/JiShe.CollectBus.Kafka/AdminClient/AdminClientService.cs +++ /dev/null @@ -1,187 +0,0 @@ -using Confluent.Kafka; -using Confluent.Kafka.Admin; -using JiShe.CollectBus.Kafka.Internal; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; - -namespace JiShe.CollectBus.Kafka.AdminClient; - -public class AdminClientService : IAdminClientService, ISingletonDependency -{ - private readonly ILogger _logger; - private readonly KafkaOptionConfig _kafkaOptionConfig; - private readonly Lazy _lazyAdminClient; - /// - /// Initializes a new instance of the class. - /// - /// - /// - public AdminClientService(IConfiguration configuration, ILogger logger, IOptions kafkaOptionConfig) - { - _logger = logger; - _kafkaOptionConfig = kafkaOptionConfig.Value; - //Instance = GetInstance(); - _lazyAdminClient = new Lazy(() => GetInstance()); - } - - /// - /// Gets or sets the instance. - /// - /// - /// The instance. - /// - public IAdminClient Instance => _lazyAdminClient.Value; - - /// - /// 创建Kafka主题 - /// - /// - /// - /// - /// - public async Task CreateTopicAsync(string topic, int numPartitions, short replicationFactor) - { - try - { - if (await CheckTopicAsync(topic)) return; - - - await Instance.CreateTopicsAsync(new[] - { - new TopicSpecification - { - Name = topic, - NumPartitions = numPartitions, - ReplicationFactor = replicationFactor - } - }); - } - catch (CreateTopicsException e) - { - if (e.Results[0].Error.Code != ErrorCode.TopicAlreadyExists) throw; - } - } - - /// - /// 删除Kafka主题 - /// - /// - /// - public async Task DeleteTopicAsync(string topic) - { - await Instance.DeleteTopicsAsync(new[] { topic }); - } - - /// - /// 获取Kafka主题列表 - /// - /// - public async Task> ListTopicsAsync() - { - var metadata = Instance.GetMetadata(TimeSpan.FromSeconds(10)); - return await Task.FromResult(new List(metadata.Topics.Select(t => t.Topic))); - } - - /// - /// 判断Kafka主题是否存在 - /// - /// - /// - public async Task TopicExistsAsync(string topic) - { - var metadata = Instance.GetMetadata(TimeSpan.FromSeconds(10)); - return await Task.FromResult(metadata.Topics.Any(t => t.Topic == topic)); - } - - /// - /// 检测分区是否存在 - /// - /// - /// - /// - public Dictionary CheckPartitionsExists(string topic, int[] partitions) - { - var result = new Dictionary(); - var metadata = Instance.GetMetadata(topic, TimeSpan.FromSeconds(10)); - if (metadata.Topics.Count == 0) - return partitions.ToDictionary(p => p, p => false); - var existingPartitions = metadata.Topics[0].Partitions.Select(p => p.PartitionId).ToHashSet(); - return partitions.ToDictionary(p => p, p => existingPartitions.Contains(p)); - } - - /// - /// 检测分区是否存在 - /// - /// - /// - /// - public bool CheckPartitionsExist(string topic, int targetPartition) - { - var metadata = Instance.GetMetadata(topic, TimeSpan.FromSeconds(10)); - if (metadata.Topics.Count == 0) - return false; - var partitions = metadata.Topics[0].Partitions; - return partitions.Any(p => p.PartitionId == targetPartition); - } - - /// - /// 获取主题的分区数量 - /// - /// - /// - public int GetTopicPartitionsNum(string topic) - { - var metadata = Instance.GetMetadata(topic, TimeSpan.FromSeconds(10)); - if (metadata.Topics.Count == 0) - return 0; - return metadata.Topics[0].Partitions.Count; - } - - /// - /// Gets the instance. - /// - /// - public IAdminClient GetInstance() - { - var adminClientConfig = new AdminClientConfig - { - BootstrapServers = _kafkaOptionConfig.BootstrapServers - }; - if (_kafkaOptionConfig.EnableAuthorization) - { - adminClientConfig.SecurityProtocol = _kafkaOptionConfig.SecurityProtocol; - adminClientConfig.SaslMechanism = _kafkaOptionConfig.SaslMechanism; - adminClientConfig.SaslUsername = _kafkaOptionConfig.SaslUserName; - adminClientConfig.SaslPassword = _kafkaOptionConfig.SaslPassword; - } - return new AdminClientBuilder(adminClientConfig).Build(); - } - - /// - /// Checks the topic asynchronous. - /// - /// The topic. - /// - public async Task CheckTopicAsync(string topic) - { - var metadata = Instance.GetMetadata(TimeSpan.FromSeconds(5)); - return await Task.FromResult(metadata.Topics.Exists(a => a.Topic == topic)); - } - - /// - /// 判断Kafka主题是否存在 - /// - /// 主题名称 - /// 副本数量,不能高于Brokers数量 - /// - public async Task CheckTopicAsync(string topic, int numPartitions) - { - var metadata = Instance.GetMetadata(TimeSpan.FromSeconds(5)); - if (numPartitions > metadata.Brokers.Count) - throw new Exception($"{nameof(CheckTopicAsync)} 主题检查时,副本数量大于了节点数量。"); - - return await Task.FromResult(metadata.Topics.Exists(a => a.Topic == topic)); - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/AdminClient/IAdminClientService.cs b/modules/JiShe.CollectBus.Kafka/AdminClient/IAdminClientService.cs deleted file mode 100644 index 92121c5..0000000 --- a/modules/JiShe.CollectBus.Kafka/AdminClient/IAdminClientService.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Kafka.AdminClient -{ - public interface IAdminClientService - { - /// - /// 创建Kafka主题 - /// - /// 主题名称 - /// 主题分区数量 - /// 副本数量,不能高于Brokers数量 - /// - Task CreateTopicAsync(string topic, int numPartitions, short replicationFactor); - - /// - /// 删除Kafka主题 - /// - /// - /// - Task DeleteTopicAsync(string topic); - - /// - /// 获取Kafka主题列表 - /// - /// - Task> ListTopicsAsync(); - - /// - /// 判断Kafka主题是否存在 - /// - /// - /// - Task TopicExistsAsync(string topic); - - /// - /// 检测分区是否存在 - /// - /// - /// - /// - Dictionary CheckPartitionsExists(string topic, int[] partitions); - - /// - /// 检测分区是否存在 - /// - /// - /// - /// - bool CheckPartitionsExist(string topic, int targetPartition); - - /// - /// 获取主题的分区数量 - /// - /// - /// - int GetTopicPartitionsNum(string topic); - } -} diff --git a/modules/JiShe.CollectBus.Kafka/Attributes/KafkaSubscribeAttribute.cs b/modules/JiShe.CollectBus.Kafka/Attributes/KafkaSubscribeAttribute.cs deleted file mode 100644 index 3b2cfae..0000000 --- a/modules/JiShe.CollectBus.Kafka/Attributes/KafkaSubscribeAttribute.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace JiShe.CollectBus.Kafka.Attributes; - -[AttributeUsage(AttributeTargets.Method)] -public class KafkaSubscribeAttribute : Attribute -{ - /// - /// 订阅主题 - /// - /// - public KafkaSubscribeAttribute(string topic) - { - Topic = topic; - } - - /// - /// 订阅主题 - /// - public KafkaSubscribeAttribute(string topic, int partition) - { - Topic = topic; - Partition = partition; - } - - /// - /// 订阅的主题 - /// - public string Topic { get; set; } = null!; - - /// - /// 分区 - /// - public int Partition { get; set; } = -1; - - /// - /// 消费者组 - /// - public string? GroupId { get; set; } = null; //"default" - - /// - /// 任务数(默认是多少个分区多少个任务) - /// 如设置订阅指定Partition则任务数始终为1 - /// - public int TaskCount { get; set; } = -1; - - /// - /// 批量处理数量 - /// - public int BatchSize { get; set; } = 100; - - /// - /// 是否启用批量处理 - /// - public bool EnableBatch { get; set; } = false; - - /// - /// 批次超时时间 - /// 格式:("00:05:00") - /// - public TimeSpan? BatchTimeout { get; set; } = null; -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/Attributes/TopicAttribute.cs b/modules/JiShe.CollectBus.Kafka/Attributes/TopicAttribute.cs deleted file mode 100644 index a023edb..0000000 --- a/modules/JiShe.CollectBus.Kafka/Attributes/TopicAttribute.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace JiShe.CollectBus.Kafka.Attributes; - -[AttributeUsage(AttributeTargets.Class, Inherited = false)] -public class TopicAttribute : Attribute -{ - /// - /// Initializes a new instance of the class. - /// - /// The name. - public TopicAttribute(string name = "Default") - { - Name = name; - } - - /// - /// Gets or sets the name. - /// - /// - /// The name. - /// - public string Name { get; set; } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/CollectBusKafkaModule.cs b/modules/JiShe.CollectBus.Kafka/CollectBusKafkaModule.cs deleted file mode 100644 index d7190b2..0000000 --- a/modules/JiShe.CollectBus.Kafka/CollectBusKafkaModule.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Confluent.Kafka; -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Extensions; -using JiShe.CollectBus.Kafka.Consumer; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Kafka.Producer; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using System.Reflection; -using Volo.Abp; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Modularity; -using static Confluent.Kafka.ConfigPropertyNames; - -namespace JiShe.CollectBus.Kafka -{ - public class CollectBusKafkaModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - var configuration = context.Services.GetConfiguration(); - //var kafkaSection = configuration.GetSection(CommonConst.Kafka); - //KafkaOptionConfig kafkaOptionConfig = new KafkaOptionConfig(); - //kafkaSection.Bind(kafkaOptionConfig); - //Configure(kafkaSection); - Configure(options => - { - configuration.GetSection(CommonConst.Kafka).Bind(options); - }); - - // 注册Producer - context.Services.AddSingleton(); - // 注册Consumer - context.Services.AddSingleton(); - - // 注册Polly - context.Services.AddSingleton(); - - var topics = ProtocolConstExtensions.GetAllTopicNamesByIssued(); - topics.AddRange(ProtocolConstExtensions.GetAllTopicNamesByReceived()); - - // 注册任务调度 - context.Services.AddSingleton(); - - //context.Services.AddHostedService(); - } - - /// - /// 在初始化之前,初始化Kafka Topic - /// - /// - public override void OnPreApplicationInitialization(ApplicationInitializationContext context) - { - var app = context.GetApplicationBuilder(); - app.ApplicationServices.UseInitKafkaTopic(); - } - - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - var app = context.GetApplicationBuilder(); - // 注册Subscriber - app.ApplicationServices.UseKafkaSubscribe(); - - // 获取程序集 - //app.UseKafkaSubscribers(Assembly.Load("JiShe.CollectBus.Application")); - } - } -} diff --git a/modules/JiShe.CollectBus.Kafka/Consumer/ConsumerService.cs b/modules/JiShe.CollectBus.Kafka/Consumer/ConsumerService.cs deleted file mode 100644 index 685915d..0000000 --- a/modules/JiShe.CollectBus.Kafka/Consumer/ConsumerService.cs +++ /dev/null @@ -1,718 +0,0 @@ -using Confluent.Kafka; -using JiShe.CollectBus.Common; -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Kafka.Serialization; -using Microsoft.AspNetCore.DataProtection.KeyManagement; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System.Collections.Concurrent; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using YamlDotNet.Core.Tokens; - -namespace JiShe.CollectBus.Kafka.Consumer -{ - public class ConsumerService : IConsumerService, IDisposable - { - private readonly ILogger _logger; - /// - /// 消费者存储 - /// Key 格式:{groupId}_{topic}_{TKey}_{TValue} - /// - private readonly ConcurrentDictionary - _consumerStore = new(); - - /// - /// 消费完或者无数据时的延迟时间 - /// - private static TimeSpan DelayTime => TimeSpan.FromMilliseconds(100); - - private readonly KafkaOptionConfig _kafkaOptionConfig; - - private readonly ServerApplicationOptions _applicationOptions; - - private readonly KafkaPollyPipeline _kafkaPollyPipeline; - - - private readonly KafkaTaskScheduler _kafkaTaskScheduler; - - /// - /// ConsumerService - /// - /// - /// - public ConsumerService(ILogger logger, IOptions kafkaOptionConfig, KafkaPollyPipeline kafkaPollyPipeline, IOptions applicationOptions, KafkaTaskScheduler kafkaTaskScheduler) - { - _logger = logger; - _kafkaOptionConfig = kafkaOptionConfig.Value; - _applicationOptions = applicationOptions.Value; - _kafkaPollyPipeline = kafkaPollyPipeline; - _kafkaTaskScheduler = kafkaTaskScheduler; - } - - #region private 私有方法 - - /// - /// 创建消费者 - /// - /// - /// - /// - private IConsumer CreateConsumer(string? groupId = null) where TKey : notnull where TValue : class - { - var config = BuildConsumerConfig(groupId); - return new ConsumerBuilder(config) - .SetValueDeserializer(new JsonSerializer()) - .SetLogHandler((_, log) => _logger.LogInformation($"消费者Log: {log.Message}")) - .SetErrorHandler((_, e) => _logger.LogError($"消费者错误: {e.Reason}")) - .Build(); - } - - private ConsumerConfig BuildConsumerConfig(string? groupId = null) - { - var config = new ConsumerConfig - { - BootstrapServers = _kafkaOptionConfig.BootstrapServers, - GroupId = groupId ?? _applicationOptions.ServerTagName, - AutoOffsetReset = AutoOffsetReset.Earliest, - EnableAutoCommit = false, // 禁止AutoCommit - EnablePartitionEof = true, // 启用分区末尾标记 - //AllowAutoCreateTopics = true, // 启用自动创建 - FetchMaxBytes = 1024 * 1024 * 50 // 增加拉取大小(50MB) - }; - - if (_kafkaOptionConfig.EnableAuthorization) - { - config.SecurityProtocol = _kafkaOptionConfig.SecurityProtocol; - config.SaslMechanism = _kafkaOptionConfig.SaslMechanism; - config.SaslUsername = _kafkaOptionConfig.SaslUserName; - config.SaslPassword = _kafkaOptionConfig.SaslPassword; - } - - return config; - } - #endregion - - /// - /// 订阅消息 - /// - /// - /// - /// - /// - /// - public async Task SubscribeAsync(string topic, Func> messageHandler, string? groupId = null) where TKey : notnull where TValue : class - { - await SubscribeAsync(new[] { topic }, messageHandler, groupId); - } - - - /// - /// 订阅消息 - /// - /// - /// - /// - /// - public async Task SubscribeAsync(string topic, Func> messageHandler, string? groupId = null) where TValue : class - { - await SubscribeAsync(new[] { topic }, messageHandler, groupId); - } - - /// - /// 订阅消息 - /// - /// - /// - /// - /// - /// - public async Task SubscribeAsync(string[] topics, Func> messageHandler, string? groupId = null) where TKey : notnull where TValue : class - { - try - { - // 扩展独立线程,避免阻塞 - _kafkaTaskScheduler.WorkerThreadExpansion(); - await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token => - { - var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(TKey).Name}_{typeof(TValue).Name}"; - var consumerStore = _consumerStore.GetOrAdd(consumerKey, _ => - ( - CreateConsumer(groupId), - new CancellationTokenSource() - )); - if (consumerStore.Consumer == null) - { - _logger.LogWarning($"{string.Join("、", topics)}创建消息消费失败或消费组已被释放"); - return; - } - var consumer = consumerStore.Consumer as IConsumer; - var cts = consumerStore.CTS; - - consumer!.Subscribe(topics); - _= Task.Factory.StartNew(async () => - { - while (!cts.IsCancellationRequested) - { - try - { - //_logger.LogInformation($"Kafka消费: {string.Join("", topics)} 开始拉取消息...."); - - var result = consumer.Consume(cts.Token); - if (result == null || result.Message == null || result.Message.Value == null) - { - await Task.Delay(DelayTime, cts.Token); - continue; - } - if (result.IsPartitionEOF) - { -#if DEBUG - _logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition); -#endif - await Task.Delay(DelayTime, cts.Token); - continue; - } - if (_kafkaOptionConfig.EnableFilter) - { - var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } }; - // 检查 Header 是否符合条件 - if (!headersFilter.Match(result.Message.Headers)) - { - consumer.Commit(result); // 提交偏移量 - // 跳过消息 - continue; - } - } - bool sucess = await messageHandler(result.Message.Key, result.Message.Value); - if (sucess) - consumer.Commit(result); // 手动提交 - } - catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex)) - { - _logger.LogError(ex, $"{string.Join("、", topics)}消息消费失败: {ex.Error.Reason}"); - throw; // 抛出异常,以便重试 - } - catch (KafkaException ex) when (KafkaPollyPipeline.IsRecoverableError(ex)) - { - _logger.LogError(ex, $"{string.Join("、", topics)} 消息消费失败: {ex.Error.Reason}"); - throw; // 抛出异常,以便重试 - } - catch (OperationCanceledException) - { - //ignore - } - catch (ObjectDisposedException) - { - _logger.LogError($"{string.Join("、", topics)}消费者被释放"); - break; - } - catch (Exception ex) - { - _logger.LogError(ex, "处理消息时发生未知错误"); - } - } - }, cts.Token, TaskCreationOptions.None, _kafkaTaskScheduler); - await Task.CompletedTask; - }); - - } - catch (Exception ex) - { - - throw; - } - } - - - - /// - /// 订阅消息 - /// - /// - /// - /// - /// - /// - public async Task SubscribeAsync(string[] topics, Func> messageHandler, string? groupId) where TValue : class - { - try - { - // 扩展独立线程,避免阻塞 - _kafkaTaskScheduler.WorkerThreadExpansion(); - await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token => - { - var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(Ignore).Name}_{typeof(TValue).Name}"; - - var consumerStore = _consumerStore.GetOrAdd(consumerKey, _ => - ( - CreateConsumer(groupId), - new CancellationTokenSource() - )); - if (consumerStore.Consumer == null) - { - _logger.LogWarning($"{string.Join("、", topics)}创建消息消费失败或消费组已被释放"); - return; - } - var consumer = consumerStore.Consumer as IConsumer; - var cts = consumerStore.CTS; - - consumer!.Subscribe(topics); - - _ = Task.Factory.StartNew(async () => - { - int count = 0; - while (!cts.IsCancellationRequested) - { - try - { - //_logger.LogInformation($"Kafka消费: {string.Join("", topics)}_{count} 开始拉取消息...."); - count++; - var result = consumer.Consume(cts.Token); - if (result == null || result.Message == null || result.Message.Value == null) - { - await Task.Delay(DelayTime, cts.Token); - continue; - } - - if (result.IsPartitionEOF) - { -#if DEBUG - _logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition); -#endif - await Task.Delay(DelayTime, cts.Token); - continue; - } - if (_kafkaOptionConfig.EnableFilter) - { - var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } }; - // 检查 Header 是否符合条件 - if (!headersFilter.Match(result.Message.Headers)) - { - consumer.Commit(result); // 提交偏移量 - // 跳过消息 - continue; - } - } - bool sucess = await messageHandler(result.Message.Value); - if (sucess) - consumer.Commit(result); // 手动提交 - //else - // consumer.StoreOffset(result); - } - catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex)) - { - _logger.LogError(ex, $"{string.Join("、", topics)}消息消费失败: {ex.Error.Reason}"); - throw; // 抛出异常,以便重试 - } - catch (OperationCanceledException) - { - //ignore - } - catch (ObjectDisposedException) - { - _logger.LogError($"{string.Join("、", topics)}消费者被释放"); - break; - } - catch (Exception ex) - { - _logger.LogError(ex, "处理消息时发生未知错误"); - } - } - }, cts.Token, TaskCreationOptions.None, _kafkaTaskScheduler); - await Task.CompletedTask; - }); - } - catch (Exception ex) - { - - throw; - } - } - - - /// - /// 批量订阅消息 - /// - /// 消息Key类型 - /// 消息Value类型 - /// 主题 - /// 批量消息处理函数 - /// 消费组ID - /// 批次大小 - /// 批次超时时间 - public async Task SubscribeBatchAsync(string topic, Func, Task> messageBatchHandler, string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null) where TKey : notnull where TValue : class - { - try - { - await SubscribeBatchAsync(new[] { topic }, messageBatchHandler, groupId, batchSize, batchTimeout); - } - catch (Exception ex) - { - - throw; - } - } - - /// - /// 批量订阅消息 - /// - /// 消息Key类型 - /// 消息Value类型 - /// 主题列表 - /// 批量消息处理函数 - /// 消费组ID - /// 批次大小 - /// 批次超时时间 - public async Task SubscribeBatchAsync(string[] topics, Func, Task> messageBatchHandler, string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null) where TKey : notnull where TValue : class - { - try - { - // 扩展独立线程,避免阻塞 - _kafkaTaskScheduler.WorkerThreadExpansion(); - await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token => - { - - var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(TKey).Name}_{typeof(TValue).Name}"; - - var consumerStore = _consumerStore.GetOrAdd(consumerKey, _ => - ( - CreateConsumer(groupId), - new CancellationTokenSource() - )); - if (consumerStore.Consumer == null) - { - _logger.LogWarning($"{string.Join("、", topics)}创建消息消费失败或消费组已被释放"); - return; - } - var consumer = consumerStore.Consumer as IConsumer; - var cts = consumerStore.CTS; - - consumer!.Subscribe(topics); - - var timeout = batchTimeout ?? TimeSpan.FromSeconds(5); // 默认超时时间调整为5秒 - - _ = Task.Factory.StartNew(async () => - { - var messages = new List<(TValue Value, TopicPartitionOffset Offset)>(); - var startTime = DateTime.UtcNow; - - while (!cts.IsCancellationRequested) - { - try - { - // 非阻塞快速累积消息 - while (messages.Count < batchSize && (DateTime.UtcNow - startTime) < timeout) - { - var result = consumer.Consume(TimeSpan.Zero); // 非阻塞调用 - - if (result != null) - { - if (result.IsPartitionEOF) - { -#if DEBUG - _logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition); -#endif - await Task.Delay(DelayTime, cts.Token); - } - else if (result.Message.Value != null) - { - if (_kafkaOptionConfig.EnableFilter) - { - var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } }; - // 检查 Header 是否符合条件 - if (!headersFilter.Match(result.Message.Headers)) - { - consumer.Commit(result); // 提交偏移量 - // 跳过消息 - continue; - } - } - messages.Add((result.Message.Value, result.TopicPartitionOffset)); - } - } - else - { - // 无消息时短暂等待 - await Task.Delay(DelayTime, cts.Token); - } - } - - // 处理批次 - if (messages.Count > 0) - { - bool success = await messageBatchHandler(messages.Select(m => m.Value).ToList()); - if (success) - { - var offsetsByPartition = new Dictionary(); - foreach (var msg in messages) - { - var tp = msg.Offset.TopicPartition; - var offset = msg.Offset.Offset; - if (!offsetsByPartition.TryGetValue(tp, out var currentMax) || offset > currentMax) - { - offsetsByPartition[tp] = offset; - } - } - - var offsetsToCommit = offsetsByPartition - .Select(kv => new TopicPartitionOffset(kv.Key, new Offset(kv.Value + 1))) - .ToList(); - consumer.Commit(offsetsToCommit); - } - messages.Clear(); - } - - startTime = DateTime.UtcNow; - } - catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex)) - { - _logger.LogError(ex, $"{string.Join("、", topics)} 消息消费失败: {ex.Error.Reason}"); - throw; // 抛出异常,以便重试 - } - catch (KafkaException ex) when (KafkaPollyPipeline.IsRecoverableError(ex)) - { - _logger.LogError(ex, $"{string.Join("、", topics)} 消息消费失败: {ex.Error.Reason}"); - throw; // 抛出异常,以便重试 - } - catch (OperationCanceledException) - { - //ignore - } - catch (ObjectDisposedException) - { - _logger.LogError($"{string.Join("、", topics)}消费者被释放"); - break; - } - catch (Exception ex) - { - _logger.LogError(ex, "处理批量消息时发生未知错误"); - } - } - }, cts.Token, TaskCreationOptions.None, _kafkaTaskScheduler); - - await Task.CompletedTask; - }); - } - catch (Exception ex) - { - - throw; - } - } - - - /// - /// 批量订阅消息 - /// - /// 消息Value类型 - /// 主题列表 - /// 批量消息处理函数 - /// 消费组ID - /// 批次大小 - /// 批次超时时间 - /// 消费等待时间 - public async Task SubscribeBatchAsync(string topic, Func, Task> messageBatchHandler, string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null, TimeSpan? consumeTimeout = null) where TValue : class - { - try - { - await SubscribeBatchAsync(new[] { topic }, messageBatchHandler, groupId, batchSize, batchTimeout, consumeTimeout); - } - catch (Exception ex) - { - - throw; - } - - } - - - /// - /// 批量订阅消息 - /// - /// 消息Value类型 - /// 主题列表 - /// 批量消息处理函数 - /// 消费组ID - /// 批次大小 - /// 批次超时时间 - /// 消费等待时间 - public async Task SubscribeBatchAsync(string[] topics, Func, Task> messageBatchHandler, string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null, TimeSpan? consumeTimeout = null) where TValue : class - { - try - { - // 扩展独立线程,避免阻塞 - _kafkaTaskScheduler.WorkerThreadExpansion(); - await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token => - { - - var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(Ignore).Name}_{typeof(TValue).Name}"; - var consumerStore = _consumerStore.GetOrAdd(consumerKey, _ => - ( - CreateConsumer(groupId), - new CancellationTokenSource() - )); - if (consumerStore.Consumer == null) - { - _logger.LogWarning($"{string.Join("、", topics)}创建消息消费失败或消费组已被释放"); - return; - } - var consumer = consumerStore.Consumer as IConsumer; - var cts = consumerStore.CTS; - - consumer!.Subscribe(topics); - - var timeout = batchTimeout ?? TimeSpan.FromSeconds(5); // 默认超时时间调整为5秒 - - _ = Task.Factory.StartNew(async () => - { - var messages = new List<(TValue Value, TopicPartitionOffset Offset)>(); - var startTime = DateTime.UtcNow; - - while (!cts.IsCancellationRequested) - { - try - { - // 非阻塞快速累积消息 - while (messages.Count < batchSize && (DateTime.UtcNow - startTime) < timeout) - { - var result = consumer.Consume(TimeSpan.Zero); // 非阻塞调用 - - if (result != null) - { - if (result.IsPartitionEOF) - { - //_logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition); - await Task.Delay(DelayTime, cts.Token); - } - else if (result.Message.Value != null) - { - if (_kafkaOptionConfig.EnableFilter) - { - var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } }; - // 检查 Header 是否符合条件 - if (!headersFilter.Match(result.Message.Headers)) - { - consumer.Commit(result); // 提交偏移量 - // 跳过消息 - continue; - } - } - messages.Add((result.Message.Value, result.TopicPartitionOffset)); - } - } - else - { - // 无消息时短暂等待 - await Task.Delay(DelayTime, cts.Token); - } - } - - // 处理批次 - if (messages.Count > 0) - { - bool success = await messageBatchHandler(messages.Select(m => m.Value).ToList()); - if (success) - { - var offsetsByPartition = new Dictionary(); - foreach (var msg in messages) - { - var tp = msg.Offset.TopicPartition; - var offset = msg.Offset.Offset; - if (!offsetsByPartition.TryGetValue(tp, out var currentMax) || offset > currentMax) - { - offsetsByPartition[tp] = offset; - } - } - - var offsetsToCommit = offsetsByPartition - .Select(kv => new TopicPartitionOffset(kv.Key, new Offset(kv.Value + 1))) - .ToList(); - consumer.Commit(offsetsToCommit); - } - messages.Clear(); - } - - startTime = DateTime.UtcNow; - } - catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex)) - { - _logger.LogError(ex, $"消息消费失败: {ex.Error.Reason}"); - throw; // 抛出异常,以便重试 - } - catch (KafkaException ex) when (KafkaPollyPipeline.IsRecoverableError(ex)) - { - _logger.LogError(ex, $"{string.Join("、", topics)} 消息消费失败: {ex.Error.Reason}"); - throw; // 抛出异常,以便重试 - } - catch (OperationCanceledException) - { - //ignore - } - catch (ObjectDisposedException) - { - _logger.LogError($"{string.Join("、", topics)}消费者被释放"); - break; - } - catch (Exception ex) - { - _logger.LogError(ex, "处理批量消息时发生未知错误"); - } - } - }, cts.Token, TaskCreationOptions.None, _kafkaTaskScheduler); - - await Task.CompletedTask; - }); - } - catch (Exception ex) - { - - throw; - } - } - - - /// - /// 取消消息订阅 - /// - /// - /// - public void Unsubscribe(string[] topics, string? groupId) where TKey : notnull where TValue : class - { - try - { - var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(TKey).Name}_{typeof(TValue).Name}"; - if (_consumerStore.TryGetValue(consumerKey, out var entry)) - { - entry.CTS.Cancel(); - (entry.Consumer as IDisposable)?.Dispose(); - entry.CTS.Dispose(); - // 从字典中移除 - _consumerStore.TryRemove(consumerKey, out entry); - } - } - catch (Exception ex) - { - - throw; - } - } - - /// - /// 释放资源 - /// - public void Dispose() - { - foreach (var entry in _consumerStore.Values) - { - entry.CTS.Cancel(); - (entry.Consumer as IDisposable)?.Dispose(); - entry.CTS.Dispose(); - } - _consumerStore.Clear(); - } - } -} diff --git a/modules/JiShe.CollectBus.Kafka/Consumer/IConsumerService.cs b/modules/JiShe.CollectBus.Kafka/Consumer/IConsumerService.cs deleted file mode 100644 index b4b4274..0000000 --- a/modules/JiShe.CollectBus.Kafka/Consumer/IConsumerService.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace JiShe.CollectBus.Kafka.Consumer; - -public interface IConsumerService -{ - Task SubscribeAsync(string topic, Func> messageHandler, - string? groupId = null) where TKey : notnull where TValue : class; - - /// - /// 订阅消息 - /// - /// - /// - /// - /// - Task SubscribeAsync(string topic, Func> messageHandler, string? groupId = null) - where TValue : class; - - Task SubscribeAsync(string[] topics, Func> messageHandler, string? groupId) - where TKey : notnull where TValue : class; - - - /// - /// 订阅消息 - /// - /// - /// - /// - /// - /// - Task SubscribeAsync(string[] topics, Func> messageHandler, string? groupId = null) - where TValue : class; - - Task SubscribeBatchAsync(string[] topics, Func, Task> messageBatchHandler, - string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null) - where TKey : notnull where TValue : class; - - Task SubscribeBatchAsync(string topic, Func, Task> messageBatchHandler, - string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null) - where TKey : notnull where TValue : class; - - Task SubscribeBatchAsync(string topic, Func, Task> messageBatchHandler, - string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null, TimeSpan? consumeTimeout = null) - where TValue : class; - - Task SubscribeBatchAsync(string[] topics, Func, Task> messageBatchHandler, - string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null, TimeSpan? consumeTimeout = null) - where TValue : class; - - void Unsubscribe(string[] topics, string groupId) where TKey : notnull where TValue : class; -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/HostedService.cs b/modules/JiShe.CollectBus.Kafka/HostedService.cs deleted file mode 100644 index c2e672c..0000000 --- a/modules/JiShe.CollectBus.Kafka/HostedService.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Kafka -{ - public class HostedService : IHostedService, IDisposable - { - private readonly ILogger _logger; - private readonly IServiceProvider _provider; - public HostedService(ILogger logger, IServiceProvider provider) - { - _logger = logger; - _provider = provider; - } - - public Task StartAsync(CancellationToken cancellationToken) - { - _logger.LogInformation("程序启动"); - Task.Run(() => - { - _provider.UseKafkaSubscribe(); - }); - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - _logger.LogInformation("结束"); - return Task.CompletedTask; - } - - public void Dispose() - { - - } - } -} diff --git a/modules/JiShe.CollectBus.Kafka/Internal/HeadersFilter.cs b/modules/JiShe.CollectBus.Kafka/Internal/HeadersFilter.cs deleted file mode 100644 index 8bcdcdc..0000000 --- a/modules/JiShe.CollectBus.Kafka/Internal/HeadersFilter.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Confluent.Kafka; - -namespace JiShe.CollectBus.Kafka.Internal; - -/// -/// 消息头过滤器 -/// -public class HeadersFilter : Dictionary -{ - /// - /// 判断Headers是否匹配 - /// - /// - /// - public bool Match(Headers headers) - { - foreach (var kvp in this) - if (!headers.TryGetLastBytes(kvp.Key, out var value) || !value.SequenceEqual(kvp.Value)) - return false; - return true; - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/Internal/IKafkaSubscribe.cs b/modules/JiShe.CollectBus.Kafka/Internal/IKafkaSubscribe.cs deleted file mode 100644 index 5345f1b..0000000 --- a/modules/JiShe.CollectBus.Kafka/Internal/IKafkaSubscribe.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace JiShe.CollectBus.Kafka.Internal; - -/// -/// Kafka订阅者 -/// -/// 订阅者需要继承此接口并需要依赖注入,并使用标记 -/// -/// -public interface IKafkaSubscribe -{ -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/Internal/ISubscribeAck.cs b/modules/JiShe.CollectBus.Kafka/Internal/ISubscribeAck.cs deleted file mode 100644 index 27512f7..0000000 --- a/modules/JiShe.CollectBus.Kafka/Internal/ISubscribeAck.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace JiShe.CollectBus.Kafka.Internal; - -public interface ISubscribeAck -{ - /// - /// 是否成功标记 - /// - bool Ack { get; set; } - - /// - /// 消息 - /// - string? Msg { get; set; } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/Internal/KafkaOptionConfig.cs b/modules/JiShe.CollectBus.Kafka/Internal/KafkaOptionConfig.cs deleted file mode 100644 index 3b053c8..0000000 --- a/modules/JiShe.CollectBus.Kafka/Internal/KafkaOptionConfig.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Confluent.Kafka; - -namespace JiShe.CollectBus.Kafka.Internal; - -public class KafkaOptionConfig -{ - /// - /// kafka地址 - /// - public string BootstrapServers { get; set; } = null!; - - /// - /// kafka主题副本数量 - /// - public short KafkaReplicationFactor { get; set; } - - /// - /// kafka主题分区数量 - /// - public int NumPartitions { get; set; } - - /// - /// 是否开启过滤器 - /// - public bool EnableFilter { get; set; } = true; - - /// - /// 是否开启认证 - /// - public bool EnableAuthorization { get; set; } = false; - - /// - /// 安全协议 - /// - public SecurityProtocol SecurityProtocol { get; set; } = SecurityProtocol.SaslPlaintext; - - /// - /// 认证方式 - /// - public SaslMechanism SaslMechanism { get; set; } = SaslMechanism.Plain; - - /// - /// 用户名 - /// - public string? SaslUserName { get; set; } - - /// - /// 密码 - /// - public string? SaslPassword { get; set; } - - /// - /// 订阅任务线程数量 - /// 当主题未指定时,订阅任务线程数量默认为:-1 - /// 优先级低于订阅任务特性TaskCount值 - /// - public int TaskThreadCount { get; set; } = -1; - -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/Internal/KafkaPollyPipeline.cs b/modules/JiShe.CollectBus.Kafka/Internal/KafkaPollyPipeline.cs deleted file mode 100644 index fd4bb1b..0000000 --- a/modules/JiShe.CollectBus.Kafka/Internal/KafkaPollyPipeline.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Confluent.Kafka; -using Microsoft.Extensions.Logging; -using Polly; -using Polly.CircuitBreaker; -using Polly.Retry; - -namespace JiShe.CollectBus.Kafka.Internal -{ - public class KafkaPollyPipeline - { - - private readonly ILogger _logger; - public KafkaPollyPipeline(ILogger logger) - { - _logger= logger; - } - - /// - /// 判断是否可恢复的异常 - /// - /// - /// - public static bool IsRecoverableError(Exception ex) - { - var errorList= new List - { - ErrorCode.GroupLoadInProgress, - ErrorCode.Local_Retry, - ErrorCode.Local_MaxPollExceeded, - ErrorCode.RequestTimedOut, - ErrorCode.LeaderNotAvailable, - ErrorCode.NotLeaderForPartition, - ErrorCode.RebalanceInProgress, - ErrorCode.NotCoordinatorForGroup, - ErrorCode.NetworkException, - ErrorCode.GroupCoordinatorNotAvailable, - ErrorCode.InvalidGroupId, - ErrorCode.IllegalGeneration - }; - return ex switch - { - ConsumeException kafkaEx => errorList.Contains(kafkaEx.Error.Code), - KafkaException kafkaEx =>kafkaEx.Error.IsFatal && errorList.Contains(kafkaEx.Error.Code), - _ => false - }; - - } - - /// - /// 创建重试 + 断路器 - /// - /// - public ResiliencePipeline KafkaPipeline - { - get - { - // 组合重试 + 断路器 - ResiliencePipeline pipeline = new ResiliencePipelineBuilder() - .AddRetry(new RetryStrategyOptions - { - ShouldHandle = args => args.Outcome.Exception switch - { - not null when IsRecoverableError(args.Outcome.Exception) => - PredicateResult.True(), - _ => PredicateResult.False() - }, - Delay = TimeSpan.FromSeconds(2), - OnRetry = args => - { - _logger.LogWarning($"重试中... 第 {args.AttemptNumber} 次,原因: {args.Outcome.Exception?.Message}"); - return default; - } - }) - .AddCircuitBreaker(new CircuitBreakerStrategyOptions - { - ShouldHandle = args => args.Outcome.Exception switch - { - not null when IsRecoverableError(args.Outcome.Exception) => - PredicateResult.True(), - _ => PredicateResult.False() - }, - FailureRatio = 0.8, // 80% 失败触发熔断 - SamplingDuration = TimeSpan.FromSeconds(10), - MinimumThroughput = 4, // 至少4次调用才计算失败率 - BreakDuration = TimeSpan.FromSeconds(10), - OnOpened = args => - { - _logger.LogWarning($"熔断器开启,等待 {args.BreakDuration} 后重试"); - return default; - }, - OnClosed = _ => - { - _logger.LogWarning("熔断器关闭,再次开始重试"); - return default; - } - }) - .Build(); - return pipeline; - } - - } - - - } -} diff --git a/modules/JiShe.CollectBus.Kafka/Internal/KafkaTaskScheduler.cs b/modules/JiShe.CollectBus.Kafka/Internal/KafkaTaskScheduler.cs deleted file mode 100644 index 9afe7fa..0000000 --- a/modules/JiShe.CollectBus.Kafka/Internal/KafkaTaskScheduler.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System.Collections.Concurrent; - -namespace JiShe.CollectBus.Kafka.Internal -{ - public class KafkaTaskScheduler : TaskScheduler, IDisposable - { - private readonly BlockingCollection _tasksCollection=new BlockingCollection (); - private readonly List _workerThreads; - private readonly object _disposeLock = new object(); - private bool _isDisposed; - - /// - /// 当前队列中的任务数 - /// - public int QueuedTasks => _tasksCollection.Count; - - /// - /// 当前工作线程数 - /// - public int WorkerThreads => _workerThreads.Count; - - /// - /// 初始化任务调度器 - /// - public KafkaTaskScheduler() - { - // 默认最大并发线程数为CPU核心数 - int MaxConcurrencyLevel = Environment.ProcessorCount; - _workerThreads = new List(MaxConcurrencyLevel); - for (int i = 0; i < MaxConcurrencyLevel; i++) - { - var thread = new Thread(ExecuteScheduledTasks) - { - IsBackground = true, - Name = $"KafkaWorkerTask-{i + 1}" - }; - thread.Start(); - _workerThreads.Add(thread); - } - - } - - /// - /// 扩容工作线程调度 - /// 可以启动多个工作线程来处理任务 - /// - /// 扩展独立线程数(默认为1) - public void WorkerThreadExpansion(int taskNum = 1) - { - int currCount = WorkerThreads+1; - Parallel.For(0, taskNum, (index) => - { - var thread = new Thread(ExecuteScheduledTasks) - { - IsBackground = true, - Name = $"KafkaWorkerTask-{index+ currCount}" - }; - thread.Start(); - _workerThreads.Add(thread); - }); - } - - /// - /// 工作线程执行循环 - /// - private void ExecuteScheduledTasks() - { - try - { - foreach (var task in _tasksCollection.GetConsumingEnumerable()) - { - TryExecuteTaskSafely(task); - } - } - catch (OperationCanceledException) { } - catch (ObjectDisposedException) { } - } - - /// - /// 安全执行任务并处理异常 - /// - private void TryExecuteTaskSafely(Task task) - { - try - { - TryExecuteTask(task); - } - catch (OperationCanceledException){} - catch (Exception ex) - { - OnExceptionOccurred(ex); - } - } - - #region TaskScheduler 重写方法 - protected override IEnumerable GetScheduledTasks() - { - ThrowIfDisposed(); - return _tasksCollection.ToList(); - } - - protected override void QueueTask(Task task) - { - ThrowIfDisposed(); - _tasksCollection.Add(task); - } - - protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) - { - // 禁止内联执行以强制所有任务在专用线程执行 - return false; - } - #endregion - - #region 释放资源 - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - lock (_disposeLock) - { - if (_isDisposed) return; - - if (disposing) - { - // 停止接收新任务 - _tasksCollection.CompleteAdding(); - - // 等待所有工作线程退出 - foreach (var thread in _workerThreads) - { - if (thread.IsAlive) - { - thread.Join(TimeSpan.FromSeconds(5)); - } - } - // 释放资源 - _tasksCollection.Dispose(); - } - _isDisposed = true; - } - } - - private void ThrowIfDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - } - #endregion - - #region 异常事件处理 - /// - /// 任务执行异常时触发 - /// - public event Action? ExceptionEvent; - - private void OnExceptionOccurred(Exception ex) - { - ExceptionEvent?.Invoke(ex); - } - #endregion - } -} diff --git a/modules/JiShe.CollectBus.Kafka/Internal/ReflectionHelper.cs b/modules/JiShe.CollectBus.Kafka/Internal/ReflectionHelper.cs deleted file mode 100644 index a39db81..0000000 --- a/modules/JiShe.CollectBus.Kafka/Internal/ReflectionHelper.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Collections; -using System.Reflection; - -namespace JiShe.CollectBus.Kafka.Internal; - -/// -/// 反射辅助类 -/// -public static class ReflectionHelper -{ - /// - /// 集合类型 - /// Item1:参数类型 - /// Item2:集合元素类型 - /// - public static Tuple GetParameterTypeInfo(this MethodInfo method, int parameterIndex = 0) - { - // 参数校验 - if (method == null) throw new ArgumentNullException(nameof(method)); - var parameters = method.GetParameters(); - if (parameterIndex < 0 || parameterIndex >= parameters.Length) - throw new ArgumentOutOfRangeException(nameof(parameterIndex)); - - var param = parameters[parameterIndex]; - var paramType = param.ParameterType; - Type? elementType = null; - - // 判断是否是集合类型(排除字符串) - if (paramType != typeof(string) && IsEnumerableType(paramType)) - elementType = GetEnumerableElementType(paramType); - - return Tuple.Create(paramType, elementType); - } - - /// - /// 判断是否是集合类型(排除字符串) - /// - public static bool IsEnumerableType(this Type type) - { - return type.IsArray - || (type.IsGenericType && type.GetInterfaces() - .Any(t => t.IsGenericType - && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))) - || type.GetInterfaces().Any(t => t == typeof(IEnumerable)); - } - - /// - /// 获取集合元素的类型 - /// - public static Type? GetEnumerableElementType(this Type type) - { - // 处理数组类型 - if (type.IsArray) - return type.GetElementType(); - - // 处理直接实现IEnumerable的类型(如IEnumerable本身) - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - return type.GetGenericArguments()[0]; - - // 处理通过接口实现IEnumerable的泛型集合(如List) - var genericEnumerable = type.GetInterfaces() - .FirstOrDefault(t => t.IsGenericType - && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); - if (genericEnumerable != null) - return genericEnumerable.GetGenericArguments()[0]; - - // 处理非泛型集合类型(如 ArrayList) - if (typeof(IEnumerable).IsAssignableFrom(type) && type == typeof(ArrayList)) - return typeof(ArrayList); - // 返回null表示无法确定元素类型 - return null; - } - - - /// - /// 判断是否使用强转换 - /// - /// - /// - public static bool IsConvertType(this Type targetType) - { - // 处理可空类型 - var underlyingType = Nullable.GetUnderlyingType(targetType) ?? targetType; - // 情况1:值类型或基元类型(如 int、DateTime) - if (underlyingType.IsValueType || underlyingType.IsPrimitive) - return true; - // 情况2:字符串类型直接赋值 - if (underlyingType == typeof(string)) - return true; - - // 情况3:枚举类型处理 - //else if (underlyingType.IsEnum) - //{ - // if (Enum.IsDefined(underlyingType, msg)) - // { - // convertedValue = Enum.Parse(underlyingType, msg.ToString()); - // return true; - // } - // return false; - //} - return false; - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/Internal/SubscribeResult.cs b/modules/JiShe.CollectBus.Kafka/Internal/SubscribeResult.cs deleted file mode 100644 index d3fbcf6..0000000 --- a/modules/JiShe.CollectBus.Kafka/Internal/SubscribeResult.cs +++ /dev/null @@ -1,62 +0,0 @@ -namespace JiShe.CollectBus.Kafka.Internal; - -public class SubscribeResult : ISubscribeAck -{ - /// - /// 是否成功 - /// - public bool Ack { get; set; } - - /// - /// 消息 - /// - public string? Msg { get; set; } - - - /// - /// 成功 - /// - /// 消息 - public SubscribeResult Success(string? msg = null) - { - Ack = true; - Msg = msg; - return this; - } - - /// - /// 失败 - /// - /// - /// - public SubscribeResult Fail(string? msg = null) - { - Msg = msg; - Ack = false; - return this; - } -} - -public static class SubscribeAck -{ - /// - /// 成功 - /// - /// 消息 - /// - public static ISubscribeAck Success(string? msg = null) - { - return new SubscribeResult().Success(msg); - } - - - /// - /// 失败 - /// - /// 消息 - /// - public static ISubscribeAck Fail(string? msg = null) - { - return new SubscribeResult().Fail(msg); - } -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.Kafka/JiShe.CollectBus.Kafka.csproj b/modules/JiShe.CollectBus.Kafka/JiShe.CollectBus.Kafka.csproj deleted file mode 100644 index a0fd2f7..0000000 --- a/modules/JiShe.CollectBus.Kafka/JiShe.CollectBus.Kafka.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - - - diff --git a/modules/JiShe.CollectBus.Kafka/KafkaSubscribeExtensions.cs b/modules/JiShe.CollectBus.Kafka/KafkaSubscribeExtensions.cs deleted file mode 100644 index 15eb60d..0000000 --- a/modules/JiShe.CollectBus.Kafka/KafkaSubscribeExtensions.cs +++ /dev/null @@ -1,435 +0,0 @@ -using Confluent.Kafka; -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Extensions; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.Kafka.AdminClient; -using JiShe.CollectBus.Kafka.Attributes; -using JiShe.CollectBus.Kafka.Consumer; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Kafka.Serialization; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System.Collections; -using System.ComponentModel; -using System.Reflection; - -namespace JiShe.CollectBus.Kafka -{ - public static class KafkaSubscribeExtensions - { - - private static long _threadCount = 0; - private static long _topicSubscribeCount = 0; - private static long _threadStartCount = 0; - - public static void UseInitKafkaTopic(this IServiceProvider provider) - { - //初始化主题信息 - var kafkaAdminClient = provider.GetRequiredService(); - var kafkaOptions = provider.GetRequiredService>(); - - var topics = ProtocolConstExtensions.GetAllTopicNamesByIssued(); - topics.AddRange(ProtocolConstExtensions.GetAllTopicNamesByReceived()); - foreach (var item in topics) - { - kafkaAdminClient.CreateTopicAsync(item, kafkaOptions.Value.NumPartitions, kafkaOptions.Value.KafkaReplicationFactor).ConfigureAwait(false).GetAwaiter().GetResult(); - } - - } - - /// - /// 添加Kafka订阅 - /// - public static void UseKafkaSubscribe(this IServiceProvider provider) - { - var lifetime = provider.GetRequiredService(); - var kafkaOptions = provider.GetRequiredService>(); - lifetime.ApplicationStarted.Register(() => - { - var logger = provider.GetRequiredService>(); - //var threadCount = 0; - //var topicCount = 0; - var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); - if (string.IsNullOrWhiteSpace(assemblyPath)) - { - logger.LogWarning($"kafka订阅未能找到程序路径"); - return; - } - var dllFiles = Directory.GetFiles(assemblyPath, "*.dll"); - foreach (var file in dllFiles) - { - // 跳过已加载的程序集 - var assemblyName = AssemblyName.GetAssemblyName(file); - var existingAssembly = AppDomain.CurrentDomain.GetAssemblies() - .FirstOrDefault(a => a.GetName().FullName == assemblyName.FullName); - var assembly = existingAssembly ?? Assembly.LoadFrom(file); - // 实现IKafkaSubscribe接口 - var subscribeTypes = assembly.GetTypes().Where(type => - typeof(IKafkaSubscribe).IsAssignableFrom(type) && - !type.IsAbstract && !type.IsInterface).ToList(); - if (subscribeTypes.Count == 0) - continue; - - // 并行处理 - Parallel.ForEach(subscribeTypes, subscribeType => - { - var subscribes = provider.GetServices(subscribeType).ToList(); - Parallel.ForEach(subscribes, subscribe => - { - if (subscribe != null) - { - Tuple tuple = BuildKafkaSubscribe(subscribe, provider, logger, kafkaOptions.Value); - //threadCount += tuple.Item1; - //topicCount += tuple.Item2; - } - }); - }); - //foreach (var subscribeType in subscribeTypes) - //{ - // var subscribes = provider.GetServices(subscribeType).ToList(); - // subscribes.ForEach(subscribe => - // { - // if (subscribe != null) - // { - // Tuple tuple = BuildKafkaSubscribe(subscribe, provider, logger, kafkaOptions.Value); - // threadCount += tuple.Item1; - // topicCount += tuple.Item2; - // } - // }); - //} - } - logger.LogWarning($"kafka订阅主题:{_topicSubscribeCount}数,共启动:{_threadCount}线程"); - - var kafkaTaskScheduler = provider.GetRequiredService(); - kafkaTaskScheduler.ExceptionEvent += (ex) => - { - logger.LogError(ex, "Kafka任务调度异常"); - }; - //logger.LogWarning($"kafka订阅工作线程数:{kafkaTaskScheduler.WorkerThreads},队列任务:{kafkaTaskScheduler.QueuedTasks}数"); - // - // 订阅调度监控测试可打开 - //_ = Task.Factory.StartNew(async () => - // { - // while (true) - // { - // logger.LogWarning($"kafka订阅工作线程数:{kafkaTaskScheduler.WorkerThreads},队列任务:{kafkaTaskScheduler.QueuedTasks}数"); - // await Task.Delay(TimeSpan.FromSeconds(5)); - // } - // }); - - }); - - } - - /// - /// 添加Kafka订阅 - /// - public static void UseKafkaSubscribersAsync(this IApplicationBuilder app, Assembly assembly) - { - var provider = app.ApplicationServices; - var lifetime = provider.GetRequiredService(); - var kafkaOptions = provider.GetRequiredService>(); - lifetime.ApplicationStarted.Register(() => - { - var logger = provider.GetRequiredService>(); - int threadCount = 0; - int topicCount = 0; - var subscribeTypes = assembly.GetTypes() - .Where(t => typeof(IKafkaSubscribe).IsAssignableFrom(t)) - .ToList(); - - if (subscribeTypes.Count == 0) return; - - Parallel.ForEach(subscribeTypes, subscribeType => - { - var subscribes = provider.GetServices(subscribeType).ToList(); - Parallel.ForEach(subscribes, subscribe => - { - if (subscribe != null) - { - Tuple tuple = BuildKafkaSubscribe(subscribe, provider, logger, kafkaOptions.Value); - //threadCount += tuple.Item1; - //topicCount += tuple.Item2; - } - }); - }); - - //foreach (var subscribeType in subscribeTypes) - //{ - // var subscribes = provider.GetServices(subscribeType).ToList(); - // subscribes.ForEach(subscribe => - // { - - // if (subscribe != null) - // { - // Tuple tuple = BuildKafkaSubscribe(subscribe, provider, logger, kafkaOptions.Value); - // threadCount += tuple.Item1; - // topicCount += tuple.Item2; - // } - // }); - //} - logger.LogInformation($"kafka订阅主题:{topicCount}数,共启动:{threadCount}线程"); - }); - } - - /// - /// 构建Kafka订阅 - /// - private static Tuple BuildKafkaSubscribe(object subscribe, IServiceProvider provider, ILogger logger, KafkaOptionConfig kafkaOptionConfig) - { - var subscribedMethods = subscribe.GetType().GetMethods() - .Select(m => new { Method = m, Attribute = m.GetCustomAttribute() }) - .Where(x => x.Attribute != null) - .ToArray(); - //var configuration = provider.GetRequiredService(); - int threadCount = 0; - - Parallel.ForEach(subscribedMethods, sub => - { - Interlocked.Increment(ref _topicSubscribeCount); - var adminClientService = provider.GetRequiredService(); - int topicCount = adminClientService.GetTopicPartitionsNum(sub.Attribute!.Topic); - // 可以根据配置文件TaskThreadCount来配置线程数 - int partitionCount = sub.Attribute!.TaskCount == -1 ? (kafkaOptionConfig.TaskThreadCount==-1? topicCount: kafkaOptionConfig.TaskThreadCount) : sub.Attribute!.TaskCount;// kafkaOptionConfig.NumPartitions; - partitionCount = partitionCount > topicCount ? topicCount : partitionCount; - //partitionCount = sub.Attribute!.TaskCount == -1 ? adminClientService.GetTopicPartitionsNum(sub.Attribute!.Topic) : sub.Attribute!.TaskCount; - if (partitionCount <= 0) - partitionCount = 1; - Parallel.For(0,partitionCount, async (partition) => - { - Interlocked.Increment(ref _threadCount); - //Task.Run(() => StartConsumerAsync(provider, sub.Attribute!, sub.Method, subscribe, logger)); - //threadCount++; - await StartConsumerAsync(provider, sub.Attribute!, sub.Method, subscribe, logger); - }); - - }); - - //foreach (var sub in subscribedMethods) - //{ - //// //int partitionCount = sub.Attribute!.TaskCount==-1?3: sub.Attribute!.TaskCount;// kafkaOptionConfig.NumPartitions; - // var adminClientService = provider.GetRequiredService(); - - // int topicCount = adminClientService.GetTopicPartitionsNum(sub.Attribute!.Topic); - - // int partitionCount = sub.Attribute!.TaskCount == -1 ? topicCount : sub.Attribute!.TaskCount;// kafkaOptionConfig.NumPartitions; - - // partitionCount = partitionCount > topicCount ? topicCount : partitionCount; - // //partitionCount = sub.Attribute!.TaskCount == -1 ? adminClientService.GetTopicPartitionsNum(sub.Attribute!.Topic) : sub.Attribute!.TaskCount; - // if (partitionCount <= 0) - // partitionCount = 1; - // for (int i = 0; i < partitionCount; i++) - // { - // //if (sub.Attribute!.Topic == ProtocolConst.SubscriberLoginReceivedEventName) - // Task.Run(() => StartConsumerAsync(provider, sub.Attribute!, sub.Method, subscribe, logger)); - // threadCount++; - // } - //} - return Tuple.Create(threadCount, subscribedMethods.Length); - } - - /// - /// 启动后台消费线程 - /// - private static async Task StartConsumerAsync(IServiceProvider provider, KafkaSubscribeAttribute attr, MethodInfo method, object subscribe, ILogger logger) - { - var consumerService = provider.GetRequiredService(); - - if (attr.EnableBatch) - { - Interlocked.Increment(ref _threadStartCount); - logger.LogInformation($"kafka开启线程消费:{_threadStartCount}"); - await consumerService.SubscribeBatchAsync(attr.Topic, async (message) => - { - try - { -#if DEBUG - logger.LogInformation($"kafka批量消费消息:{message.Serialize()}"); -#endif - // 处理消息 - return await ProcessMessageAsync(message.ToList(), method, subscribe); - } - catch (ConsumeException ex) - { - // 处理消费错误 - logger.LogError($"kafka批量消费异常:{ex.Message}"); - } - catch (Exception ex) - { - // 处理消费错误 - logger.LogError($"kafka批量消费异常:{ex.Message}"); - } - return await Task.FromResult(false); - }, attr.GroupId, attr.BatchSize, attr.BatchTimeout); - } - else - { - Interlocked.Increment(ref _threadStartCount); - logger.LogInformation($"kafka开启线程消费:{_threadStartCount}"); - await consumerService.SubscribeAsync(attr.Topic, async (message) => - { - try - { -#if DEBUG - logger.LogInformation($"kafka消费消息:{message}"); -#endif - // 处理消息 - return await ProcessMessageAsync(new List() { message }, method, subscribe); - } - catch (ConsumeException ex) - { - // 处理消费错误 - logger.LogError($"kafka消费异常:{ex.Message}"); - } - catch (Exception ex) - { - // 处理消费错误 - logger.LogError($"kafka批量消费异常:{ex.Message}"); - } - return await Task.FromResult(false); - }, attr.GroupId); - } - - } - - - /// - /// 处理消息 - /// - private static async Task ProcessMessageAsync(List messages, MethodInfo method, object subscribe) - { - try - { - var parameters = method.GetParameters(); - bool isGenericTask = method.ReturnType.IsGenericType - && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>); - bool existParameters = parameters.Length > 0; - object[]? executeParameters = null; - - if (existParameters) - { - IList? list = null; - Tuple tuple = method.GetParameterTypeInfo(); - bool isEnumerable = false; - if (tuple.Item2 != null) - { - Type listType = typeof(List<>).MakeGenericType(tuple.Item2); - list = (IList)Activator.CreateInstance(listType)!; - isEnumerable = tuple.Item2.IsConvertType(); - } - else - { - isEnumerable = tuple.Item1.IsConvertType(); - } - #region 暂时 - //foreach (var msg in messages) - //{ - // if (tuple.Item2 != null) - // { - // if (isEnumerable) - // { - // var parameterType = parameters[0].ParameterType; - // var data=messages?.Serialize().Deserialize(parameterType); - // messageObj = data!=null? new[] { data }:null; - // break; - // } - // else - // { - // // 集合类型 - // var data = msg?.Serialize().Deserialize(tuple.Item2) /*isEnumerable ? Convert.ChangeType(msg, tuple.Item2) : msg?.Serialize().Deserialize(tuple.Item2)*/; - // if (data != null) - // list?.Add(data); - // } - - // } - // else - // { - // // (dynamic)Convert.ChangeType(msg, tuple.Item1) - // using (var stream = new MemoryStream(msg)) - // { - // var data1= System.Text.Json.JsonSerializer.Deserialize(stream, tuple.Item1); - // } - // var data = isEnumerable ? System.Text.Json.JsonSerializer.Deserialize(msg, tuple.Item1): msg?.ToString()?.Deserialize(tuple.Item1); - // if (data != null) - // messageObj = new[] { data }; - // } - //} - //if (tuple.Item2 != null && list != null && list.Count > 0) - //{ - // messageObj = new[] { list }; - //} - #endregion - var parameterDescriptors = method.GetParameters(); - executeParameters = new object?[parameterDescriptors.Length]; - for (var i = 0; i < parameterDescriptors.Length; i++) - { - foreach (var item in messages) - { - - object? tempParameter = null; - var parameterDescriptor = parameterDescriptors[i]; - if (KafkaSerialization.IsJsonType(item)) - { - tempParameter = KafkaSerialization.Deserialize(item, tuple.Item2 != null ? tuple.Item2 : parameterDescriptor.ParameterType); - } - else - { - - var converter = TypeDescriptor.GetConverter(parameterDescriptor.ParameterType); - if (converter.CanConvertFrom(item.GetType())) - { - tempParameter = converter.ConvertFrom(item); - } - else - { - if (parameterDescriptor.ParameterType.IsInstanceOfType(item)) - tempParameter = item; - else - tempParameter = Convert.ChangeType(item, parameterDescriptor.ParameterType); - } - } - if (tuple.Item2 == null) - { - executeParameters[i] = tempParameter; - } - else - { - list.Add(tempParameter); - } - - } - if (list != null && list.Count > 0) - executeParameters[i] = list; - } - } - - var result = method.Invoke(subscribe, executeParameters); - if (result is Task genericTask) - { - await genericTask.ConfigureAwait(false); - return genericTask.Result.Ack; - } - else if (result is Task nonGenericTask) - { - await nonGenericTask.ConfigureAwait(false); - return true; - } - else if (result is ISubscribeAck ackResult) - { - return ackResult.Ack; - } - return false; - } - catch (Exception ex) - { - - throw; - } - } - } - - -} diff --git a/modules/JiShe.CollectBus.Kafka/Producer/IProducerService.cs b/modules/JiShe.CollectBus.Kafka/Producer/IProducerService.cs deleted file mode 100644 index 8cbe665..0000000 --- a/modules/JiShe.CollectBus.Kafka/Producer/IProducerService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Confluent.Kafka; - -namespace JiShe.CollectBus.Kafka.Producer -{ - public interface IProducerService - { - Task ProduceAsync(string topic, TKey key, TValue value) where TKey : notnull where TValue : class; - - Task ProduceAsync(string topic, TValue value) where TValue : class; - - Task ProduceAsync(string topic, TKey key, TValue value, int? partition, Action>? deliveryHandler = null) where TKey : notnull where TValue : class; - - Task ProduceAsync(string topic, TValue value, int? partition = null, Action>? deliveryHandler = null) where TValue : class; - } -} diff --git a/modules/JiShe.CollectBus.Kafka/Producer/ProducerService.cs b/modules/JiShe.CollectBus.Kafka/Producer/ProducerService.cs deleted file mode 100644 index 72e9096..0000000 --- a/modules/JiShe.CollectBus.Kafka/Producer/ProducerService.cs +++ /dev/null @@ -1,265 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Confluent.Kafka; -using JiShe.CollectBus.Common; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.Kafka.Consumer; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Kafka.Serialization; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; -using YamlDotNet.Serialization; - -namespace JiShe.CollectBus.Kafka.Producer -{ - public class ProducerService: IProducerService, IDisposable - { - private readonly ILogger _logger; - private readonly IConfiguration _configuration; - private readonly ConcurrentDictionary _producerCache = new(); - private class KafkaProducer where TKey : notnull where TValue : class { } - private readonly KafkaOptionConfig _kafkaOptionConfig; - private readonly ServerApplicationOptions _applicationOptions; - /// - /// ProducerService - /// - /// - /// - /// - public ProducerService(IConfiguration configuration,ILogger logger, IOptions kafkaOptionConfig, IOptions applicationOptions) - { - _configuration = configuration; - _logger = logger; - _kafkaOptionConfig = kafkaOptionConfig.Value; - _applicationOptions = applicationOptions.Value; - } - - #region private 私有方法 - /// - /// 创建生产者实例 - /// - /// - /// - /// - private IProducer GetProducer(Type typeKey) - { - return (IProducer)_producerCache.GetOrAdd(typeKey, _ => - { - var config = BuildProducerConfig(); - return new ProducerBuilder(config) - .SetValueSerializer(new JsonSerializer()) // Value 使用自定义 JSON 序列化 - .SetLogHandler((_, msg) => _logger.Log(ConvertLogLevel(msg.Level), msg.Message)) - .Build(); - }); - } - - /// - /// 配置 - /// - /// - private ProducerConfig BuildProducerConfig() - { - var config = new ProducerConfig - { - BootstrapServers = _kafkaOptionConfig.BootstrapServers, - //AllowAutoCreateTopics = true, - QueueBufferingMaxKbytes = 4_194_304, // 4_194_304 2_097_151 // 修改缓冲区最大为2GB,默认为1GB - QueueBufferingMaxMessages = int.MaxValue, // 缓冲区消息条 - CompressionType = CompressionType.Lz4, // 配置使用压缩算法LZ4,其他:gzip/snappy/zstd - BatchSize = 32_768, // 修改批次大小为32K - LingerMs = 20, // 修改等待时间为20ms,默认为5ms - Acks = Acks.All, // 表明只有所有副本Broker都收到消息才算提交成功, 可以 Acks.Leader - MessageSendMaxRetries = 50, // 消息发送失败最大重试50次 - MessageTimeoutMs = 120000, // 消息发送超时时间为2分钟,设置值MessageTimeoutMs > LingerMs - }; - - if (_kafkaOptionConfig.EnableAuthorization) - { - config.SecurityProtocol = _kafkaOptionConfig.SecurityProtocol; - config.SaslMechanism = _kafkaOptionConfig.SaslMechanism; - config.SaslUsername = _kafkaOptionConfig.SaslUserName; - config.SaslPassword = _kafkaOptionConfig.SaslPassword; - } - - return config; - } - - private static LogLevel ConvertLogLevel(SyslogLevel level) => level switch - { - SyslogLevel.Emergency => LogLevel.Critical, - SyslogLevel.Alert => LogLevel.Critical, - SyslogLevel.Critical => LogLevel.Critical, - SyslogLevel.Error => LogLevel.Error, - SyslogLevel.Warning => LogLevel.Warning, - SyslogLevel.Notice => LogLevel.Information, - SyslogLevel.Info => LogLevel.Information, - SyslogLevel.Debug => LogLevel.Debug, - _ => LogLevel.None - }; - - #endregion - - /// - /// 发布消息 - /// - /// - /// - /// - /// - /// - /// - public async Task ProduceAsync(string topic, TKey key, TValue value)where TKey : notnull where TValue : class - { - try - { - var typeKey = typeof(KafkaProducer); - var producer = GetProducer(typeKey); - var message = new Message - { - Key = key, - Value = value, - Headers = new Headers{ - { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } - } - }; - await producer.ProduceAsync(topic, message); - } - catch (Exception ex) - { - - throw; - } - } - - /// - /// 发布消息 - /// - /// - /// - /// - /// - public async Task ProduceAsync(string topic, TValue value) where TValue : class - { - try - { - var typeKey = typeof(KafkaProducer); - var producer = GetProducer(typeKey); - var message = new Message - { - Value = value, - Headers = new Headers{ - { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } - } - }; - await producer.ProduceAsync(topic, message); - } - catch (Exception ex) - { - - throw; - } - } - - /// - /// 发布消息 - /// - /// - /// - /// - /// - /// - /// - /// - /// - public async Task ProduceAsync(string topic,TKey key,TValue value,int? partition=null, Action>? deliveryHandler = null)where TKey : notnull where TValue : class - { - try - { - var message = new Message - { - Key = key, - Value = value, - Headers = new Headers{ - { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } - } - }; - var typeKey = typeof(KafkaProducer); - var producer = GetProducer(typeKey); - if (partition.HasValue) - { - var topicPartition = new TopicPartition(topic, new Partition(partition.Value)); - producer.Produce(topicPartition, message, deliveryHandler); - } - else - { - producer.Produce(topic, message, deliveryHandler); - } - await Task.CompletedTask; - } - catch (Exception ex) - { - - throw; - } - - } - - /// - /// 发布消息 - /// - /// - /// - /// - /// - /// - /// - /// - public async Task ProduceAsync(string topic, TValue value, int? partition=null, Action>? deliveryHandler = null) where TValue : class - { - try - { - var message = new Message - { - Value = value, - Headers = new Headers{ - { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } - } - }; - var typeKey = typeof(KafkaProducer); - var producer = GetProducer(typeKey); - if (partition.HasValue) - { - var topicPartition = new TopicPartition(topic, new Partition(partition.Value)); - //_logger.LogError($"push消息:{topic}-{partition.Value}"); - producer.Produce(topicPartition, message, deliveryHandler); - } - else - { - producer.Produce(topic, message, deliveryHandler); - } - await Task.CompletedTask; - } - catch (Exception ex) - { - - throw; - } - } - - public void Dispose() - { - foreach (var producer in _producerCache.Values.OfType()) - { - producer.Dispose(); - } - _producerCache.Clear(); - } - } -} diff --git a/modules/JiShe.CollectBus.Kafka/Serialization/JsonSerializer.cs b/modules/JiShe.CollectBus.Kafka/Serialization/JsonSerializer.cs deleted file mode 100644 index 98fda49..0000000 --- a/modules/JiShe.CollectBus.Kafka/Serialization/JsonSerializer.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Text.Json; -using Confluent.Kafka; -using System.Text.Json.Serialization; -using System.Text.Encodings.Web; - -namespace JiShe.CollectBus.Kafka.Serialization -{ - /// - /// JSON 序列化器(支持泛型) - /// - public class JsonSerializer : ISerializer, IDeserializer - { - private static readonly JsonSerializerOptions _options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.Never, - WriteIndented = false,// 设置格式化输出 - IncludeFields = true,// 允许反序列化到非公共 setter 和字段 - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,// 允许特殊字符 - IgnoreReadOnlyFields = true, - IgnoreReadOnlyProperties = true, - NumberHandling = JsonNumberHandling.AllowReadingFromString, // 允许数字字符串 - AllowTrailingCommas = true, // 忽略尾随逗号 - ReadCommentHandling = JsonCommentHandling.Skip, // 忽略注释 - PropertyNameCaseInsensitive = true, // 属性名称大小写不敏感 - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 属性名称使用驼峰命名规则 - Converters = { new DateTimeJsonConverter() } // 注册你的自定义转换器, - }; - - public byte[] Serialize(T data, SerializationContext context) - { - if (data == null) - return null; - - try - { - return JsonSerializer.SerializeToUtf8Bytes(data, _options); - } - catch (Exception ex) - { - throw new InvalidOperationException("Kafka序列化失败", ex); - } - } - - public T Deserialize(ReadOnlySpan data, bool isNull, SerializationContext context) - { - if (isNull) - return default; - try - { - if (data.IsEmpty) - return default; - return JsonSerializer.Deserialize(data, _options)!; - } - catch (Exception ex) - { - throw new InvalidOperationException("Kafka反序列化失败", ex); - } - } - } - - - public class DateTimeJsonConverter : JsonConverter - { - private readonly string _dateFormatString; - public DateTimeJsonConverter() - { - _dateFormatString = "yyyy-MM-dd HH:mm:ss"; - } - - public DateTimeJsonConverter(string dateFormatString) - { - _dateFormatString = dateFormatString; - } - - public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return DateTime.Parse(reader.GetString()); - } - - public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString(_dateFormatString)); - } - } - - - public static class KafkaSerialization - { - - /// - /// 判断是否是json类型 - /// - /// - /// - public static bool IsJsonType(this object jsonObject) - { - return jsonObject is JsonElement; - } - public static object? Deserialize(object value, Type valueType) - { - try - { - var _jsonSerializerOptions = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.Never, - WriteIndented = false,// 设置格式化输出 - IncludeFields = true,// 允许反序列化到非公共 setter 和字段 - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,// 允许特殊字符 - IgnoreReadOnlyFields = true, - IgnoreReadOnlyProperties = true, - NumberHandling = JsonNumberHandling.AllowReadingFromString, // 允许数字字符串 - AllowTrailingCommas = true, // 忽略尾随逗号 - ReadCommentHandling = JsonCommentHandling.Skip, // 忽略注释 - PropertyNameCaseInsensitive = true, // 属性名称大小写不敏感 - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 属性名称使用驼峰命名规则 - Converters = { new DateTimeJsonConverter() } // 注册你的自定义转换器, - }; - - if (value is JsonElement jsonElement) - { - //return jsonElement.Deserialize(valueType, _jsonSerializerOptions); - return JsonSerializer.Deserialize(jsonElement, valueType, _jsonSerializerOptions); - } - - return null; - } - catch (Exception ex) - { - - throw; - } - } - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/FodyWeavers.xml b/modules/JiShe.CollectBus.MongoDB/FodyWeavers.xml deleted file mode 100644 index 1715698..0000000 --- a/modules/JiShe.CollectBus.MongoDB/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/modules/JiShe.CollectBus.MongoDB/JiShe.CollectBus.MongoDB.abppkg b/modules/JiShe.CollectBus.MongoDB/JiShe.CollectBus.MongoDB.abppkg deleted file mode 100644 index 8b23fd1..0000000 --- a/modules/JiShe.CollectBus.MongoDB/JiShe.CollectBus.MongoDB.abppkg +++ /dev/null @@ -1,3 +0,0 @@ -{ - "role": "lib.mongodb" -} \ No newline at end of file diff --git a/modules/JiShe.CollectBus.MongoDB/JiShe.CollectBus.MongoDB.csproj b/modules/JiShe.CollectBus.MongoDB/JiShe.CollectBus.MongoDB.csproj deleted file mode 100644 index 2987d33..0000000 --- a/modules/JiShe.CollectBus.MongoDB/JiShe.CollectBus.MongoDB.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - net8.0 - enable - JiShe.CollectBus - - - - - - - - - - - - - - - diff --git a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusDbSchemaMigrator.cs b/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusDbSchemaMigrator.cs deleted file mode 100644 index 28dbd19..0000000 --- a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusDbSchemaMigrator.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using JiShe.CollectBus.Data; -using Microsoft.Extensions.DependencyInjection; -using MongoDB.Driver; -using Volo.Abp.Data; -using Volo.Abp.DependencyInjection; -using Volo.Abp.MongoDB; -using Volo.Abp.MultiTenancy; - -namespace JiShe.CollectBus.MongoDB -{ - public class CollectBusDbSchemaMigrator : ICollectBusDbSchemaMigrator, ITransientDependency - { - private readonly IServiceProvider _serviceProvider; - - public CollectBusDbSchemaMigrator(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public async Task MigrateAsync() - { - var dbContexts = _serviceProvider.GetServices(); - var connectionStringResolver = _serviceProvider.GetRequiredService(); - - if (_serviceProvider.GetRequiredService().IsAvailable) - { - dbContexts = dbContexts.Where(x => !x.GetType().IsDefined(typeof(IgnoreMultiTenancyAttribute))); - } - - foreach (var dbContext in dbContexts) - { - var connectionString = - await connectionStringResolver.ResolveAsync( - ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType())); - var mongoUrl = new MongoUrl(connectionString); - var databaseName = mongoUrl.DatabaseName; - var client = new MongoClient(mongoUrl); - - if (databaseName.IsNullOrWhiteSpace()) - { - databaseName = ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType()); - } - - (dbContext as AbpMongoDbContext)?.InitializeCollections(client.GetDatabase(databaseName)); - } - } - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContext.cs b/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContext.cs deleted file mode 100644 index c0ddd77..0000000 --- a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContext.cs +++ /dev/null @@ -1,94 +0,0 @@ -using JiShe.CollectBus.IotSystems.Devices; -using JiShe.CollectBus.IotSystems.MessageReceiveds; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.IotSystems.Protocols; -using JiShe.CollectBus.ShardingStrategy; -using MongoDB.Bson; -using MongoDB.Bson.Serialization; -using MongoDB.Driver; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using JiShe.CollectBus.IotSystems.MessageIssueds; -using Volo.Abp.Data; -using Volo.Abp.MongoDB; -using Volo.Abp.MultiTenancy; -using JiShe.CollectBus.IotSystems.LogRecord; - -namespace JiShe.CollectBus.MongoDB; - -[IgnoreMultiTenancy] -[ConnectionStringName(CollectBusDbProperties.MongoDbConnectionStringName)] -public class CollectBusMongoDbContext : AbpMongoDbContext, ICollectBusMongoDbContext -{ - /* Add mongo collections here. Example: - * public IMongoCollection Questions => Collection(); - */ - - public IMongoCollection MessageReceiveds => Collection(); - public IMongoCollection MessageReceivedLogins => Collection(); - public IMongoCollection MessageReceivedHeartbeats => Collection(); - public IMongoCollection Devices => Collection(); - public IMongoCollection ProtocolInfos => Collection(); - - public IMongoCollection MessageIssueds => Collection(); - - - protected override void CreateModel(IMongoModelBuilder modelBuilder) - { - //modelBuilder.Entity(builder => - //{ - // builder.CreateCollectionOptions.Collation = new Collation(locale: "en_US", strength: CollationStrength.Secondary); - // builder.ConfigureIndexes(indexes => - // { - // indexes.CreateOne( - // new CreateIndexModel( - // Builders.IndexKeys.Ascending("MyProperty"), - // new CreateIndexOptions { Unique = true } - // ) - // ); - // } - // ); - - // //// 创建索引 - // //builder.ConfigureIndexes(index => - // //{ - - - // // //List> createIndexModels = new List>(); - // // //createIndexModels.Add(new CreateIndexModel( - // // // Builders.IndexKeys.Ascending(nameof(MeterReadingRecords)), - // // // new CreateIndexOptions - // // // { - // // // Unique = true - // // // } - // // // )); - - - // // //var indexKeys = Builders.IndexKeys - // // //.Ascending("CreationTime") - // // //.Ascending("OrderNumber"); - - // // //var indexOptions = new CreateIndexOptions - // // //{ - // // // Background = true, - // // // Name = "IX_CreationTime_OrderNumber" - // // //}; - // // //index.CreateOne( - // // //new CreateIndexModel(indexKeys, indexOptions)); - - // // //index.CreateOne(new CreateIndexModel( - // // // Builders.IndexKeys.Ascending(nameof(MeterReadingRecords)), - // // // new CreateIndexOptions - // // // { - // // // Unique = true - // // // } - // // // )); - // //}); - - //}); - - base.CreateModel(modelBuilder); - modelBuilder.ConfigureCollectBus(); - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContextExtensions.cs b/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContextExtensions.cs deleted file mode 100644 index 891bfb5..0000000 --- a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContextExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Volo.Abp; -using Volo.Abp.MongoDB; - -namespace JiShe.CollectBus.MongoDB; - -public static class CollectBusMongoDbContextExtensions -{ - public static void ConfigureCollectBus( - this IMongoModelBuilder builder) - { - Check.NotNull(builder, nameof(builder)); - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbModule.cs b/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbModule.cs deleted file mode 100644 index 89fd079..0000000 --- a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbModule.cs +++ /dev/null @@ -1,54 +0,0 @@ -using JiShe.CollectBus.IotSystems.LogRecord; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.Repository; -using JiShe.CollectBus.Repository.LogRecord; -using JiShe.CollectBus.Repository.MeterReadingRecord; -using JiShe.CollectBus.ShardingStrategy; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using System; -using Volo.Abp; -using Volo.Abp.AuditLogging.MongoDB; -using Volo.Abp.BackgroundJobs.MongoDB; -using Volo.Abp.Domain.Repositories; -using Volo.Abp.Domain.Repositories.MongoDB; -using Volo.Abp.Modularity; -using Volo.Abp.MongoDB; -using Volo.Abp.Uow; - -namespace JiShe.CollectBus.MongoDB; - -[DependsOn( - typeof(CollectBusDomainModule), - typeof(AbpMongoDbModule), - typeof(AbpBackgroundJobsMongoDbModule), - typeof(AbpAuditLoggingMongoDbModule) - )] -public class CollectBusMongoDbModule : AbpModule -{ - public override void ConfigureServices(ServiceConfigurationContext context) - { - context.Services.AddMongoDbContext(options => - { - options.AddDefaultRepositories(includeAllEntities: true); - - // 注册分表策略 - context.Services.AddTransient( - typeof(IShardingStrategy<>), - typeof(DayShardingStrategy<>)); - - - context.Services.AddTransient(typeof(HourShardingStrategy<>)); - - //// 分表策略仓储 替换默认仓储 - //options.AddRepository(); - - options.AddRepository(); - }); - context.Services.AddAlwaysDisableUnitOfWorkTransaction(); - Configure(options => - { - options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; - }); - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/MongoDB/ICollectBusMongoDbContext.cs b/modules/JiShe.CollectBus.MongoDB/MongoDB/ICollectBusMongoDbContext.cs deleted file mode 100644 index 2b554be..0000000 --- a/modules/JiShe.CollectBus.MongoDB/MongoDB/ICollectBusMongoDbContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Volo.Abp.Data; -using Volo.Abp.MongoDB; - -namespace JiShe.CollectBus.MongoDB; - -[ConnectionStringName(CollectBusDbProperties.MongoDbConnectionStringName)] -public interface ICollectBusMongoDbContext : IAbpMongoDbContext -{ - /* Define mongo collections here. Example: - * IMongoCollection Questions { get; } - */ -} diff --git a/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/ILogRecordRepository.cs b/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/ILogRecordRepository.cs deleted file mode 100644 index 8d1c203..0000000 --- a/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/ILogRecordRepository.cs +++ /dev/null @@ -1,57 +0,0 @@ -using JiShe.CollectBus.IotSystems.LogRecord; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using MongoDB.Driver; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Volo.Abp.Domain.Repositories; - -namespace JiShe.CollectBus.Repository.LogRecord -{ - public interface ILogRecordRepository : IRepository - { - /// - /// 批量插入 - /// - /// - /// - /// - Task InsertManyAsync(List entities, - DateTime? dateTime); - - /// - /// 单个插入 - /// - /// - /// - /// - Task InsertAsync(LogRecords entity, DateTime? dateTime); - - /// - /// 单条更新 - /// - /// 过滤条件,示例:Builders.Filter.Eq(x => x.Id, filter.Id) - /// 包含待更新的内容,示例:Builders.Update.Set(x => x.SendHexMessage, SendHexMessage).Set(x => x.MessageId, MessageId) - /// 数据实体,用于获取对应的分片库 - /// - Task UpdateOneAsync(FilterDefinition filter, UpdateDefinition update, LogRecords entity); - - /// - /// 单个获取 - /// - /// - /// - /// - Task FirOrDefaultAsync(LogRecords entity, DateTime dateTime); - - /// - /// 多集合数据查询 - /// - /// - /// - /// - Task> ParallelQueryAsync(DateTime startTime, DateTime endTime); - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/LogRecordRepository.cs b/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/LogRecordRepository.cs deleted file mode 100644 index 415ad06..0000000 --- a/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/LogRecordRepository.cs +++ /dev/null @@ -1,166 +0,0 @@ -using JiShe.CollectBus.IotSystems.LogRecord; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.MongoDB; -using JiShe.CollectBus.Repository.MeterReadingRecord; -using JiShe.CollectBus.ShardingStrategy; -using MongoDB.Driver; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Domain.Repositories.MongoDB; -using Volo.Abp.MongoDB; - -namespace JiShe.CollectBus.Repository.LogRecord -{ - public class LogRecordRepository : MongoDbRepository, ILogRecordRepository - { - - private readonly HourShardingStrategy _hourShardingStrategy; - private readonly IMongoDbContextProvider _dbContextProvider; - - public LogRecordRepository( - IMongoDbContextProvider dbContextProvider, - HourShardingStrategy hourShardingStrategy - ) - : base(dbContextProvider) - { - _dbContextProvider = dbContextProvider; - _hourShardingStrategy = hourShardingStrategy; - } - - /// - /// 批量插入 - /// - /// - /// - /// - public override async Task> InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default(CancellationToken)) - { - var collection = await GetShardedCollection(DateTime.Now); - await collection.InsertManyAsync(entities); - - return entities; - } - - /// - /// 批量插入 - /// - /// - /// - /// - public async Task InsertManyAsync(List entities, DateTime? dateTime) - { - var collection = await GetShardedCollection(dateTime); - await collection.InsertManyAsync(entities); - } - - - /// - /// 单条插入 - /// - /// - /// - /// - public override async Task InsertAsync(LogRecords entity, bool autoSave = false, CancellationToken cancellationToken = default(CancellationToken)) - { - var collection = await GetShardedCollection(DateTime.Now); - await collection.InsertOneAsync(entity); - return entity; - } - - - /// - /// 单条插入 - /// - /// - /// - /// - public async Task InsertAsync(LogRecords entity, DateTime? dateTime) - { - var collection = await GetShardedCollection(dateTime); - await collection.InsertOneAsync(entity); - return entity; - } - - /// - /// 单条更新 - /// - /// 过滤条件,示例:Builders.Filter.Eq(x => x.Id, filter.Id) - /// 包含待更新的内容,示例:Builders.Update.Set(x => x.SendHexMessage, SendHexMessage).Set(x => x.MessageId, MessageId) - /// 数据实体,用于获取对应的分片库 - /// - public async Task UpdateOneAsync(FilterDefinition filter, UpdateDefinition update, LogRecords entity) - { - var collection = await GetShardedCollection(entity.CreationTime); - - await collection.UpdateOneAsync(filter, update); - return entity; - } - - - /// - /// 单个获取 - /// - /// - /// - /// - /// - public async Task FirOrDefaultAsync(LogRecords entity, DateTime dateTime) - { - var collection = await GetShardedCollection(dateTime); - var query = await collection.FindAsync(d => d.CreationTime == dateTime && d.AFN == entity.AFN && d.Fn == entity.Fn && d.Code == entity.Code); - return await query.FirstOrDefaultAsync(); - } - - /// - /// 多集合数据查询 - /// - /// - /// - /// - public async Task> ParallelQueryAsync(DateTime startTime, DateTime endTime) - { - var collectionNames = _hourShardingStrategy.GetQueryCollectionNames(startTime, endTime); - - var dbContext = await DbContextProvider.GetDbContextAsync(); - - var tasks = collectionNames.Select(async name => - { - var collection = dbContext.Database.GetCollection(name); - var filter = Builders.Filter.And( - Builders.Filter.Gte(x => x.CreationTime, startTime), - Builders.Filter.Lte(x => x.CreationTime, endTime) - ); - return await collection.Find(filter).ToListAsync(); - }); - - var results = await Task.WhenAll(tasks); - return results.SelectMany(r => r).ToList(); - } - - /// - /// 获得分片集合 - /// - /// - private async Task> GetShardedCollection(DateTime? dateTime) - { - var dbContext = await DbContextProvider.GetDbContextAsync(); - string collectionName = string.Empty; - - if (dateTime != null) - { - collectionName = _hourShardingStrategy.GetCollectionName(dateTime.Value); - } - else - { - collectionName = _hourShardingStrategy.GetCurrentCollectionName(); - } - - return dbContext.Database.GetCollection(collectionName); - } - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/IMeterReadingRecordRepository.cs b/modules/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/IMeterReadingRecordRepository.cs deleted file mode 100644 index 20b7809..0000000 --- a/modules/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/IMeterReadingRecordRepository.cs +++ /dev/null @@ -1,60 +0,0 @@ -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using MongoDB.Driver; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Volo.Abp.Domain.Repositories; - -namespace JiShe.CollectBus.Repository.MeterReadingRecord -{ - /// - /// 抄读仓储接口 - /// - public interface IMeterReadingRecordRepository : IRepository - { - /// - /// 批量插入 - /// - /// - /// - /// - Task InsertManyAsync(List entities, - DateTime? dateTime); - - /// - /// 单个插入 - /// - /// - /// - /// - Task InsertAsync(MeterReadingRecords entity, DateTime? dateTime); - - /// - /// 单条更新 - /// - /// 过滤条件,示例:Builders.Filter.Eq(x => x.Id, filter.Id) - /// 包含待更新的内容,示例:Builders.Update.Set(x => x.Processed, true).Set(x => x.ProcessedTime, Clock.Now) - /// 数据实体,用于获取对应的分片库 - /// - Task UpdateOneAsync(FilterDefinition filter, UpdateDefinition update, MeterReadingRecords entity); - - /// - /// 单个获取 - /// - /// - /// - /// - Task FirOrDefaultAsync(MeterReadingRecords entity, DateTime? dateTime); - - /// - /// 多集合数据查询 - /// - /// - /// - /// - Task> ParallelQueryAsync(DateTime startTime, DateTime endTime); - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs b/modules/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs deleted file mode 100644 index cb35b5c..0000000 --- a/modules/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs +++ /dev/null @@ -1,173 +0,0 @@ -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.MongoDB; -using JiShe.CollectBus.ShardingStrategy; -using MongoDB.Bson; -using MongoDB.Driver; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Volo.Abp.Domain.Entities; -using Volo.Abp.Domain.Repositories.MongoDB; -using Volo.Abp.MongoDB; -using Volo.Abp.MongoDB.DistributedEvents; -using Volo.Abp.Timing; -using static System.Net.Mime.MediaTypeNames; - -namespace JiShe.CollectBus.Repository.MeterReadingRecord -{ - /// - /// 抄读记录仓储 - /// - public class MeterReadingRecordRepository : MongoDbRepository, IMeterReadingRecordRepository - { - - private readonly IShardingStrategy _shardingStrategy; - private readonly IMongoDbContextProvider _dbContextProvider; - - public MeterReadingRecordRepository( - IMongoDbContextProvider dbContextProvider, - IShardingStrategy shardingStrategy - ) - : base(dbContextProvider) - { - _dbContextProvider = dbContextProvider; - _shardingStrategy = shardingStrategy; - } - - /// - /// 批量插入 - /// - /// - /// - /// - public override async Task> InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default(CancellationToken)) - { - var collection = await GetShardedCollection(DateTime.Now); - await collection.InsertManyAsync(entities); - - return entities; - } - - /// - /// 批量插入 - /// - /// - /// - /// - public async Task InsertManyAsync(List entities, DateTime? dateTime) - { - var collection = await GetShardedCollection(dateTime); - await collection.InsertManyAsync(entities); - } - - - /// - /// 单条插入 - /// - /// - /// - /// - public override async Task InsertAsync(MeterReadingRecords entity, bool autoSave = false, CancellationToken cancellationToken = default(CancellationToken)) - { - var collection = await GetShardedCollection(DateTime.Now); - await collection.InsertOneAsync(entity); - return entity; - } - - - /// - /// 单条插入 - /// - /// - /// - /// - public async Task InsertAsync(MeterReadingRecords entity, DateTime? dateTime) - { - var collection = await GetShardedCollection(dateTime); - await collection.InsertOneAsync(entity); - return entity; - } - - /// - /// 单条更新 - /// - /// 过滤条件,示例:Builders.Filter.Eq(x => x.Id, filter.Id) - /// 包含待更新的内容,示例:Builders.Update.Set(x => x.Processed, true).Set(x => x.ProcessedTime, Clock.Now) - /// 数据实体,用于获取对应的分片库 - /// - public async Task UpdateOneAsync(FilterDefinition filter, UpdateDefinition update, MeterReadingRecords entity) - { - var collection = await GetShardedCollection(entity.CreationTime); - - await collection.UpdateOneAsync(filter, update); - return entity; - } - - - /// - /// 单个获取 - /// - /// - /// - /// - /// - public async Task FirOrDefaultAsync(MeterReadingRecords entity, DateTime? dateTime) - { - var collection = await GetShardedCollection(dateTime); - var query = await collection.FindAsync(d => d.CreationTime == dateTime.Value && d.AFN == entity.AFN && d.Fn == entity.Fn && d.FocusAddress == entity.FocusAddress); - return await query.FirstOrDefaultAsync(); - } - - /// - /// 多集合数据查询 - /// - /// - /// - /// - public async Task> ParallelQueryAsync(DateTime startTime, DateTime endTime) - { - var collectionNames = _shardingStrategy.GetQueryCollectionNames(startTime, endTime); - var database = await GetDatabaseAsync(); - - - var tasks = collectionNames.Select(async name => - { - var collection = database.GetCollection(name); - var filter = Builders.Filter.And( - Builders.Filter.Gte(x => x.CreationTime, startTime), - Builders.Filter.Lte(x => x.CreationTime, endTime) - ); - return await collection.Find(filter).ToListAsync(); - }); - - var results = await Task.WhenAll(tasks); - return results.SelectMany(r => r).ToList(); - } - - /// - /// 获得分片集合 - /// - /// - private async Task> GetShardedCollection(DateTime? dateTime) - { - var database = await GetDatabaseAsync(); - string collectionName = string.Empty; - - if (dateTime != null) - { - collectionName = _shardingStrategy.GetCollectionName(dateTime.Value); - } - else - { - collectionName = _shardingStrategy.GetCurrentCollectionName(); - } - - return database.GetCollection(collectionName); - } - - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs deleted file mode 100644 index 0c721a5..0000000 --- a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs +++ /dev/null @@ -1,61 +0,0 @@ -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; - -namespace JiShe.CollectBus.ShardingStrategy -{ - /// - /// 按天分表策略 - /// - /// - public class DayShardingStrategy : IShardingStrategy - { - /// - /// 获取指定时间对应的集合名 - /// - /// - /// - public string GetCollectionName(DateTime dateTime) - { - var baseName = typeof(TEntity).Name; - return $"{baseName}_{dateTime.GetDataTableShardingStrategy(TableTimeStrategyEnum.DayShardingStrategy)}"; - } - - /// - /// 获取当前时间对应的集合名 - /// - /// - public string GetCurrentCollectionName() - { - var baseName = typeof(TEntity).Name; - return $"{baseName}_{DateTime.Now.GetDataTableShardingStrategy(TableTimeStrategyEnum.DayShardingStrategy)}"; - } - - /// - /// 用于查询时确定目标集合 - /// - /// - /// - /// - public IEnumerable GetQueryCollectionNames(DateTime? startTime, DateTime? endTime) - { - var months = new List(); - var current = startTime ?? DateTime.MinValue; - var end = endTime ?? DateTime.MaxValue; - var baseName = typeof(TEntity).Name; - - while (current <= end) - { - months.Add($"{baseName}_{current.GetDataTableShardingStrategy(TableTimeStrategyEnum.DayShardingStrategy)}"); - current = current.AddMonths(1); - } - - return months.Distinct(); - } - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/HourShardingStrategy.cs b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/HourShardingStrategy.cs deleted file mode 100644 index 20f65de..0000000 --- a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/HourShardingStrategy.cs +++ /dev/null @@ -1,58 +0,0 @@ -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace JiShe.CollectBus.ShardingStrategy -{ - /// - /// 按小时分表 - /// - /// - public class HourShardingStrategy - { - /// - /// 获取指定时间对应的集合名 - /// - /// - /// - public string GetCollectionName(DateTime dateTime) - { - var baseName = typeof(TEntity).Name; - return $"{baseName}_{dateTime.GetDataTableShardingStrategy(TableTimeStrategyEnum.HourShardingStrategy)}"; - } - - /// - /// 获取当前时间对应的集合名 - /// - /// - public string GetCurrentCollectionName() - { - var baseName = typeof(TEntity).Name; - return $"{baseName}_{DateTime.Now.GetDataTableShardingStrategy(TableTimeStrategyEnum.HourShardingStrategy)}"; - } - - /// - /// 用于查询时确定目标集合 - /// - /// - /// - /// - public IEnumerable GetQueryCollectionNames(DateTime? startTime, DateTime? endTime) - { - var list = new List(); - var current = startTime ?? DateTime.MinValue; - var end = endTime ?? DateTime.MaxValue; - var baseName = typeof(TEntity).Name; - - while (current <= end) - { - list.Add($"{baseName}_{current.GetDataTableShardingStrategy(TableTimeStrategyEnum.HourShardingStrategy)}"); - current = current.AddHours(1); - } - - return list.Distinct(); - } - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IHourShardingStrategy.cs b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IHourShardingStrategy.cs deleted file mode 100644 index ad0654c..0000000 --- a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IHourShardingStrategy.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.ShardingStrategy -{ - public interface IHourShardingStrategy : IShardingStrategy - { - } -} diff --git a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IShardingStrategy.cs b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IShardingStrategy.cs deleted file mode 100644 index 151d5df..0000000 --- a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IShardingStrategy.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.ShardingStrategy -{ - /// - /// 数据存储分片策略 - /// - /// - public interface IShardingStrategy - { - /// - /// 获取指定时间对应的集合名 - /// - /// - string GetCollectionName(DateTime dateTime); - - /// - /// 获取当前时间对应的集合名 - /// - /// - string GetCurrentCollectionName(); - - /// - /// 用于查询时确定目标集合 - /// - /// - /// - /// - IEnumerable GetQueryCollectionNames(DateTime? startTime = null, - DateTime? endTime = null); - } -} diff --git a/protocols/JiShe.CollectBus.Protocol.Contracts/ProtocolPools/IPluginContainer.cs b/protocols/JiShe.CollectBus.Protocol.Contracts/ProtocolPools/IPluginContainer.cs deleted file mode 100644 index c34a1eb..0000000 --- a/protocols/JiShe.CollectBus.Protocol.Contracts/ProtocolPools/IPluginContainer.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace JiShe.CollectBus.Protocol.Contracts.ProtocolPools -{ - public interface IPluginContainer - { - } -} diff --git a/protocols/JiShe.CollectBus.Protocol.Contracts/ProtocolPools/PluginContainer.cs b/protocols/JiShe.CollectBus.Protocol.Contracts/ProtocolPools/PluginContainer.cs deleted file mode 100644 index 116eccc..0000000 --- a/protocols/JiShe.CollectBus.Protocol.Contracts/ProtocolPools/PluginContainer.cs +++ /dev/null @@ -1,26 +0,0 @@ -using JiShe.CollectBus.Interfaces; - -namespace JiShe.CollectBus.Protocol.Contracts.ProtocolPools -{ - public class PluginContainer: IPluginContainer - { - public Dictionary ProtocolPools; - - public PluginContainer() - { - - - } - - - } - - public static class ServiceProviderKeyedServiceExtensions - { - //public static Task AddKeyedSingleton(this IServiceProvider provider, string key) - //{ - // //var aa = Activator.CreateInstance(); - - //} - } -} diff --git a/protocols/JiShe.CollectBus.Protocol.T1882018/GlobalUsings.cs b/protocols/JiShe.CollectBus.Protocol.T1882018/GlobalUsings.cs new file mode 100644 index 0000000..d1b32ae --- /dev/null +++ b/protocols/JiShe.CollectBus.Protocol.T1882018/GlobalUsings.cs @@ -0,0 +1,9 @@ +// Global using directives + +global using System.Text; +global using Volo.Abp.Modularity; +global using JiShe.ServicePro.Enums; +global using JiShe.ServicePro.FreeRedisProvider; +global using JiShe.ServicePro.Consts; +global using JiShe.ServicePro.Core; +global using JiShe.ServicePro.ServerOptions; \ No newline at end of file diff --git a/protocols/JiShe.CollectBus.Protocol.T1882018/JiShe.CollectBus.Protocol.T1882018.csproj b/protocols/JiShe.CollectBus.Protocol.T1882018/JiShe.CollectBus.Protocol.T1882018.csproj index 4866985..400c849 100644 --- a/protocols/JiShe.CollectBus.Protocol.T1882018/JiShe.CollectBus.Protocol.T1882018.csproj +++ b/protocols/JiShe.CollectBus.Protocol.T1882018/JiShe.CollectBus.Protocol.T1882018.csproj @@ -1,14 +1,16 @@  - net8.0 + net9.0 enable enable - - + + + + diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F1_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F1_Analysis.cs index 6fd96e1..81f187e 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F1_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F1_Analysis.cs @@ -8,6 +8,8 @@ using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto; using JiShe.CollectBus.Protocol.Dto; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol3761; +using JiShe.ServicePro.Enums; +using JiShe.ServicePro.FreeRedisProvider; using Microsoft.Extensions.Logging; namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_00H @@ -39,10 +41,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_00H ItemType= $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询电表信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F2_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F2_Analysis.cs index 2e465b8..8a76b57 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F2_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F2_Analysis.cs @@ -36,10 +36,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_00H ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询电表信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F1_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F1_Analysis.cs index 686d027..c2cfaba 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F1_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F1_Analysis.cs @@ -38,10 +38,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_02H ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F2_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F2_Analysis.cs index 6428063..2f26ae1 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F2_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F2_Analysis.cs @@ -38,10 +38,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_02H ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F3_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F3_Analysis.cs index 1c3027b..b157325 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F3_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F3_Analysis.cs @@ -38,10 +38,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_02H ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F1_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F1_Analysis.cs index 5011b4e..dc99c5c 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F1_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F1_Analysis.cs @@ -8,7 +8,7 @@ using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto; using JiShe.CollectBus.Protocol.Dto; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol.T37612012.AnalysisData; -using JiShe.CollectBus.Protocol3761; +using JiShe.CollectBus.Protocol3761; using Microsoft.Extensions.Logging; namespace JiShe.CollectBus.Protocol.AnalysisData.AFN_09H @@ -45,10 +45,10 @@ namespace JiShe.CollectBus.Protocol.AnalysisData.AFN_09H ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F9_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F9_Analysis.cs index 7547662..fa8c753 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F9_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F9_Analysis.cs @@ -39,10 +39,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_09H ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F10_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F10_Analysis.cs index 7e36e11..ad00f2c 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F10_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F10_Analysis.cs @@ -44,10 +44,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0AH ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F66_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F66_Analysis.cs index 5305476..7dbaa7a 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F66_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F66_Analysis.cs @@ -43,10 +43,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0AH ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F68_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F68_Analysis.cs index 48aa519..e096978 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F68_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0AH/AFN10_F68_Analysis.cs @@ -38,10 +38,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0AH ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F129_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F129_Analysis.cs index 35c258f..229c735 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F129_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F129_Analysis.cs @@ -1,17 +1,12 @@ -using Apache.IoTDB; -using JiShe.CollectBus.Common.Consts; +using JiShe.CollectBus.Common.Consts; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IotSystems.Ammeters; -using JiShe.CollectBus.IotSystems.Devices; using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto; using JiShe.CollectBus.Protocol.Dto; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol.T37612012.Appendix; using JiShe.CollectBus.Protocol3761; using Microsoft.Extensions.Logging; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0CH { @@ -58,12 +53,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0CH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code,input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code,input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F130_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F130_Analysis.cs index 3f28d97..5ae6d70 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F130_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F130_Analysis.cs @@ -41,12 +41,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0CH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F131_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F131_Analysis.cs index 887e982..c9d9cbb 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F131_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F131_Analysis.cs @@ -42,12 +42,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0CH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F132_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F132_Analysis.cs index 55ab674..9cdb741 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F132_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F132_Analysis.cs @@ -8,7 +8,7 @@ using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto; using JiShe.CollectBus.Protocol.Dto; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol.T37612012.Appendix; -using JiShe.CollectBus.Protocol3761; +using JiShe.CollectBus.Protocol3761; using Microsoft.Extensions.Logging; using YamlDotNet.Core.Tokens; @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0CH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F145_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F145_Analysis.cs index 13541e3..9cf2c32 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F145_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F145_Analysis.cs @@ -44,10 +44,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0CH AnalysisBaseDto data = GenerateFinalResult(datas, "当月正向有功最大需量及发生时间", itemType); // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F149_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F149_Analysis.cs index 1a32132..b261ac6 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F149_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F149_Analysis.cs @@ -44,10 +44,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AFN_0CH AnalysisBaseDto data = GenerateFinalResult(datas, itemType,"上月(上一结算日)正向有功最大需量及发生时间"); // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F188_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F188_Analysis.cs index a95bd09..1c624d1 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F188_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F188_Analysis.cs @@ -44,10 +44,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AFN_0CH string itemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}"; AnalysisBaseDto data = GenerateFinalResult(input.UnitData.HexMessageList, itemType); // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F25_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F25_Analysis.cs index e6e6096..85fdf43 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F25_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F25_Analysis.cs @@ -71,12 +71,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AFN_0CH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F2_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F2_Analysis.cs index a54db8c..48d798c 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F2_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F2_Analysis.cs @@ -43,10 +43,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AFN_0CH var data = await GenerateFinalResultAsync(input.UnitData.HexMessageList, itemType); // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F33_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F33_Analysis.cs index fd7dd30..861c256 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F33_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F33_Analysis.cs @@ -49,10 +49,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AFN_0CH ItemType= $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F49_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F49_Analysis.cs index 257a666..7d6c448 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F49_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0CH/AFN12_F49_Analysis.cs @@ -66,12 +66,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AFN_0CH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F100_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F100_Analysis.cs index 8fbad80..128e522 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F100_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F100_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F101_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F101_Analysis.cs index 10f52b2..9bfb4fe 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F101_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F101_Analysis.cs @@ -45,12 +45,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F102_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F102_Analysis.cs index e40c2e8..5e38127 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F102_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F102_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F103_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F103_Analysis.cs index ed7d4c1..8a2333f 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F103_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F103_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F104_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F104_Analysis.cs index 6b0585e..8d45257 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F104_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F104_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F105_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F105_Analysis.cs index 78eadd8..a34572c 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F105_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F105_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F106_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F106_Analysis.cs index ff5e8e6..24d7589 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F106_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F106_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F107_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F107_Analysis.cs index 6f26aa9..996ba77 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F107_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F107_Analysis.cs @@ -45,12 +45,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F108_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F108_Analysis.cs index ced6c30..3d423d9 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F108_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F108_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F11_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F11_Analysis.cs index becaf19..214d9e5 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F11_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F11_Analysis.cs @@ -45,12 +45,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F145_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F145_Analysis.cs index 7ffa444..07d99b2 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F145_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F145_Analysis.cs @@ -45,12 +45,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F146_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F146_Analysis.cs index e5d011f..a41f93d 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F146_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F146_Analysis.cs @@ -45,12 +45,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F147_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F147_Analysis.cs index 4ec9e9f..9aad2fe 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F147_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F147_Analysis.cs @@ -45,12 +45,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F148_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F148_Analysis.cs index d10dcce..4310d04 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F148_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F148_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F161_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F161_Analysis.cs index 382134e..3890010 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F161_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F161_Analysis.cs @@ -45,12 +45,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F162_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F162_Analysis.cs index 90991c9..01a3cac 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F162_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F162_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F163_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F163_Analysis.cs index e2bb0b0..10be34e 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F163_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F163_Analysis.cs @@ -45,12 +45,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F164_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F164_Analysis.cs index c636e4c..b9abaf5 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F164_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F164_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F165_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F165_Analysis.cs index d69b3a1..dedd9a7 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F165_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F165_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F166_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F166_Analysis.cs index b02c99b..1d58fcf 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F166_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F166_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F167_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F167_Analysis.cs index fb99e97..cabe4ea 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F167_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F167_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F168_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F168_Analysis.cs index 4b7d39a..bc30e29 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F168_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F168_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F177_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F177_Analysis.cs index e147c17..bb7c1e6 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F177_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F177_Analysis.cs @@ -43,12 +43,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F178_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F178_Analysis.cs index d60bd89..d7807df 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F178_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F178_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F179_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F179_Analysis.cs index b685ec3..9a670dd 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F179_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F179_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F180_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F180_Analysis.cs index 9c42518..9852016 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F180_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F180_Analysis.cs @@ -43,12 +43,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F181_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F181_Analysis.cs index 24c4195..d89f147 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F181_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F181_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F182_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F182_Analysis.cs index 820dfc1..54510e5 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F182_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F182_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F183_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F183_Analysis.cs index 45307ce..29214c4 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F183_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F183_Analysis.cs @@ -43,12 +43,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F184_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F184_Analysis.cs index 7714ed2..cc0f2f2 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F184_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F184_Analysis.cs @@ -44,12 +44,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F189_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F189_Analysis.cs index c826ffb..687167d 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F189_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F189_Analysis.cs @@ -45,12 +45,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F190_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F190_Analysis.cs index d6bf837..54adf41 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F190_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F190_Analysis.cs @@ -42,10 +42,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH AnalysisBaseDto data = GenerateFinalResult(datas, "抄表日冻结正向无功最大需量及发生时间", dataType); // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F193_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F193_Analysis.cs index f6d698e..3180e03 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F193_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F193_Analysis.cs @@ -43,10 +43,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH AnalysisBaseDto data = GenerateFinalResult(datas, "月冻结正向有功最大需量及发生时间", dataType); // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F195_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F195_Analysis.cs index ceda00c..043ce9c 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F195_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F195_Analysis.cs @@ -43,10 +43,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH AnalysisBaseDto data = GenerateFinalResult(datas, "月冻结反向有功最大需量及发生时间", dataType); // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F19_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F19_Analysis.cs index 531b234..13dfd15 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F19_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F19_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F3_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F3_Analysis.cs index 7ed137e..bd7484b 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F3_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F3_Analysis.cs @@ -49,12 +49,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F4_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F4_Analysis.cs index c99a894..c3bfbcf 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F4_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F4_Analysis.cs @@ -48,12 +48,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F81_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F81_Analysis.cs index 4bf1e2e..f395291 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F81_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F81_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F82_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F82_Analysis.cs index 0c4aa75..0166d4d 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F82_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F82_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F83_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F83_Analysis.cs index ef77a80..eeaeb74 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F83_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F83_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F84_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F84_Analysis.cs index 66216bc..25c96a4 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F84_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F84_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F85_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F85_Analysis.cs index b9103b8..e4a9ad0 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F85_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F85_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F86_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F86_Analysis.cs index 1748a3d..c2d5d09 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F86_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F86_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F87_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F87_Analysis.cs index 26dfcd1..66a3367 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F87_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F87_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F88_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F88_Analysis.cs index 29fb978..5858719 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F88_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F88_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F89_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F89_Analysis.cs index 330cf0a..e480a9b 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F89_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F89_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F90_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F90_Analysis.cs index 3033c76..c06829d 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F90_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F90_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F91_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F91_Analysis.cs index b18d244..aa0077b 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F91_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F91_Analysis.cs @@ -46,12 +46,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F92_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F92_Analysis.cs index 1d1bff5..a556c5e 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F92_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F92_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F93_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F93_Analysis.cs index bbd8f0d..a73763e 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F93_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F93_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F94_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F94_Analysis.cs index 19e6d2f..cd7447f 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F94_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F94_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F95_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F95_Analysis.cs index 842496f..b2406af 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F95_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F95_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F97_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F97_Analysis.cs index 8ce25e1..731ee4d 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F97_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F97_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F98_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F98_Analysis.cs index 6da01d5..b7d81fb 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F98_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F98_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F99_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F99_Analysis.cs index 33b8560..64451ad 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F99_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0DH/AFN13_F99_Analysis.cs @@ -47,12 +47,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0DH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0EH/AFN14_F1_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0EH/AFN14_F1_Analysis.cs index 7dc7275..bec9102 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0EH/AFN14_F1_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_0EH/AFN14_F1_Analysis.cs @@ -49,12 +49,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_0EH if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/AFN16_F1_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/AFN16_F1_Analysis.cs index 627b1da..19c1fa5 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/AFN16_F1_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/AFN16_F1_Analysis.cs @@ -69,10 +69,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_10H ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.FocusId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.FocusAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F101_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F101_Analysis.cs index 6066e26..ebea790 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F101_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F101_Analysis.cs @@ -40,10 +40,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_10H.Ammeter ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}" }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F97_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F97_Analysis.cs index bab56d2..0da01ca 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F97_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F97_Analysis.cs @@ -1,5 +1,4 @@ -using DeviceDetectorNET.Parser.Device; -using GatherService.WattMeter.AnalysisData.AFN_10H; +using DeviceDetectorNET.Parser.Device; using JiShe.CollectBus.Common.Consts; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.Common.Extensions; @@ -47,10 +46,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_10H.Ammeter AnalysisBaseDto data = GenerateFinalResult(datas); // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn, datas[1]); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn, datas[1]); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F98_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F98_Analysis.cs index ae0512f..36d9c66 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F98_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Ammeter/AFN16_F98_Analysis.cs @@ -47,10 +47,10 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_10H.Ammeter }; // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn); if (deviceInfo != null) { - data.ProjectId = deviceInfo.ProjectID; + data.ProjectId = deviceInfo.ProjectId; data.DeviceId = deviceInfo.MeterId; data.DatabaseBusiID = deviceInfo.DatabaseBusiID; data.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Watermeter/ES190DC_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Watermeter/ES190DC_Analysis.cs index e831c9c..4c75999 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Watermeter/ES190DC_Analysis.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_10H/Watermeter/ES190DC_Analysis.cs @@ -42,12 +42,12 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_10H.Watermeter if (list.Count > 0) { // 查询设备信息 - DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn, list[0].DeviceAddress); + DeviceCacheInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code, input.DA.Pn, list[0].DeviceAddress); if (deviceInfo != null) { list.ForEach(item => { - item.ProjectId = deviceInfo.ProjectID; + item.ProjectId = deviceInfo.ProjectId; item.DeviceId = deviceInfo.MeterId; item.DatabaseBusiID = deviceInfo.DatabaseBusiID; item.DeviceAddress = deviceInfo.MeterAddress; diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/DataStorage.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/DataStorage.cs index ea11af8..aa80d1f 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/DataStorage.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/DataStorage.cs @@ -1,36 +1,23 @@ -using Confluent.Kafka; -using FreeRedis; +using FreeRedis; using JiShe.CollectBus.Common; using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Encrypt; -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Extensions; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.FreeRedis; -using JiShe.CollectBus.IoTDB.Context; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IoTDB.Model; -using JiShe.CollectBus.IoTDB.Options; -using JiShe.CollectBus.IoTDB.Provider; -using JiShe.CollectBus.IotSystems.Ammeters; -using JiShe.CollectBus.IotSystems.Devices; -using JiShe.CollectBus.IotSystems.LogRecord; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto; using JiShe.CollectBus.Protocol.Dto; -using Microsoft.AspNetCore.Http; +using JiShe.ServicePro.Consts; +using JiShe.ServicePro.Core; +using JiShe.ServicePro.Encrypt; +using JiShe.ServicePro.IoTDBManagement.Model; +using JiShe.ServicePro.IoTDBManagement.Options; +using JiShe.ServicePro.IoTDBManagement.SessionPools; +using JiShe.ServicePro.IoTDBManagement.TableModels; +using JiShe.ServicePro.IoTDBManagement.TreeModels; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System.Diagnostics; -using System.Diagnostics.Metrics; using System.Threading.Channels; -using TouchSocket.Core; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; -using YamlDotNet.Core.Tokens; using static FreeSql.Internal.GlobalFilter; -using static IClientRPCService; using static JiShe.CollectBus.Common.Consts.T37612012PacketItemCodeConst; namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData @@ -38,13 +25,13 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData public class DataStorage:ITransientDependency { private readonly IGuidGenerator _guidGenerator; - private readonly IIoTDbProvider _dbProvider; + private readonly IoTDBSessionPoolProvider _dbProvider; private readonly ServerApplicationOptions _applicationOptions; private readonly ILogger _logger; private readonly IMemoryCache _imemoryCache; private readonly IFreeRedisProvider _freeRedisProvider; - private RedisClient Instance { get; set; } - public DataStorage(IIoTDbProvider dbProvider, IOptions applicationOptions, + private IRedisClient Instance { get; set; } + public DataStorage(IoTDBSessionPoolProvider dbProvider, IOptions applicationOptions, IGuidGenerator guidGenerator, ILogger logger, IMemoryCache memoryCache, IFreeRedisProvider freeRedisProvider) { _dbProvider= dbProvider; @@ -81,13 +68,13 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData /// /// /// - public async Task GetDeviceInfoAsync(string code,int pn=1,string meterAddress=null) + public async Task GetDeviceInfoAsync(string code,int pn=1,string meterAddress=null) { string redisCacheDeviceInfoHashKey = $"{string.Format(RedisConst.CacheDeviceInfoHashKey, _applicationOptions.SystemType, _applicationOptions.ServerTagName)}"; string deviceKey= $"{code}:{pn}"; var deviceInfo= await _imemoryCache.GetOrCreateAsync(deviceKey, async entry => { - List devices= await Instance.HGetAsync>(redisCacheDeviceInfoHashKey, code) ?? new List(); + List devices= await Instance.HGetAsync>(redisCacheDeviceInfoHashKey, code) ?? new List(); var data = devices.Where(s => s.MeteringCode == pn).FirstOrDefault(); if (data != null) entry.SetSlidingExpiration(TimeSpan.FromMinutes(5)); @@ -101,7 +88,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData string deviceAddressKey = $"{code}:{meterAddress}"; deviceInfo = await _imemoryCache.GetOrCreateAsync(deviceAddressKey, async entry => { - List devices = await Instance.HGetAsync>(redisCacheDeviceInfoHashKey, code) ?? new List(); + List devices = await Instance.HGetAsync>(redisCacheDeviceInfoHashKey, code) ?? new List(); var data = devices.Where(s => s.MeterAddress == meterAddress).FirstOrDefault(); if (data != null) entry.SetSlidingExpiration(TimeSpan.FromMinutes(5)); @@ -155,7 +142,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceId = $"{data.DeviceId}", DeviceType = $"{data.DeviceType.ToString()}", ProjectId = $"{data.ProjectId}", - DataType = analysisBaseDto.DataType, + IoTDataType = analysisBaseDto.DataType, Timestamps = data.TimeSpan!.Value.GetFormatTime(analysisBaseDto.DensityUnit, analysisBaseDto.TimeDensity).GetDateTimeOffset().ToUnixTimeNanoseconds(), SingleMeasuring = (data.FiledName ?? string.Empty, data.DataValue ?? default) }; @@ -182,7 +169,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData ProjectId = $"{data.ProjectId}", DeviceType = $"{data.DeviceType}", DeviceId = $"{data.DeviceId}", - DataType = analysisBaseDto.DataType, + IoTDataType = analysisBaseDto.DataType, FocusId = data.FocusId, FocusAddress = analysisBaseDto.Code, Timestamps = DateTime.Now.GetDateTimeOffset().ToUnixTimeNanoseconds(), @@ -220,7 +207,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceId = $"{data.DeviceId}", DeviceType = $"{data.DeviceType}", ProjectId = $"{data.ProjectId}", - DataType = analysisBaseDto.DataType, + IoTDataType = analysisBaseDto.DataType, Timestamps = meter.Timestamps, SingleMeasuring = (IotDbFieldConst.IsSync, false) }; @@ -233,7 +220,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceId = $"{data.DeviceId}", DeviceType = $"{data.DeviceType}", ProjectId = $"{data.ProjectId}", - DataType = analysisBaseDto.DataType, + IoTDataType = analysisBaseDto.DataType, Timestamps = meter.Timestamps, SingleMeasuring = (IotDbFieldConst.DatabaseBusiID, data.DatabaseBusiID) }; @@ -293,7 +280,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceId = $"{item.DeviceId}", DeviceType = $"{item.DeviceType}", ProjectId = $"{item.ProjectId}", - DataType = analysisBaseDto.DataType, + IoTDataType = analysisBaseDto.DataType, Timestamps = item.TimeSpan!.Value.GetFormatTime(analysisBaseDto.DensityUnit, analysisBaseDto.TimeDensity).GetDateTimeOffset().ToUnixTimeNanoseconds(), // TODO:这里暂时格式化15分钟数据,需要进行调整 SingleMeasuring =(item.FiledName ?? string.Empty, item.DataValue ?? default) }; @@ -319,7 +306,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData ProjectId = $"{item.ProjectId}", DeviceType = $"{item.DeviceType}", DeviceId = $"{item.DeviceId}", - DataType = IOTDBDataTypeConst.Log, // 匹配不到下发记录标记为LOG + IoTDataType = IOTDBDataTypeConst.Log, // 匹配不到下发记录标记为LOG Timestamps = DateTime.Now.CheckTimePoint().GetDateTimeOffset().ToUnixTimeNanoseconds(), DatabaseBusiID = item.DatabaseBusiID, PendingCopyReadTime = item.TimeSpan.Value.GetFormatTime(analysisBaseDto.DensityUnit, analysisBaseDto.TimeDensity), @@ -347,7 +334,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData } meterReadingTelemetryPacketInfos.Add(taskData); //如果无字段名,则不保存数据,如saveData=false 也不保存数据 - if (!string.IsNullOrWhiteSpace(item.FiledName) && item.ProjectId>0) + if (!string.IsNullOrWhiteSpace(item.FiledName) && !string.IsNullOrWhiteSpace(item.ProjectId)) { treeModelSingleMeasuringEntities.Add(meter); @@ -358,11 +345,11 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceId = $"{item.DeviceId}", DeviceType = $"{item.DeviceType}", ProjectId = $"{item.ProjectId}", - DataType = analysisBaseDto.DataType, + IoTDataType = analysisBaseDto.DataType, Timestamps = meter.Timestamps, SingleMeasuring = (IotDbFieldConst.IsSync, false) }; - if(!meterIsSyncs.Any(a=> a.DataType == meterIsSync.DataType && a.ProjectId == meterIsSync.ProjectId && a.SystemName== meterIsSync.SystemName && a.DeviceId== meterIsSync.DeviceId && a.Timestamps== meterIsSync.Timestamps)) + if(!meterIsSyncs.Any(a=> a.IoTDataType == meterIsSync.IoTDataType && a.ProjectId == meterIsSync.ProjectId && a.SystemName== meterIsSync.SystemName && a.DeviceId== meterIsSync.DeviceId && a.Timestamps== meterIsSync.Timestamps)) meterIsSyncs.Add(meterIsSync); // 数据库业务ID @@ -372,11 +359,11 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceId = $"{item.DeviceId}", DeviceType = $"{item.DeviceType}", ProjectId = $"{item.ProjectId}", - DataType = analysisBaseDto.DataType, + IoTDataType = analysisBaseDto.DataType, Timestamps = meter.Timestamps, SingleMeasuring = (IotDbFieldConst.DatabaseBusiID, item.DatabaseBusiID) }; - if (!meterDataBaseIDs.Any(a => a.DataType == meterIsSync.DataType && a.ProjectId == meterIsSync.ProjectId && a.SystemName == meterIsSync.SystemName && a.DeviceId == meterIsSync.DeviceId && a.Timestamps == meterIsSync.Timestamps)) + if (!meterDataBaseIDs.Any(a => a.IoTDataType == meterIsSync.IoTDataType && a.ProjectId == meterIsSync.ProjectId && a.SystemName == meterIsSync.SystemName && a.DeviceId == meterIsSync.DeviceId && a.Timestamps == meterIsSync.Timestamps)) meterDataBaseIDs.Add(meterIsDatabaseBusiID); } @@ -412,7 +399,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData if (!data.TimeSpan.HasValue) data.TimeSpan = analysisBaseDto.ReceivedTime; // 类型(心跳,登录,上电,掉电) - if (data.ProjectId > 0) + if (!string.IsNullOrWhiteSpace(data.ProjectId)) { long timestamps = DateTime.Now.GetDateTimeOffset().ToUnixTimeNanoseconds(); var treeData = new TreeModelSingleMeasuringEntity() @@ -421,7 +408,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceId = $"{data.DeviceId}", DeviceType = $"{data.DeviceType}", ProjectId = $"{data.ProjectId}", - DataType = IOTDBDataTypeConst.Status, + IoTDataType = IOTDBDataTypeConst.Status, Timestamps = timestamps, SingleMeasuring = (data.FiledName!, data.DataValue!) }; @@ -433,7 +420,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceId = $"{data.DeviceId}", DeviceType = $"{data.DeviceType}", ProjectId = $"{data.ProjectId}", - DataType = IOTDBDataTypeConst.Status, + IoTDataType = IOTDBDataTypeConst.Status, Timestamps = timestamps, SingleMeasuring = (IotDbFieldConst.FrameData, analysisBaseDto.ReceivedHexMessage ?? string.Empty) }; @@ -447,7 +434,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceType = $"{data.DeviceType}", ProjectId = $"{data.ProjectId}", Timestamps = timestamps, - DataType = IOTDBDataTypeConst.Status, + IoTDataType = IOTDBDataTypeConst.Status, SingleMeasuring = (IotDbFieldConst.RecordingTime, data.TimeSpan.HasValue ? data.TimeSpan.Value : DateTime.Now) }; await _dbProvider.GetSessionPool(false).InsertAsync(treeRecordingTimeData); @@ -464,7 +451,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData ProjectId = $"{data.ProjectId}", DeviceType = $"{data.DeviceType}", DeviceId = $"{data.DeviceId}", - DataType = analysisBaseDto.DataType, + IoTDataType = analysisBaseDto.DataType, FocusId = data.FocusId, FocusAddress = analysisBaseDto.Code, Timestamps = DateTime.Now.GetDateTimeOffset().ToUnixTimeNanoseconds(), @@ -514,7 +501,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData if (!item.TimeSpan.HasValue) item.TimeSpan = analysisBaseDto.ReceivedTime; // 类型(心跳,登录,上电,掉电) - if (item.ProjectId > 0) + if (!string.IsNullOrWhiteSpace(item.ProjectId)) { long timestamps = DateTime.Now.GetDateTimeOffset().ToUnixTimeNanoseconds(); var treeData = new TreeModelSingleMeasuringEntity() @@ -523,7 +510,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData DeviceId = $"{item.DeviceId}", DeviceType = $"{item.DeviceType}", ProjectId = $"{item.ProjectId}", - DataType = IOTDBDataTypeConst.Status, + IoTDataType = IOTDBDataTypeConst.Status, Timestamps = timestamps, SingleMeasuring = (item.FiledName!, item.DataValue!) }; @@ -534,7 +521,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData SystemName = _applicationOptions.SystemType, DeviceType = $"{item.DeviceType}", ProjectId = $"{item.ProjectId}", - DataType = IOTDBDataTypeConst.Status, + IoTDataType = IOTDBDataTypeConst.Status, Timestamps = timestamps, SingleMeasuring = (IotDbFieldConst.FrameData, analysisBaseDto.ReceivedHexMessage ?? string.Empty) }; @@ -547,7 +534,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData SystemName = _applicationOptions.SystemType, DeviceType = $"{item.DeviceType}", ProjectId = $"{item.ProjectId}", - DataType = IOTDBDataTypeConst.Status, + IoTDataType = IOTDBDataTypeConst.Status, Timestamps = timestamps, SingleMeasuring = (IotDbFieldConst.RecordingTime, item.TimeSpan.HasValue ? item.TimeSpan.Value : DateTime.Now) }; @@ -566,7 +553,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData ProjectId = $"{item.ProjectId}", DeviceType = $"{item.DeviceType}", DeviceId = $"{item.DeviceId}", - DataType = analysisBaseDto.DataType, + IoTDataType = analysisBaseDto.DataType, Timestamps = DateTime.Now.GetDateTimeOffset().ToUnixTimeNanoseconds(), DatabaseBusiID = item.DatabaseBusiID, PendingCopyReadTime = item.TimeSpan!.Value.GetFormatTime(analysisBaseDto.DensityUnit, analysisBaseDto.TimeDensity), diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/GlobalUsings.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/GlobalUsings.cs new file mode 100644 index 0000000..d1b32ae --- /dev/null +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/GlobalUsings.cs @@ -0,0 +1,9 @@ +// Global using directives + +global using System.Text; +global using Volo.Abp.Modularity; +global using JiShe.ServicePro.Enums; +global using JiShe.ServicePro.FreeRedisProvider; +global using JiShe.ServicePro.Consts; +global using JiShe.ServicePro.Core; +global using JiShe.ServicePro.ServerOptions; \ No newline at end of file diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/JiShe.CollectBus.Protocol.T37612012.csproj b/protocols/JiShe.CollectBus.Protocol.T37612012/JiShe.CollectBus.Protocol.T37612012.csproj index e83c165..ce34bdf 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/JiShe.CollectBus.Protocol.T37612012.csproj +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/JiShe.CollectBus.Protocol.T37612012.csproj @@ -1,18 +1,18 @@  - net8.0 + net9.0 enable enable - - + + + - diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/T37612012ProtocolPlugin.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/T37612012ProtocolPlugin.cs index ed82fa0..addfb17 100644 --- a/protocols/JiShe.CollectBus.Protocol.T37612012/T37612012ProtocolPlugin.cs +++ b/protocols/JiShe.CollectBus.Protocol.T37612012/T37612012ProtocolPlugin.cs @@ -1,23 +1,21 @@ using JiShe.CollectBus.Common.BuildSendDatas; -using JiShe.CollectBus.Common.Consts; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.Common.Extensions; using JiShe.CollectBus.Common.Helpers; using JiShe.CollectBus.Common.Models; -using JiShe.CollectBus.Enums; -using JiShe.CollectBus.IotSystems.Devices; using JiShe.CollectBus.IotSystems.MessageReceiveds; using JiShe.CollectBus.IotSystems.Protocols; -using JiShe.CollectBus.Kafka.Producer; using JiShe.CollectBus.Protocol.Abstracts; using JiShe.CollectBus.Protocol.Models; using JiShe.CollectBus.Protocol.T37612012.SendData; using JiShe.CollectBus.Protocol3761; +using JiShe.ServicePro.Kafka.Consts; +using JiShe.ServicePro.Kafka.Extensions; +using JiShe.ServicePro.Kafka.Producer; using Mapster; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using TouchSocket.Sockets; -using Volo.Abp.Domain.Repositories; namespace JiShe.CollectBus.Protocol.T37612012 { @@ -27,7 +25,6 @@ namespace JiShe.CollectBus.Protocol.T37612012 private readonly IProducerService _producerService; - private readonly IRepository _deviceRepository; private readonly ITcpService _tcpService; public readonly Dictionary T3761AFNHandlers; @@ -41,7 +38,6 @@ namespace JiShe.CollectBus.Protocol.T37612012 _logger = logger; //_logger = serviceProvider.GetRequiredService>(); _producerService = serviceProvider.GetRequiredService(); - _deviceRepository = serviceProvider.GetRequiredService>(); _tcpService = tcpService; T3761AFNHandlers = Telemetry3761PacketBuilder.T3761AFNHandlers; } @@ -94,9 +90,9 @@ namespace JiShe.CollectBus.Protocol.T37612012 /// private async Task OnTcpNormalReceived(ITcpSessionClient tcpSessionClient, TB3761 tB3761) { - //string topicName = string.Format(ProtocolConst.AFNTopicNameFormat, aFn); + //string topicName = string.Format(KafkaTopicConsts.AFNTopicNameFormat, aFn); //todo 如何确定时标?目前集中器的采集频率,都是固定,数据上报的时候,根据当前时间,往后推测出应当采集的时间点作为时标。但是如果由于网络问题,数据一直没上报的情况改怎么计算? - //await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedEventName, new MessageReceived + //await _producerBus.PublishAsync(KafkaTopicConsts.SubscriberReceivedEventName, new MessageReceived //{ // ClientId = client.Id, // ClientIp = client.IP, @@ -112,7 +108,7 @@ namespace JiShe.CollectBus.Protocol.T37612012 return; } //TODO:根据AFN进行分流推送到kafka - string topicName = string.Format(ProtocolConst.AFNTopicNameFormat, tB3761.AFN_FC.AFN.ToString().PadLeft(2, '0')); + string topicName = string.Format(KafkaTopicConsts.AFNTopicNameFormat, tB3761.AFN_FC.AFN.ToString().PadLeft(2, '0')); tB3761.MessageId = Guid.NewGuid().ToString(); tB3761.ReceivedTime = DateTime.Now; MessageProtocolAnalysis messageReceivedAnalysis = new MessageProtocolAnalysis() @@ -127,13 +123,12 @@ namespace JiShe.CollectBus.Protocol.T37612012 Data = tB3761 }; - List topics = ProtocolConstExtensions.GetAllTopicNamesByReceived(); + List topics = KafkaTopicConstExtensions.GetAllTopicNamesByReceived(); if (topics.Contains(topicName)) await _producerService.ProduceAsync(topicName, messageReceivedAnalysis); else { _logger.LogError($"不支持的上报kafka主题:{topicName}"); - await _producerService.ProduceAsync(ProtocolConst.SubscriberReceivedEventName, messageReceivedAnalysis); } } @@ -151,15 +146,15 @@ namespace JiShe.CollectBus.Protocol.T37612012 { string oldClientId = $"{client.Id}"; await client.ResetIdAsync(code); - //var deviceInfoList = await _deviceRepository.GetListAsync(a => a.Number == code); - //if (deviceInfoList != null && deviceInfoList.Count > 1) + //var DeviceCacheInfoList = await _deviceRepository.GetListAsync(a => a.Number == code); + //if (DeviceCacheInfoList != null && DeviceCacheInfoList.Count > 1) //{ // //todo 推送集中器编号重复预警 // _logger.LogError($"集中器编号:{code},存在多个集中器,请检查集中器编号是否重复"); // return; //} - //var entity = deviceInfoList?.FirstOrDefault(a => a.Number == code); + //var entity = DeviceCacheInfoList?.FirstOrDefault(a => a.Number == code); //if (entity == null) //{ // await _deviceRepository.InsertAsync(new Device(code, oldClientId, DateTime.Now, DateTime.Now, DeviceStatus.Online)); @@ -181,7 +176,7 @@ namespace JiShe.CollectBus.Protocol.T37612012 MessageId = Guid.NewGuid().ToString(), ReceivedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; - //await _producerService.ProduceAsync(ProtocolConst.SubscriberLoginReceivedEventName, messageReceivedLoginEvent); + //await _producerService.ProduceAsync(KafkaTopicConsts.SubscriberLoginReceivedEventName, messageReceivedLoginEvent); var reqParam = new ReqParameter2 { @@ -213,7 +208,7 @@ namespace JiShe.CollectBus.Protocol.T37612012 { await _tcpService.SendAsync(issuedEventMessage.ClientId, issuedEventMessage.Message); _logger.LogWarning($"集中器地址{issuedEventMessage.ClientId} 登录回复下发内容:{Convert.ToHexString(bytes)}"); - // await _producerService.ProduceAsync(ProtocolConst.SubscriberLoginIssuedEventName, issuedEventMessage); + // await _producerService.ProduceAsync(KafkaTopicConsts.SubscriberLoginIssuedEventName, issuedEventMessage); } } @@ -230,15 +225,15 @@ namespace JiShe.CollectBus.Protocol.T37612012 string clientId = code; //string oldClientId = $"{client.Id}"; - //var deviceInfoList = await _deviceRepository.GetListAsync(a => a.Number == code); - //if (deviceInfoList != null && deviceInfoList.Count > 1) + //var DeviceCacheInfoList = await _deviceRepository.GetListAsync(a => a.Number == code); + //if (DeviceCacheInfoList != null && DeviceCacheInfoList.Count > 1) //{ // //todo 推送集中器编号重复预警 // _logger.LogError($"集中器编号:{code},存在多个集中器,请检查集中器编号是否重复"); // return; //} - //var entity = deviceInfoList?.FirstOrDefault(a => a.Number == code); + //var entity = DeviceCacheInfoList?.FirstOrDefault(a => a.Number == code); //if (entity == null) //没有登录帧的设备,只有心跳帧 //{ await client.ResetIdAsync(clientId); @@ -268,7 +263,7 @@ namespace JiShe.CollectBus.Protocol.T37612012 MessageId = Guid.NewGuid().ToString(), ReceivedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; - // await _producerService.ProduceAsync(ProtocolConst.SubscriberHeartbeatReceivedEventName, messageReceivedHeartbeatEvent); + // await _producerService.ProduceAsync(KafkaTopicConsts.SubscriberHeartbeatReceivedEventName, messageReceivedHeartbeatEvent); var reqParam = new ReqParameter2() { @@ -301,7 +296,7 @@ namespace JiShe.CollectBus.Protocol.T37612012 { await _tcpService.SendAsync(issuedEventMessage.ClientId, bytes); _logger.LogWarning($"集中器地址{issuedEventMessage.ClientId} 心跳回复下发内容:{Convert.ToHexString(bytes)}"); - // await _producerService.ProduceAsync(ProtocolConst.SubscriberHeartbeatIssuedEventName, issuedEventMessage); + // await _producerService.ProduceAsync(KafkaTopicConsts.SubscriberHeartbeatIssuedEventName, issuedEventMessage); } diff --git a/protocols/JiShe.CollectBus.Protocol.T6452007/GlobalUsings.cs b/protocols/JiShe.CollectBus.Protocol.T6452007/GlobalUsings.cs new file mode 100644 index 0000000..d1b32ae --- /dev/null +++ b/protocols/JiShe.CollectBus.Protocol.T6452007/GlobalUsings.cs @@ -0,0 +1,9 @@ +// Global using directives + +global using System.Text; +global using Volo.Abp.Modularity; +global using JiShe.ServicePro.Enums; +global using JiShe.ServicePro.FreeRedisProvider; +global using JiShe.ServicePro.Consts; +global using JiShe.ServicePro.Core; +global using JiShe.ServicePro.ServerOptions; \ No newline at end of file diff --git a/protocols/JiShe.CollectBus.Protocol.T6452007/JiShe.CollectBus.Protocol.T6452007.csproj b/protocols/JiShe.CollectBus.Protocol.T6452007/JiShe.CollectBus.Protocol.T6452007.csproj index e19a7e0..1876a75 100644 --- a/protocols/JiShe.CollectBus.Protocol.T6452007/JiShe.CollectBus.Protocol.T6452007.csproj +++ b/protocols/JiShe.CollectBus.Protocol.T6452007/JiShe.CollectBus.Protocol.T6452007.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable @@ -10,8 +10,8 @@ - - + + diff --git a/protocols/JiShe.CollectBus.Protocol.Test/JiShe.CollectBus.Protocol.Test.csproj b/protocols/JiShe.CollectBus.Protocol.Test/JiShe.CollectBus.Protocol.Test.csproj index e798bdb..5eceed5 100644 --- a/protocols/JiShe.CollectBus.Protocol.Test/JiShe.CollectBus.Protocol.Test.csproj +++ b/protocols/JiShe.CollectBus.Protocol.Test/JiShe.CollectBus.Protocol.Test.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable @@ -10,8 +10,8 @@ - - + + diff --git a/protocols/JiShe.CollectBus.Protocol.Test/JiSheCollectBusTestProtocolModule.cs b/protocols/JiShe.CollectBus.Protocol.Test/JiSheCollectBusTestProtocolModule.cs index 6c3ebdc..f35b00e 100644 --- a/protocols/JiShe.CollectBus.Protocol.Test/JiSheCollectBusTestProtocolModule.cs +++ b/protocols/JiShe.CollectBus.Protocol.Test/JiSheCollectBusTestProtocolModule.cs @@ -1,5 +1,7 @@ using JiShe.CollectBus.Protocol.Interfaces; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System.Reflection; using Volo.Abp; using Volo.Abp.Modularity; diff --git a/protocols/JiShe.CollectBus.Protocol.Test/TestServer.cs b/protocols/JiShe.CollectBus.Protocol.Test/TestServer.cs new file mode 100644 index 0000000..23a8703 --- /dev/null +++ b/protocols/JiShe.CollectBus.Protocol.Test/TestServer.cs @@ -0,0 +1,27 @@ +using JiShe.CollectBus.Protocol.Interfaces; +using JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_00H; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.Protocol.Test +{ + public class TestServer: IAnalysisStrategy + { + private readonly ILogger _logger; + public TestServer (ILogger logger) + { + _logger= logger; + } + + public Task ExecuteAsync(string input, Action? result = null) + { + _logger.LogWarning("测试插件动态加载"); + return Task.FromResult(true); + } + + } +} diff --git a/protocols/JiShe.CollectBus.Protocol/Abstracts/ProtocolPlugin.cs b/protocols/JiShe.CollectBus.Protocol/Abstracts/ProtocolPlugin.cs index c6779e5..a9add3f 100644 --- a/protocols/JiShe.CollectBus.Protocol/Abstracts/ProtocolPlugin.cs +++ b/protocols/JiShe.CollectBus.Protocol/Abstracts/ProtocolPlugin.cs @@ -1,12 +1,10 @@ -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.FreeRedis; -using JiShe.CollectBus.IotSystems.Protocols; +using JiShe.CollectBus.IotSystems.Protocols; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol.Models; +using JiShe.ServicePro.FreeRedisProvider; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using TouchSocket.Sockets; -using Volo.Abp.Domain.Repositories; namespace JiShe.CollectBus.Protocol.Abstracts { @@ -20,13 +18,11 @@ namespace JiShe.CollectBus.Protocol.Abstracts public const string errorData = "EE"; 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(); } @@ -40,10 +36,8 @@ namespace JiShe.CollectBus.Protocol.Abstracts if (Info == null) { throw new ArgumentNullException(nameof(Info)); - } + } - await _protocolInfoRepository.DeleteDirectAsync(a => a.Name == Info.Name); - await _protocolInfoRepository.InsertAsync(Info); await _redisProvider.Instance.HDelAsync($"{RedisConst.ProtocolKey}", Info.Name); await _redisProvider.Instance.HSetAsync($"{RedisConst.ProtocolKey}", Info.Name, Info); } diff --git a/protocols/JiShe.CollectBus.Protocol/GlobalUsings.cs b/protocols/JiShe.CollectBus.Protocol/GlobalUsings.cs new file mode 100644 index 0000000..b6f2092 --- /dev/null +++ b/protocols/JiShe.CollectBus.Protocol/GlobalUsings.cs @@ -0,0 +1,9 @@ +// Global using directives + +global using System.Text; +global using Volo.Abp.Modularity; +global using JiShe.ServicePro.Enums; +global using JiShe.ServicePro.FreeRedisProvider; +global using JiShe.ServicePro.Consts; +global using JiShe.ServicePro.Core; +global using JiShe.ServicePro.ServerOptions; diff --git a/protocols/JiShe.CollectBus.Protocol/JiShe.CollectBus.Protocol.csproj b/protocols/JiShe.CollectBus.Protocol/JiShe.CollectBus.Protocol.csproj index 7fead27..e9a8214 100644 --- a/protocols/JiShe.CollectBus.Protocol/JiShe.CollectBus.Protocol.csproj +++ b/protocols/JiShe.CollectBus.Protocol/JiShe.CollectBus.Protocol.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable @@ -18,13 +18,14 @@ - - + + + + + - - diff --git a/protocols/JiShe.CollectBus.Protocol/Services/ProtocolService.cs b/protocols/JiShe.CollectBus.Protocol/Services/ProtocolService.cs index d51b7be..99bb314 100644 --- a/protocols/JiShe.CollectBus.Protocol/Services/ProtocolService.cs +++ b/protocols/JiShe.CollectBus.Protocol/Services/ProtocolService.cs @@ -1,9 +1,9 @@ using System.Text.RegularExpressions; using JiShe.CollectBus.Common; -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.FreeRedis; +using JiShe.CollectBus.Common.Consts; using JiShe.CollectBus.IotSystems.Protocols; using JiShe.CollectBus.Protocol.Interfaces; +using JiShe.ServicePro.FreeRedisProvider; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; diff --git a/services/JiShe.CollectBus.Application.Contracts/CollectBusApplicationContractsModule.cs b/services/JiShe.CollectBus.Application.Contracts/CollectBusApplicationContractsModule.cs index 932231c..9fd14ae 100644 --- a/services/JiShe.CollectBus.Application.Contracts/CollectBusApplicationContractsModule.cs +++ b/services/JiShe.CollectBus.Application.Contracts/CollectBusApplicationContractsModule.cs @@ -1,12 +1,14 @@ using Volo.Abp.Application; using Volo.Abp.Modularity; using Volo.Abp.Authorization; +using JiShe.ServicePro.IoTDBManagement; namespace JiShe.CollectBus; [DependsOn( typeof(CollectBusDomainSharedModule), typeof(AbpDddApplicationContractsModule), + typeof(IoTDBManagementApplicationContractsModule), typeof(AbpAuthorizationModule) )] public class CollectBusApplicationContractsModule : AbpModule diff --git a/services/JiShe.CollectBus.Application.Contracts/DataChannels/IDataChannelManageService.cs b/services/JiShe.CollectBus.Application.Contracts/DataChannels/IDataChannelManageService.cs index a126910..a2df329 100644 --- a/services/JiShe.CollectBus.Application.Contracts/DataChannels/IDataChannelManageService.cs +++ b/services/JiShe.CollectBus.Application.Contracts/DataChannels/IDataChannelManageService.cs @@ -1,5 +1,5 @@ using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using Microsoft.AspNetCore.Mvc; +using JiShe.ServicePro.IoTDBManagement.TableModels; using System; using System.Collections.Generic; using System.Linq; diff --git a/services/JiShe.CollectBus.Application.Contracts/GlobalUsings.cs b/services/JiShe.CollectBus.Application.Contracts/GlobalUsings.cs new file mode 100644 index 0000000..6b81fc0 --- /dev/null +++ b/services/JiShe.CollectBus.Application.Contracts/GlobalUsings.cs @@ -0,0 +1,13 @@ +// Global using directives + +global using System.Text; +global using Volo.Abp.Modularity; +global using JiShe.ServicePro.Enums; +global using JiShe.ServicePro.FreeRedisProvider; +global using JiShe.ServicePro.IoTDBManagement.Options; +global using JiShe.ServicePro.IoTDBManagement.SessionPools; +global using JiShe.ServicePro.IoTDBManagement.TableModels; +global using JiShe.ServicePro.Kafka.Consts; +global using JiShe.ServicePro.Kafka.Internal; +global using JiShe.ServicePro.Consts; +global using JiShe.ServicePro.Core; \ No newline at end of file diff --git a/services/JiShe.CollectBus.Application.Contracts/JiShe.CollectBus.Application.Contracts.csproj b/services/JiShe.CollectBus.Application.Contracts/JiShe.CollectBus.Application.Contracts.csproj index fedbafd..11df2da 100644 --- a/services/JiShe.CollectBus.Application.Contracts/JiShe.CollectBus.Application.Contracts.csproj +++ b/services/JiShe.CollectBus.Application.Contracts/JiShe.CollectBus.Application.Contracts.csproj @@ -3,7 +3,7 @@ - net8.0 + net9.0 enable JiShe.CollectBus True @@ -21,12 +21,15 @@ - - - + + + + + + + - diff --git a/services/JiShe.CollectBus.Application.Contracts/Permissions/CollectBusPermissions.cs b/services/JiShe.CollectBus.Application.Contracts/Permissions/CollectBusPermissions.cs index 7d71651..d6d633a 100644 --- a/services/JiShe.CollectBus.Application.Contracts/Permissions/CollectBusPermissions.cs +++ b/services/JiShe.CollectBus.Application.Contracts/Permissions/CollectBusPermissions.cs @@ -8,6 +8,6 @@ public class CollectBusPermissions public static string[] GetAll() { - return ReflectionHelper.GetPublicConstantsRecursively(typeof(CollectBusPermissions)); + return Volo.Abp.Reflection.ReflectionHelper.GetPublicConstantsRecursively(typeof(CollectBusPermissions)); } } diff --git a/services/JiShe.CollectBus.Application.Contracts/RedisDataCache/IRedisDataCacheService.cs b/services/JiShe.CollectBus.Application.Contracts/RedisDataCache/IRedisDataCacheService.cs deleted file mode 100644 index e28417b..0000000 --- a/services/JiShe.CollectBus.Application.Contracts/RedisDataCache/IRedisDataCacheService.cs +++ /dev/null @@ -1,174 +0,0 @@ -using JiShe.CollectBus.Common.Models; -using JiShe.CollectBus.IotSystems.Ammeters; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Application.Contracts -{ - /// - /// 数据缓存服务接口 - /// - public interface IRedisDataCacheService - { - /// - /// 单个添加数据 - /// - /// - /// Set索引缓存Key - /// hash缓存Key - /// 待缓存数据 - /// - Task InsertDataAsync( - string redisSetIndexCacheKey, - string redisDeviceInfoHashCacheKey, - T data) where T : DeviceCacheBasicModel; - - - /// - /// 批量添加数据 - /// - /// - /// Set索引缓存Key - /// hash缓存Key - /// 待缓存数据集合 - /// - Task BatchInsertDataAsync( - string redisSetIndexCacheKey, - string redisDeviceInfoHashCacheKey, - Dictionary> items) where T : DeviceCacheBasicModel; - - - /// - /// 删除缓存信息 - /// - /// - /// Set索引缓存Key - /// hash缓存Key - /// 已缓存数据 - /// - Task RemoveCacheDataAsync( - string redisSetIndexCacheKey, - string redisDeviceInfoHashCacheKey, - T data) where T : DeviceCacheBasicModel; - - /// - /// 修改缓存信息,映射关系未发生改变 - /// - /// - /// 主数据存储Hash缓存Key - /// Set索引缓存Key - /// ZSET索引缓存Key - /// 待修改缓存数据 - /// - Task ModifyDataAsync( - string redisHashCacheKey, - string redisSetIndexCacheKey, - string redisZSetScoresIndexCacheKey, - T newData) where T : DeviceCacheBasicModel; - - - /// - /// 修改缓存信息,映射关系已改变 - /// - /// - /// 主数据存储Hash缓存Key - /// Set索引缓存Key - /// 旧的映射关系 - /// ZSET索引缓存Key - /// 待修改缓存数据 - /// - Task ModifyDataAsync( - string redisHashCacheKey, - string redisSetIndexCacheKey, - string oldMemberId, - string redisZSetScoresIndexCacheKey, - T newData) where T : DeviceCacheBasicModel; - - /// - /// 通过集中器与表计信息排序索引获取数据 - /// - /// - /// 主数据存储Hash缓存Key - /// ZSET索引缓存Key - /// 分页尺寸 - /// 最后一个索引 - /// 最后一个唯一标识 - /// 排序方式 - /// - Task> GetPagedData( - string redisHashCacheKey, - string redisZSetScoresIndexCacheKey, - IEnumerable focusIds, - int pageSize = 10, - decimal? lastScore = null, - string lastMember = null, - bool descending = true) - where T : DeviceCacheBasicModel; - - /// - /// 通过集中器与表计信息排序索引获取数据 - /// - /// - /// 主数据存储Hash缓存Key - /// ZSET索引缓存Key - /// ZSET索引的原始数据,例如集中地址和点位的组合 - /// 分页尺寸 - /// 最后一个索引 - /// 最后一个唯一标识 - /// 排序方式 - /// - Task> GetSingleData( - string redisHashCacheKey, - string redisZSetScoresIndexCacheKey, - string scoreValueRawData, - int pageSize = 10, - decimal? lastScore = null, - string lastMember = null, - bool descending = true) - where T : DeviceCacheBasicModel; - - - /// - /// 通过ZSET索引获取数据,支持10万级别数据处理,控制在13秒以内。 - /// - /// - /// 主数据存储Hash缓存Key - /// ZSET索引缓存Key - /// 分页尺寸 - /// 最后一个索引 - /// 最后一个唯一标识 - /// 排序方式 - /// - Task> GetAllPagedData( - string redisHashCacheKey, - string redisZSetScoresIndexCacheKey, - int pageSize = 1000, - decimal? lastScore = null, - string lastMember = null, - bool descending = true) - where T : DeviceCacheBasicModel; - - /// - /// 通过ZSET索引获取数据,支持10万级别数据处理,控制在13秒以内。 - /// - /// - /// ZSET索引缓存Key - /// 主数据存储Hash缓存Key - /// 分页尺寸 - /// 最后一个索引 - /// 最后一个唯一标识 - /// 排序方式 - /// - Task> GetAllPagedData2( - string redisCacheDeviceGroupSetIndexKey, - string redisCacheDeviceInfoHashKey, - int pageSize = 1000, - decimal? lastScore = null, - string lastMember = null, - bool descending = true) - where T : DeviceCacheBasicModel; - } -} diff --git a/services/JiShe.CollectBus.Application.Contracts/ScheduledMeterReading/IScheduledMeterReadingService.cs b/services/JiShe.CollectBus.Application.Contracts/ScheduledMeterReading/IScheduledMeterReadingService.cs index b82feab..4173f76 100644 --- a/services/JiShe.CollectBus.Application.Contracts/ScheduledMeterReading/IScheduledMeterReadingService.cs +++ b/services/JiShe.CollectBus.Application.Contracts/ScheduledMeterReading/IScheduledMeterReadingService.cs @@ -7,6 +7,8 @@ using JiShe.CollectBus.IotSystems.Ammeters; using JiShe.CollectBus.IotSystems.Devices; using JiShe.CollectBus.IotSystems.MeterReadingRecords; using JiShe.CollectBus.IotSystems.Watermeter; +using JiShe.ServicePro.FreeRedisProvider; +using JiShe.ServicePro.IoTDBManagement.TableModels; using Volo.Abp.Application.Services; namespace JiShe.CollectBus.ScheduledMeterReading @@ -35,7 +37,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// /// 采集端Code /// - Task> GetAmmeterInfoList(string gatherCode = ""); + Task> GetAmmeterInfoList(string gatherCode = ""); /// /// 初始化电表缓存数据 @@ -44,23 +46,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// Task InitAmmeterCacheData(string gatherCode = ""); - /// - /// 1分钟采集电表数据,只获取任务数据下发,不构建任务 - /// - /// - Task AmmeterScheduledMeterOneMinuteReading(); - - /// - /// 5分钟采集电表数据,只获取任务数据下发,不构建任务 - /// - /// - Task AmmeterScheduledMeterFiveMinuteReading(); - - /// - /// 15分钟采集电表数据,只获取任务数据下发,不构建任务 - /// - /// - Task AmmeterScheduledMeterFifteenMinuteReading(); + /// /// 获取电表阀控配置 @@ -83,7 +69,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - Task> AmmeterScheduledAutomaticVerificationTime(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps); + Task> AmmeterScheduledAutomaticVerificationTime(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps); /// /// 日冻结抄读 @@ -93,7 +79,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - Task> AmmeterScheduledGetAutomaticDayFreezeData(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps); + Task> AmmeterScheduledGetAutomaticDayFreezeData(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps); /// /// 月冻结数据抄读 @@ -103,7 +89,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - Task> AmmeterScheduledGetAutomaticMonthFreezeData(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps); + Task> AmmeterScheduledGetAutomaticMonthFreezeData(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps); #endregion @@ -113,7 +99,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// /// 采集端Code /// - Task> GetWatermeterInfoList(string gatherCode = ""); + Task> GetWatermeterInfoList(string gatherCode = ""); /// /// 初始化水表缓存数据,只获取任务数据下发,不构建任务 @@ -122,12 +108,6 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// Task InitWatermeterCacheData(string gatherCode = ""); - /// - /// 水表数据采集 - /// - /// - Task WatermeterScheduledMeterAutoReadding(); - #endregion #region 集中器处理 @@ -140,7 +120,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - Task> ConcentratorScheduledAutomaticGetTerminalVersion(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps); + Task> ConcentratorScheduledAutomaticGetTerminalVersion(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps); /// /// 自动获取远程通信模块(SIM)版本信息 @@ -150,7 +130,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - Task> ConcentratorScheduledAutomaticGetTelematicsModule(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps); + Task> ConcentratorScheduledAutomaticGetTelematicsModule(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps); #endregion diff --git a/services/JiShe.CollectBus.Application.Contracts/Subscribers/IServiceCommunicationChannelSubscriberService.cs b/services/JiShe.CollectBus.Application.Contracts/Subscribers/IServiceCommunicationChannelSubscriberService.cs new file mode 100644 index 0000000..fc448b9 --- /dev/null +++ b/services/JiShe.CollectBus.Application.Contracts/Subscribers/IServiceCommunicationChannelSubscriberService.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace JiShe.CollectBus.Subscribers +{ + /// + /// 数据通讯通道订阅者服务 + /// + public interface IServiceCommunicationChannelSubscriberService:IApplicationService + { + /// + /// 数据通讯通道消息消费订阅 + /// + /// + /// + Task ServiceCommunicationChannelIssuedEvent(Dictionary issuedMessage); + } +} diff --git a/services/JiShe.CollectBus.Application.Contracts/Subscribers/ISubscriberAppService.cs b/services/JiShe.CollectBus.Application.Contracts/Subscribers/ISubscriberAppService.cs index 63594c8..1f227fa 100644 --- a/services/JiShe.CollectBus.Application.Contracts/Subscribers/ISubscriberAppService.cs +++ b/services/JiShe.CollectBus.Application.Contracts/Subscribers/ISubscriberAppService.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using JiShe.CollectBus.Common.Models; +using JiShe.CollectBus.Common.Models; using JiShe.CollectBus.IotSystems.MessageReceiveds; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Protocol3761; +using JiShe.ServicePro.Kafka.Internal; using Volo.Abp.Application.Services; namespace JiShe.CollectBus.Subscribers @@ -12,7 +9,6 @@ namespace JiShe.CollectBus.Subscribers { Task LoginIssuedEvent(List issuedEventMessage); Task HeartbeatIssuedEvent(List issuedEventMessage); - Task ReceivedEvent(MessageProtocolAnalysis receivedMessage); Task ReceivedHeartbeatEvent(List receivedHeartbeatMessage); Task ReceivedLoginEvent(List receivedLoginMessage); } diff --git a/services/JiShe.CollectBus.Application.Contracts/Subscribers/IWorkerSubscriberAppService.cs b/services/JiShe.CollectBus.Application.Contracts/Subscribers/IWorkerSubscriberAppService.cs index e899ce4..7ab7f03 100644 --- a/services/JiShe.CollectBus.Application.Contracts/Subscribers/IWorkerSubscriberAppService.cs +++ b/services/JiShe.CollectBus.Application.Contracts/Subscribers/IWorkerSubscriberAppService.cs @@ -1,11 +1,5 @@ -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.IotSystems.MessageIssueds; -using JiShe.CollectBus.IotSystems.MessageReceiveds; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.Kafka.Attributes; -using JiShe.CollectBus.Kafka.Internal; -using System.Collections.Generic; -using System.Threading.Tasks; +using JiShe.ServicePro.IoTDBManagement.TableModels; +using JiShe.ServicePro.Kafka.Internal; using Volo.Abp.Application.Services; namespace JiShe.CollectBus.Subscribers diff --git a/services/JiShe.CollectBus.Application/CollectBusAppService.cs b/services/JiShe.CollectBus.Application/CollectBusAppService.cs index bd4a232..c2e621a 100644 --- a/services/JiShe.CollectBus.Application/CollectBusAppService.cs +++ b/services/JiShe.CollectBus.Application/CollectBusAppService.cs @@ -1,17 +1,8 @@ -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Extensions; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.FreeSql; -using JiShe.CollectBus.Localization; +using JiShe.CollectBus.Localization; +using JiShe.ServicePro.FreeRedisProvider; +using JiShe.ServicePro.FreeSqlProvider; using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using JiShe.CollectBus.FreeRedis; using Volo.Abp.Application.Services; -using Volo.Abp.Timing; namespace JiShe.CollectBus; diff --git a/services/JiShe.CollectBus.Application/CollectBusApplicationModule.cs b/services/JiShe.CollectBus.Application/CollectBusApplicationModule.cs index 942befc..5a9f7a2 100644 --- a/services/JiShe.CollectBus.Application/CollectBusApplicationModule.cs +++ b/services/JiShe.CollectBus.Application/CollectBusApplicationModule.cs @@ -1,30 +1,20 @@ -using Cassandra.Mapping; -using JiShe.CollectBus.Cassandra; -using JiShe.CollectBus.DataChannels; -using JiShe.CollectBus.FreeRedis; -using JiShe.CollectBus.FreeSql; +using JiShe.CollectBus.DataChannels; using JiShe.CollectBus.Interceptors; -using JiShe.CollectBus.IoTDB; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.Kafka; -using JiShe.CollectBus.Mappers; using JiShe.CollectBus.Protocol; using JiShe.CollectBus.ScheduledMeterReading; +using JiShe.CollectBus.Workers; +using JiShe.ServicePro.FreeRedisProvider; +using JiShe.ServicePro.FreeSqlProvider; +using JiShe.ServicePro.IoTDBManagement; +using JiShe.ServicePro.IoTDBManagement.TableModels; +using JiShe.ServicePro.Kafka; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Threading.Channels; -using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Application; -using Volo.Abp.AuditLogging; using Volo.Abp.Autofac; using Volo.Abp.AutoMapper; -using Volo.Abp.BackgroundJobs; -using Volo.Abp.BackgroundWorkers; -using Volo.Abp.BackgroundWorkers.Hangfire; using Volo.Abp.Modularity; namespace JiShe.CollectBus; @@ -34,16 +24,12 @@ namespace JiShe.CollectBus; typeof(CollectBusApplicationContractsModule), typeof(AbpDddApplicationModule), typeof(AbpAutoMapperModule), + typeof(ServiceProFreeRedisProviderModule), + typeof(ServiceProFreeSqlProviderModule), + typeof(ServiceProKafkaModule), + typeof(IoTDBManagementApplicationModule), typeof(AbpAutofacModule), - typeof(AbpBackgroundWorkersHangfireModule), - typeof(CollectBusFreeRedisModule), - typeof(CollectBusFreeSqlModule), - typeof(CollectBusKafkaModule), - typeof(CollectBusIoTDbModule), typeof(CollectBusDomainSharedModule), - typeof(AbpAuditLoggingDomainModule), - typeof(AbpBackgroundJobsDomainModule), - typeof(CollectBusCassandraModule), typeof(CollectBusProtocolModule) )] public class CollectBusApplicationModule : AbpModule @@ -55,8 +41,10 @@ public class CollectBusApplicationModule : AbpModule context.Services.AddAutoMapperObjectMapper(); Configure(options => { options.AddMaps(true); }); - context.Services.AddSingleton(new MappingConfiguration() - .Define(new CollectBusMapping())); + context.Services.AddHostedService(); + + //context.Services.AddSingleton(new MappingConfiguration() + // .Define(new CollectBusMapping())); // 注册拦截器 context.Services.OnRegistered(ctx => @@ -65,38 +53,17 @@ public class CollectBusApplicationModule : AbpModule var any = methods.Any(a => a.GetCustomAttribute() != null); if (any) ctx.Interceptors.TryAdd(); }); + } public override async Task OnApplicationInitializationAsync( ApplicationInitializationContext context) { - var assembly = Assembly.GetExecutingAssembly(); - var types = assembly.GetTypes().Where(t => typeof(ICollectWorker).IsAssignableFrom(t) && !t.IsInterface) - .ToList(); - foreach (var type in types) await context.AddBackgroundWorkerAsync(type); - - //Task.Run(() => - //{ - // //默认初始化表计信息 - // var dbContext = context.ServiceProvider.GetRequiredService(); - // dbContext.InitAmmeterCacheData(); - // //await dbContext.InitWatermeterCacheData(); - //}).ConfigureAwait(false); - + + //下发任务通道构建 DataChannelManage.TaskDataChannel = Channel.CreateUnbounded>>(); - - - // 日志存储通道构建 - DataChannelManage.LogSaveChannel = Channel.CreateUnbounded(); - - // 日志刷新通道构建 - DataChannelManage.LogRefreshChannel = Channel.CreateUnbounded(); - - // 启动通道任务 - var _dataChannelManage = context.ServiceProvider.GetRequiredService(); - _ = _dataChannelManage.LogSaveAsync(DataChannelManage.LogSaveChannel.Reader); - + //默认初始化表计信息 var dbContext = context.ServiceProvider.GetRequiredService(); await dbContext.InitAmmeterCacheData("V4-Gather-8890"); diff --git a/services/JiShe.CollectBus.Application/DataChannels/DataChannelManage.cs b/services/JiShe.CollectBus.Application/DataChannels/DataChannelManage.cs index 6130a25..1eb8c4c 100644 --- a/services/JiShe.CollectBus.Application/DataChannels/DataChannelManage.cs +++ b/services/JiShe.CollectBus.Application/DataChannels/DataChannelManage.cs @@ -1,5 +1,6 @@ using JiShe.CollectBus.IotSystems.MeterReadingRecords; using JiShe.CollectBus.Protocol.Dto; +using JiShe.ServicePro.IoTDBManagement.TableModels; using System; using System.Collections.Generic; using System.Linq; diff --git a/services/JiShe.CollectBus.Application/DataChannels/DataChannelManageService.cs b/services/JiShe.CollectBus.Application/DataChannels/DataChannelManageService.cs index 5fe49f2..c45c242 100644 --- a/services/JiShe.CollectBus.Application/DataChannels/DataChannelManageService.cs +++ b/services/JiShe.CollectBus.Application/DataChannels/DataChannelManageService.cs @@ -1,29 +1,10 @@ -using Amazon.Runtime.Internal.Transform; -using DnsClient.Protocol; -using JiShe.CollectBus.Common; +using JiShe.CollectBus.Common; using JiShe.CollectBus.Common.DeviceBalanceControl; -using JiShe.CollectBus.IoTDB.Context; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IotSystems.LogRecord; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Kafka.Producer; -using JiShe.CollectBus.Protocol.Dto; -using JiShe.CollectBus.Protocol.Models; -using JiShe.CollectBus.Repository.LogRecord; -using Mapster; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; -using System.Threading; using System.Threading.Channels; -using System.Threading.Tasks; using Volo.Abp.DependencyInjection; -using ZstdSharp; namespace JiShe.CollectBus.DataChannels { @@ -33,26 +14,23 @@ namespace JiShe.CollectBus.DataChannels public class DataChannelManageService : IDataChannelManageService, ITransientDependency { private readonly ILogger _logger; - private readonly IIoTDbProvider _dbProvider; + private readonly IoTDBSessionPoolProvider _dbProvider; private readonly IProducerService _producerService; private readonly KafkaOptionConfig _kafkaOptions; - private readonly ServerApplicationOptions _applicationOptions; - private readonly ILogRecordRepository _logRecordRepository; + private readonly ServerApplicationOptions _applicationOptions; public DataChannelManageService( ILogger logger, - IIoTDbProvider dbProvider, + IoTDBSessionPoolProvider dbProvider, IProducerService producerService, IOptions kafkaOptions, - IOptions applicationOptions, - ILogRecordRepository logRecordRepository) + IOptions applicationOptions) { _logger = logger; _dbProvider = dbProvider; _producerService = producerService; _kafkaOptions = kafkaOptions.Value; - _applicationOptions = applicationOptions.Value; - _logRecordRepository= logRecordRepository; + _applicationOptions = applicationOptions.Value; } /// @@ -204,131 +182,6 @@ namespace JiShe.CollectBus.DataChannels await Task.Delay(1000 * (retry + 1)); } } - } - - - - /// - /// 日志保存 - /// - /// - /// - public async Task LogSaveAsync(ChannelReader channelReader) - { - - const int BatchSize = 1000; - const int EmptyWaitMilliseconds = 1000; - var timeout = TimeSpan.FromSeconds(2); - var timer = Stopwatch.StartNew(); - long timeoutMilliseconds = 0; - try - { - while (true) - { - var batch = new List(); - var canRead = channelReader.Count; - if (canRead <= 0) - { - if (timeoutMilliseconds > 0) - { - _logger.LogError($"{nameof(LogSaveAsync)} 通道处理数据耗时{timeoutMilliseconds}毫秒"); - } - timeoutMilliseconds = 0; - //无消息时短等待1秒 - await Task.Delay(EmptyWaitMilliseconds); - continue; - } - - timer.Restart(); - var startTime = DateTime.Now; - - try - { - // 异步批量读取数据 - while (batch != null && batch.Count < BatchSize && (DateTime.Now - startTime) < timeout) - { - try - { - if (channelReader.TryRead(out var dataItem)) - { - batch.Add(dataItem); - } - } - catch (Exception) - { - throw; - } - } - } - catch (Exception) - { - throw; - } - - if (batch == null || batch.Count == 0) - { - await Task.Delay(EmptyWaitMilliseconds); - continue; - } - try - { - - // 按小时分组 - var hourGroups = new Dictionary>(); - DateTime? dateTime = null; - List batchList = new List(); - int index = 1; - foreach (var item in batch) - { - var records = item.Adapt(); - - if (!records.ReceivedTime.HasValue) - records.ReceivedTime = DateTime.Now; - var curDateTime = new DateTime(records.ReceivedTime.Value.Year, records.ReceivedTime.Value.Month, records.ReceivedTime.Value.Hour, records.ReceivedTime.Value.Day, records.ReceivedTime.Value.Hour, 0, 0); - if (!dateTime.HasValue || curDateTime != dateTime) - { - dateTime = curDateTime; - if (batchList.Count > 0) - { - var immutableList = ImmutableList.CreateRange(batchList); - hourGroups.Add(dateTime.Value, immutableList.ToList()); - batchList.Clear(); - } - } - batchList.Add(records); - // 最后一批 - if(index== batch.Count) - { - var immutableList = ImmutableList.CreateRange(batchList); - hourGroups.Add(dateTime.Value, immutableList.ToList()); - batchList.Clear(); - } - index++; - } - foreach (var (time, records) in hourGroups) - { - // 批量写入数据库 - await _logRecordRepository.InsertManyAsync(records, time); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "数据通道处理日志数据时发生异常"); - } - batch.Clear(); - timer.Stop(); - - timeoutMilliseconds = timeoutMilliseconds + timer.ElapsedMilliseconds; - - startTime = DateTime.Now; - } - } - catch (Exception ex) - { - _logger.LogCritical(ex, "日志处理发生致命错误"); - throw; - } - } - + } } } diff --git a/services/JiShe.CollectBus.Application/EnergySystem/CacheAppService.cs b/services/JiShe.CollectBus.Application/EnergySystem/CacheAppService.cs index 6e2034a..b8f5ca0 100644 --- a/services/JiShe.CollectBus.Application/EnergySystem/CacheAppService.cs +++ b/services/JiShe.CollectBus.Application/EnergySystem/CacheAppService.cs @@ -1,18 +1,4 @@ -using JiShe.CollectBus.IotSystems.Records; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.EnergySystems.TableViews; -using JiShe.CollectBus.FreeSql; -using Volo.Abp.Domain.Repositories; -using JiShe.CollectBus.IotSystems.PrepayModel; -using JiShe.CollectBus.Ammeters; -using JiShe.CollectBus.Common.BuildSendDatas; -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Helpers; +using JiShe.CollectBus.EnergySystems.TableViews; namespace JiShe.CollectBus.EnergySystem { diff --git a/services/JiShe.CollectBus.Application/EnergySystem/EnergySystemAppService.cs b/services/JiShe.CollectBus.Application/EnergySystem/EnergySystemAppService.cs index 19a1296..305162f 100644 --- a/services/JiShe.CollectBus.Application/EnergySystem/EnergySystemAppService.cs +++ b/services/JiShe.CollectBus.Application/EnergySystem/EnergySystemAppService.cs @@ -1,26 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using DeviceDetectorNET.Class.Device; -using JiShe.CollectBus.Common.BuildSendDatas; -using JiShe.CollectBus.Common.Consts; +using JiShe.CollectBus.Common.BuildSendDatas; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.Common.Extensions; -using JiShe.CollectBus.Common.Helpers; using JiShe.CollectBus.Common.Models; using JiShe.CollectBus.EnergySystem.Dto; -using JiShe.CollectBus.FreeSql; using JiShe.CollectBus.IotSystems.PrepayModel; using JiShe.CollectBus.IotSystems.Records; -using JiShe.CollectBus.Kafka.Producer; -using JiShe.CollectBus.Protocol.Contracts; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using Volo.Abp.Domain.Repositories; -using YamlDotNet.Core; namespace JiShe.CollectBus.EnergySystem { @@ -71,7 +58,7 @@ namespace JiShe.CollectBus.EnergySystem if (bytes == null) return result; - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -80,7 +67,7 @@ namespace JiShe.CollectBus.EnergySystem // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -117,7 +104,7 @@ namespace JiShe.CollectBus.EnergySystem foreach (var bytes in bytesList) { - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -126,7 +113,7 @@ namespace JiShe.CollectBus.EnergySystem // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -168,7 +155,7 @@ namespace JiShe.CollectBus.EnergySystem }).ToList(); var bytes = Build3761SendData.BuildAmmeterParameterSetSendCmd(address, meterParameters); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -176,7 +163,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -206,7 +193,7 @@ namespace JiShe.CollectBus.EnergySystem var dataUnit = Build645SendData.BuildReadMeterAddressSendDataUnit(detail.MeterAddress); var bytes =Build3761SendData.BuildTransparentForwardingSendCmd(address, detail.Port, detail.BaudRate.ToString(), dataUnit, StopBit.Stop1, Parity.None); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -214,7 +201,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -297,7 +284,7 @@ namespace JiShe.CollectBus.EnergySystem if (bytes != null) { - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -306,7 +293,7 @@ namespace JiShe.CollectBus.EnergySystem // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -365,7 +352,7 @@ namespace JiShe.CollectBus.EnergySystem var bytes = Build3761SendData.BuildCommunicationParametersSetSendCmd(address, masterIP, materPort, backupIP, backupPort, input.Data.APN); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -374,7 +361,7 @@ namespace JiShe.CollectBus.EnergySystem // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -402,7 +389,7 @@ namespace JiShe.CollectBus.EnergySystem var bytes = Build3761SendData.BuildTerminalCalendarClockSendCmd(address); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -410,7 +397,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -438,7 +425,7 @@ namespace JiShe.CollectBus.EnergySystem bool isManual = !input.AreaCode.Equals("5110");//低功耗集中器不是长连接,在连接的那一刻再发送 var bytes = Build3761SendData.BuildConrCheckTimeSendCmd(address,DateTime.Now, isManual); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -446,7 +433,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -473,7 +460,7 @@ namespace JiShe.CollectBus.EnergySystem var address = $"{input.AreaCode}{input.Address}"; var bytes = Build3761SendData.BuildConrRebootSendCmd(address); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -481,7 +468,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -509,7 +496,7 @@ namespace JiShe.CollectBus.EnergySystem var address = $"{input.AreaCode}{input.Address}"; var pnList = input.Data.Split(',').Select(it => int.Parse(it)).ToList(); var bytes = Build3761SendData.BuildAmmeterParameterReadingSendCmd(address, pnList); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -517,7 +504,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -566,7 +553,7 @@ namespace JiShe.CollectBus.EnergySystem foreach (var bytes in bytesList) { - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -575,7 +562,7 @@ namespace JiShe.CollectBus.EnergySystem // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -644,7 +631,7 @@ namespace JiShe.CollectBus.EnergySystem foreach (var bytes in bytesList) { - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -652,7 +639,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -681,7 +668,7 @@ namespace JiShe.CollectBus.EnergySystem var address = $"{code.AreaCode}{code.Address}"; var bytes = Build3761SendData.BuildAmmeterReportCollectionItemsSetSendCmd(address,input.Detail.Pn, input.Detail.Unit,input.Detail.Cycle,input.Detail.BaseTime, input.Detail.CurveRatio,input.Detail.Details.Select(it => new PnFn(it.Pn,it.Fn)).ToList()); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -689,7 +676,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -717,7 +704,7 @@ namespace JiShe.CollectBus.EnergySystem { var address = $"{code.AreaCode}{code.Address}"; var bytes = Build3761SendData.BuildAmmeterAutoUpSwitchSetSendCmd(address, input.Detail.Pn,input.Detail.IsOpen); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -725,7 +712,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -751,7 +738,7 @@ namespace JiShe.CollectBus.EnergySystem var result = new BaseResultDto(); var address = $"{input.AreaCode}{input.Address}"; var bytes = Build3761SendData.BuildAmmeterReadAutoUpSwitchSendCmd(address, input.Detail.Pn); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -759,7 +746,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -786,7 +773,7 @@ namespace JiShe.CollectBus.EnergySystem { var address = $"{data.AreaCode}{data.Address}"; var bytes = Build3761SendData.BuildTerminalVersionInfoReadingSendCmd(address); - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -794,7 +781,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -849,7 +836,7 @@ namespace JiShe.CollectBus.EnergySystem foreach (var bytes in bytesList) { - //await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + //await _capBus.PublishAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage //{ // //ClientId = messageReceived.ClientId, // DeviceNo = address, @@ -857,7 +844,7 @@ namespace JiShe.CollectBus.EnergySystem // Type = IssuedEventType.Data, // MessageId = Guid.NewGuid().ToString() //}); - await _producerService.ProduceAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage + await _producerService.ProduceAsync(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, diff --git a/services/JiShe.CollectBus.Application/GlobalUsings.cs b/services/JiShe.CollectBus.Application/GlobalUsings.cs new file mode 100644 index 0000000..4c0d3a4 --- /dev/null +++ b/services/JiShe.CollectBus.Application/GlobalUsings.cs @@ -0,0 +1,17 @@ +// Global using directives + +global using System.Text; +global using Volo.Abp.Modularity; +global using JiShe.ServicePro.Enums; +global using JiShe.ServicePro.FreeRedisProvider; +global using JiShe.ServicePro.IoTDBManagement.Options; +global using JiShe.ServicePro.IoTDBManagement.SessionPools; +global using JiShe.ServicePro.IoTDBManagement.TableModels; +global using JiShe.ServicePro.Kafka.Consts; +global using JiShe.ServicePro.Kafka.Internal; +global using JiShe.ServicePro.Kafka.Attributes; +global using JiShe.ServicePro.FreeSqlProvider; +global using JiShe.ServicePro.Kafka.Producer; +global using JiShe.ServicePro.Consts; +global using JiShe.ServicePro.Core; +global using JiShe.ServicePro.ServerOptions; diff --git a/services/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj b/services/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj index 01878c2..96ad2e7 100644 --- a/services/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj +++ b/services/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj @@ -3,34 +3,37 @@ - net8.0 + net9.0 enable JiShe.CollectBus True + + + + + + - - - - - - - - + + + + + + + + + + + - - - - - - diff --git a/services/JiShe.CollectBus.Application/Mappers/CollectBusMapping.cs b/services/JiShe.CollectBus.Application/Mappers/CollectBusMapping.cs deleted file mode 100644 index a4d4ef4..0000000 --- a/services/JiShe.CollectBus.Application/Mappers/CollectBusMapping.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Cassandra.Mapping; -using JiShe.CollectBus.IotSystems.Devices; -using JiShe.CollectBus.IotSystems.MessageIssueds; - -namespace JiShe.CollectBus.Mappers -{ - public class CollectBusMapping: Mappings - { - public CollectBusMapping() - { - For() - .Column(e => e.Type, cm => cm.WithName("type").WithDbType()); - For() - .Column(e => e.Status, cm => cm.WithName("status").WithDbType()); - } - } -} diff --git a/services/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs b/services/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs index 0e92ff6..f84dd55 100644 --- a/services/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs +++ b/services/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs @@ -1,13 +1,11 @@ using JiShe.CollectBus.Common; +using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.IotSystems.Ammeters; using JiShe.CollectBus.IotSystems.Devices; -using JiShe.CollectBus.Kafka.Producer; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol3761; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System; -using System.Threading.Tasks; using TouchSocket.Core; using TouchSocket.Sockets; using Volo.Abp.Caching; @@ -20,37 +18,37 @@ namespace JiShe.CollectBus.Plugins { private readonly IProducerService _producerService; private readonly ILogger _logger; - private readonly IRepository _deviceRepository; private readonly IDistributedCache _ammeterInfoCache; private readonly IServiceProvider _serviceProvider; private readonly IProtocolService _protocolService; private readonly ServerApplicationOptions _serverApplicationOptions; + private readonly IFreeRedisProvider _freeRedisProvider; /// /// /// /// /// - /// /// /// /// /// + /// public TcpMonitor(IProducerService producerService, ILogger logger, - IRepository deviceRepository, IDistributedCache ammeterInfoCache, IServiceProvider serviceProvider, IOptions serverApplicationOptions, - IProtocolService protocolService) + IProtocolService protocolService, + IFreeRedisProvider freeRedisProvider) { _producerService = producerService; _logger = logger; - _deviceRepository = deviceRepository; _ammeterInfoCache = ammeterInfoCache; _serviceProvider= serviceProvider; _protocolService = protocolService; _serverApplicationOptions = serverApplicationOptions.Value; + _freeRedisProvider = freeRedisProvider; } public async Task OnTcpReceived(ITcpSession client, ReceivedDataEventArgs e) @@ -74,6 +72,61 @@ namespace JiShe.CollectBus.Plugins _logger.LogError($"指令初步解析失败,指令内容:{messageHexString}"); } + ////登录和心跳的时候,需要过滤TCP连接 + //if (tB3761.DT?.Fn == (int)FN.登录 || tB3761.DT?.Fn == (int)FN.心跳) + //{ + // string focusAddress = tB3761.A.Code; + // if (string.IsNullOrWhiteSpace(focusAddress))//集中器地址为空,主动关闭连接 + // { + // await tcpSessionClient.CloseAsync(); + // _logger.LogError($"指令解析失败,没有找到集中器地址,指令内容:{messageHexString}"); + // return; + // } + + // //查找Redis缓存的集中器信息 + // var focusCacheInfos = _freeRedisProvider.Instance.HGet(RedisConst.CacheAllFocusInfoHashKey, focusAddress); + // if (focusCacheInfos == null) + // { + // await tcpSessionClient.CloseAsync(); + // _logger.LogError($"TCP连接关闭,没有找到集中器地址{focusAddress}的缓存信息"); + // return; + // } + + // //检查集中器的信息的服务器标记是否为空,如果不为空,需要检查是否与当前服务器的标记一致 + // if (!string.IsNullOrWhiteSpace(focusCacheInfos.ServerTagName) && focusCacheInfos.ServerTagName != _serverApplicationOptions.ServerTagName) + // { + // await tcpSessionClient.CloseAsync(); + // _logger.LogError($"TCP连接关闭,集中器地址{focusAddress}的服务器标记为:{focusCacheInfos.ServerTagName},当前服务器的标记为:{_serverApplicationOptions.ServerTagName},不一致拒绝连接。"); + // return; + // } + + // int currentTCPConnectionCount = tcpSessionClient.Service.Count; + // int configTCPConnectionCount = _serverApplicationOptions.TCPConnectionCount;//配置TCP连接数量 + + // _logger.LogWarning($"当前配置连接数量为:{configTCPConnectionCount},当前连接数量为:{currentTCPConnectionCount}"); + + // if (currentTCPConnectionCount >= configTCPConnectionCount) + // { + // _logger.LogError($"当前连接数量为:{currentTCPConnectionCount},大于配置连接数量{configTCPConnectionCount},将拒绝集中{focusAddress}连接"); + // await tcpSessionClient.CloseAsync(); + // return; + // } + + // //缓存更新状态 + // focusCacheInfos.LastSurvivalTime = DateTime.Now; + // focusCacheInfos.ServerTagName = _serverApplicationOptions.ServerTagName; + // focusCacheInfos.OnLineStatus = true; + + // _freeRedisProvider.Instance.HSet(RedisConst.CacheAllFocusInfoHashKey, focusAddress, focusCacheInfos); + + // //连接成功以后,通知信息到后台 + // Dictionary channelMessage = new Dictionary(); + // channelMessage.Add(ServiceCommunicationTypeEnum.FocusStatusReceived, focusCacheInfos.Serialize()); + // _= _producerService.ProduceAsync(KafkaTopicConsts.ServiceCommunicationChannelTopic, channelMessage); + //} + + //todo 后续可以考虑Redis的bitmap做日活签到。 + // TODO:要注意保存树模型的时时标字段,如果没有时标,则取接受时间做兼容 await e.InvokeNext(); } @@ -97,21 +150,23 @@ namespace JiShe.CollectBus.Plugins } public async Task OnTcpClosed(ITcpSession client, ClosedEventArgs e) - { - //todo: 删除Redis缓存 - var tcpSessionClient = (ITcpSessionClient)client; - //var entity = await _deviceRepository.FindAsync(a => a.ClientId == tcpSessionClient.Id); - //if (entity != null) - //{ - // entity.UpdateByOnClosed(); - // await _deviceRepository.UpdateAsync(entity); - //} - //else - //{ - //_logger.LogWarning($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}已关闭连接,但采集程序检索失败"); - //} + { + var tcpSessionClient = (ITcpSessionClient)client; _logger.LogWarning($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}已关闭连接"); + //查找Redis缓存的集中器信息 + var focusCacheInfos = _freeRedisProvider.Instance.HGet(RedisConst.CacheAllFocusInfoHashKey, tcpSessionClient.Id); + if (focusCacheInfos != null) + { + + //缓存更新状态 + focusCacheInfos.LastSurvivalTime = DateTime.Now; + focusCacheInfos.ServerTagName = _serverApplicationOptions.ServerTagName; + focusCacheInfos.OnLineStatus = false; + + _freeRedisProvider.Instance.HSet(RedisConst.CacheAllFocusInfoHashKey, tcpSessionClient.Id, focusCacheInfos); + } + await e.InvokeNext(); } diff --git a/services/JiShe.CollectBus.Application/RedisDataCache/RedisDataCacheService.cs b/services/JiShe.CollectBus.Application/RedisDataCache/RedisDataCacheService.cs deleted file mode 100644 index c59fb4f..0000000 --- a/services/JiShe.CollectBus.Application/RedisDataCache/RedisDataCacheService.cs +++ /dev/null @@ -1,994 +0,0 @@ -using Confluent.Kafka; -using FreeRedis; -using JiShe.CollectBus.Application.Contracts; -using JiShe.CollectBus.Common.Extensions; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.Common.Models; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using JiShe.CollectBus.FreeRedis; -using Volo.Abp.DependencyInjection; -using static FreeSql.Internal.GlobalFilter; -using static System.Runtime.InteropServices.JavaScript.JSType; -using static Volo.Abp.UI.Navigation.DefaultMenuNames.Application; -using JiShe.CollectBus.IotSystems.Ammeters; -using System.IO.Pipelines; - -namespace JiShe.CollectBus.RedisDataCache -{ - /// - /// 数据缓存服务接口 - /// - public class RedisDataCacheService : IRedisDataCacheService, ITransientDependency - { - private readonly IFreeRedisProvider _freeRedisProvider; - private readonly ILogger _logger; - private RedisClient Instance { get; set; } - - /// - /// 数据缓存服务接口 - /// - /// - /// - public RedisDataCacheService(IFreeRedisProvider freeRedisProvider, - ILogger logger) - { - this._freeRedisProvider = freeRedisProvider; - this._logger = logger; - - Instance = _freeRedisProvider.Instance; - } - - /// - /// 单个添加数据 - /// - /// - /// Set索引缓存Key - /// hash缓存Key - /// 待缓存数据 - /// - public async Task InsertDataAsync( - string redisSetIndexCacheKey, - string redisDeviceInfoHashCacheKey, - T data) where T : DeviceCacheBasicModel - { - // 参数校验增强 - if (data == null || string.IsNullOrWhiteSpace(redisSetIndexCacheKey) - || string.IsNullOrWhiteSpace(redisDeviceInfoHashCacheKey)) - { - _logger.LogError($"{nameof(InsertDataAsync)} 参数异常,-101"); - return; - } - - // 使用事务保证原子性 - using (var trans = Instance.Multi()) - { - // Set索引缓存 - trans.SAdd(redisSetIndexCacheKey, $"{data.TimeDensity.ToString().PadLeft(2, '0')}:{data.FocusAddress}"); - - //检查HSet是否存在对应的信息,如果存在,需要进一步检查value是否已经存在,如果存在则更新,不存在则添加 - var oldValue = Instance.HGet>(redisDeviceInfoHashCacheKey, data.FocusAddress); - if (oldValue == null || oldValue.Count <= 0)//直接添加 - { - //设备信息缓存 - trans.HSet(redisDeviceInfoHashCacheKey, data.FocusAddress, data); - } - else - { - // 移除缓存中同类型旧数据 - oldValue.RemoveAll(device => device.MeterType == data.MeterType); - - //添加新数据 - oldValue.Add(data); - - //设备信息缓存 - trans.HSet(redisDeviceInfoHashCacheKey, data.FocusAddress, oldValue); - } - - var results = trans.Exec(); - - if (results == null || results.Length <= 0) - { - _logger.LogError($"{nameof(InsertDataAsync)} 添加事务提交失败,-102"); - } - } - - await Task.CompletedTask; - } - - /// - /// 批量添加数据 - /// - /// - /// Set索引缓存Key - /// hash缓存Key - /// 待缓存数据集合 - /// - public async Task BatchInsertDataAsync( - string redisSetIndexCacheKey, - string redisDeviceInfoHashCacheKey, - Dictionary> items) where T : DeviceCacheBasicModel - { - if (items == null - || items.Count() <= 0 - || string.IsNullOrWhiteSpace(redisSetIndexCacheKey) - || string.IsNullOrWhiteSpace(redisDeviceInfoHashCacheKey) - ) - { - _logger.LogError($"{nameof(BatchInsertDataAsync)} 参数异常,-101"); - return; - } - - const int BATCH_SIZE = 1000; // 每批1000条 - var semaphore = new SemaphoreSlim(Environment.ProcessorCount * 2); - - foreach (var batch in items.Batch(BATCH_SIZE)) - { - await semaphore.WaitAsync(); - - _ = Task.Run(() => - { - using (var pipe = Instance.StartPipe()) - { - foreach (var item in batch) - { - // Set索引缓存 - pipe.SAdd(redisSetIndexCacheKey, $"{item.Value.First().TimeDensity.ToString().PadLeft(2, '0')}:{item.Value.First().FocusAddress}"); - - //检查HSet是否存在对应的信息,如果存在,需要进一步检查value是否已经存在,如果存在则更新,不存在则添加 - var oldValue = Instance.HGet>(redisDeviceInfoHashCacheKey, item.Key); - if (oldValue == null || oldValue.Count <= 0)//直接添加 - { - //设备信息缓存 - pipe.HSet(redisDeviceInfoHashCacheKey, item.Key, item.Value); - } - else - { - // 移除缓存中同类型旧数据 - oldValue.RemoveAll(device => device.MeterType == item.Value[0].MeterType); - - //添加新数据 - oldValue.AddRange(item.Value); - - //设备信息缓存 - pipe.HSet(redisDeviceInfoHashCacheKey, item.Key, oldValue); - } - } - pipe.EndPipe(); - } - semaphore.Release(); - }); - } - - await Task.CompletedTask; - } - - /// - /// 删除缓存信息 - /// - /// - /// Set索引缓存Key - /// hash缓存Key - /// 已缓存数据 - /// - public async Task RemoveCacheDataAsync( - string redisSetIndexCacheKey, - string redisDeviceInfoHashCacheKey, - T data) where T : DeviceCacheBasicModel - { - if (data == null - || string.IsNullOrWhiteSpace(redisDeviceInfoHashCacheKey) - || string.IsNullOrWhiteSpace(redisSetIndexCacheKey) ) - { - _logger.LogError($"{nameof(RemoveCacheDataAsync)} 参数异常,-101"); - return; - } - - const string luaScript = @" - local hashCacheKey = KEYS[1] - local setIndexCacheKey = KEYS[2] - local focusAddress = ARGV[1] - local scoreValue = ARGV[2] - - local deleted = 0 - if redis.call('HDEL', hashCacheKey, focusAddress) > 0 then - deleted = 1 - end - - redis.call('SREM', setIndexCacheKey, scoreValue) - return deleted - "; - - var keys = new[] - { - redisDeviceInfoHashCacheKey, - redisSetIndexCacheKey - }; - - var result = await Instance.EvalAsync(luaScript, keys, new object[] { data.FocusAddress , data.ScoreValue}); - - if ((int)result == 0) - { - _logger.LogError($"{nameof(RemoveCacheDataAsync)} 删除指定Key{redisDeviceInfoHashCacheKey}的{data.MemberId}数据失败,-102"); - } - } - - /// - /// 修改缓存信息,映射关系未发生改变 - /// - /// - /// 主数据存储Hash缓存Key - /// Set索引缓存Key - /// ZSET索引缓存Key - /// 待修改缓存数据 - /// - public async Task ModifyDataAsync( - string redisHashCacheKey, - string redisSetIndexCacheKey, - string redisZSetScoresIndexCacheKey, - T newData) where T : DeviceCacheBasicModel - { - if (newData == null - || string.IsNullOrWhiteSpace(redisHashCacheKey) - || string.IsNullOrWhiteSpace(redisSetIndexCacheKey) - || string.IsNullOrWhiteSpace(redisZSetScoresIndexCacheKey)) - { - _logger.LogError($"{nameof(ModifyDataAsync)} 参数异常,-101"); - return; - } - - var luaScript = @" - local hashCacheKey = KEYS[1] - local member = ARGV[1] - local newData = ARGV[2] - - -- 校验存在性 - if redis.call('HEXISTS', hashCacheKey, member) == 0 then - return 0 - end - - -- 更新主数据 - redis.call('HSET', hashCacheKey, member, newData) - - return 1 - "; - - - var result = await Instance.EvalAsync(luaScript, - new[] - { - redisHashCacheKey - }, - new object[] - { - newData.MemberId, - newData.Serialize() - }); - - if ((int)result == 0) - { - _logger.LogError($"{nameof(ModifyDataAsync)} 更新指定Key{redisHashCacheKey}的{newData.MemberId}数据失败,-102"); - } - } - - /// - /// 修改缓存信息,映射关系已经改变 - /// - /// - /// 主数据存储Hash缓存Key - /// Set索引缓存Key - /// 旧的映射关系 - /// ZSET索引缓存Key - /// 待修改缓存数据 - /// - public async Task ModifyDataAsync( - string redisHashCacheKey, - string redisSetIndexCacheKey, - string oldMemberId, - string redisZSetScoresIndexCacheKey, - T newData) where T : DeviceCacheBasicModel - { - if (newData == null - || string.IsNullOrWhiteSpace(redisHashCacheKey) - || string.IsNullOrWhiteSpace(redisSetIndexCacheKey) - || string.IsNullOrWhiteSpace(oldMemberId) - || string.IsNullOrWhiteSpace(redisZSetScoresIndexCacheKey)) - { - _logger.LogError($"{nameof(ModifyDataAsync)} 参数异常,-101"); - return; - } - - var luaScript = @" - local hashCacheKey = KEYS[1] - local setIndexCacheKey = KEYS[2] - local zsetScoresIndexCacheKey = KEYS[3] - local member = ARGV[1] - local oldMember = ARGV[2] - local newData = ARGV[3] - local newScore = ARGV[4] - - -- 校验存在性 - if redis.call('HEXISTS', hashCacheKey, oldMember) == 0 then - return 0 - end - - -- 删除旧数据 - redis.call('HDEL', hashCacheKey, oldMember) - - -- 插入新主数据 - redis.call('HSET', hashCacheKey, member, newData) - - -- 处理变更 - if newScore ~= '' then - -- 删除旧索引 - redis.call('SREM', setIndexCacheKey, oldMember) - redis.call('ZREM', zsetScoresIndexCacheKey, oldMember) - - -- 添加新索引 - redis.call('SADD', setIndexCacheKey, member) - redis.call('ZADD', zsetScoresIndexCacheKey, newScore, member) - end - - return 1 - "; - - var result = await Instance.EvalAsync(luaScript, - new[] - { - redisHashCacheKey, - redisSetIndexCacheKey, - redisZSetScoresIndexCacheKey - }, - new object[] - { - newData.MemberId, - oldMemberId, - newData.Serialize(), - newData.ScoreValue.ToString() ?? "", - }); - - if ((int)result == 0) - { - _logger.LogError($"{nameof(ModifyDataAsync)} 更新指定Key{redisHashCacheKey}的{newData.MemberId}数据失败,-102"); - } - } - - - /// - /// 通过集中器与表计信息排序索引获取指定集中器号集合数据 - /// - /// - /// 主数据存储Hash缓存Key - /// 集中器与表计信息排序索引ZSET缓存Key - /// 集中器Id - /// 分页尺寸 - /// 最后一个索引 - /// 最后一个唯一标识 - /// 排序方式 - /// - public async Task> GetPagedData( - string redisCacheKey, - string redisCacheFocusScoresIndexKey, - IEnumerable focusIds, - int pageSize = 10, - decimal? lastScore = null, - string lastMember = null, - bool descending = true) - where T : DeviceCacheBasicModel - { - throw new Exception(); - } - - - /// - /// 通过集中器与表计信息排序索引获取单个数据 - /// - /// - /// 主数据存储Hash缓存Key - /// ZSET索引缓存Key - /// ZSET索引的原始数据,例如集中地址和点位的组合 - /// 分页尺寸 - /// 最后一个索引 - /// 最后一个唯一标识 - /// 排序方式 - /// - public async Task> GetSingleData( - string redisHashCacheKey, - string redisZSetScoresIndexCacheKey, - string scoreValueRawData, - int pageSize = 10, - decimal? lastScore = null, - string lastMember = null, - bool descending = true) - where T : DeviceCacheBasicModel - { - // 参数校验 - if (string.IsNullOrWhiteSpace(redisHashCacheKey) || - string.IsNullOrWhiteSpace(redisZSetScoresIndexCacheKey) || - string.IsNullOrWhiteSpace(scoreValueRawData)) - { - _logger.LogError($"{nameof(GetSingleData)} 参数异常,-101"); - return new BusCacheGlobalPagedResult { Items = new List() }; - } - - // 解析原始数据 - var rawDataArray = scoreValueRawData.Split(':'); - if (rawDataArray.Length != 2) - { - _logger.LogError($"{nameof(GetSingleData)} scoreValueRawData格式错误,应为[focusAddress:point]"); - return new BusCacheGlobalPagedResult { Items = new List() }; - } - - // 计算Score值 - long scoreValue; - try - { - var focusAddress = rawDataArray[0]; - var point = Convert.ToInt32(rawDataArray[1]); - scoreValue = CommonHelper.GetFocusScores(focusAddress, point); - } - catch (Exception ex) - { - _logger.LogError(ex, $"{nameof(GetSingleData)} 计算Score值失败"); - return new BusCacheGlobalPagedResult { Items = new List() }; - } - - // Lua脚本:原子化查询操作 - const string luaScript = @" - local zsetKey = KEYS[1] - local hashKey = KEYS[2] - local targetScore = ARGV[1] - - -- 精确匹配Score并获取第一个成员 - local members = redis.call('ZRANGEBYSCORE', zsetKey, targetScore, targetScore, 'LIMIT', 0, 1) - if #members == 0 then - return nil - end - - -- 获取哈希表数据 - local member = members[1] - local data = redis.call('HGET', hashKey, member) - return {member, data}"; - - try - { - // 执行脚本 - var result = await Instance.EvalAsync( - luaScript, - new[] { redisZSetScoresIndexCacheKey, redisHashCacheKey }, - new object[] { scoreValue }); - - // 处理空结果 - if (result == null) - return new BusCacheGlobalPagedResult { Items = new List() }; - - // 解析Redis返回数据 - var resultArray = (object[])result; - var memberId = (string)resultArray[0]; - var dataStr = (string)resultArray[1]; - - // 反序列化数据 - var data = BusJsonSerializer.Deserialize(dataStr); - if (data == null) - { - _logger.LogError($"{nameof(GetSingleData)} 反序列化失败,MemberId: {memberId}"); - return new BusCacheGlobalPagedResult { Items = new List() }; - } - - // 构造返回结果 - return new BusCacheGlobalPagedResult - { - Items = new List { data }, - TotalCount = 1, - PageSize = 1, - HasNext = false, - NextScore = null, - NextMember = null - }; - } - catch (Exception ex) - { - _logger.LogError(ex, $"{nameof(GetSingleData)} Redis操作异常"); - return new BusCacheGlobalPagedResult { Items = new List() }; - } - } - - /// - /// 通过ZSET索引获取数据,支持10万级别数据处理,控制在13秒以内。 - /// - /// - /// 主数据存储Hash缓存Key - /// ZSET索引缓存Key - /// 分页尺寸 - /// 最后一个索引 - /// 最后一个唯一标识 - /// 排序方式 - /// - public async Task> GetAllPagedData( - string redisHashCacheKey, - string redisZSetScoresIndexCacheKey, - int pageSize = 1000, - decimal? lastScore = null, - string lastMember = null, - bool descending = true) - where T : DeviceCacheBasicModel - { - // 参数校验增强 - if (string.IsNullOrWhiteSpace(redisHashCacheKey) || - string.IsNullOrWhiteSpace(redisZSetScoresIndexCacheKey)) - { - _logger.LogError($"{nameof(GetAllPagedData)} 参数异常,-101"); - return new BusCacheGlobalPagedResult { Items = new List() }; - } - - pageSize = Math.Clamp(pageSize, 1, 10000); - - var luaScript = @" - local command = ARGV[1] - local range_start = ARGV[2] - local range_end = ARGV[3] - local limit = tonumber(ARGV[4]) - local last_score = ARGV[5] - local last_member = ARGV[6] - - -- 获取原始数据 - local members - if command == 'ZRANGEBYSCORE' then - members = redis.call(command, KEYS[1], range_start, range_end, 'WITHSCORES', 'LIMIT', 0, limit * 2) - else - members = redis.call('ZREVRANGEBYSCORE', KEYS[1], range_start, range_end, 'WITHSCORES', 'LIMIT', 0, limit * 2) - end - - -- 过滤数据 - local filtered_members = {} - local count = 0 - for i = 1, #members, 2 do - local member = members[i] - local score = members[i+1] - local include = true - if last_score ~= '' and last_member ~= '' then - if command == 'ZRANGEBYSCORE' then - -- 升序:score > last_score 或 (score == last_score 且 member > last_member) - if score == last_score then - include = member > last_member - else - include = tonumber(score) > tonumber(last_score) - end - else - -- 降序:score < last_score 或 (score == last_score 且 member < last_member) - if score == last_score then - include = member < last_member - else - include = tonumber(score) < tonumber(last_score) - end - end - end - if include then - table.insert(filtered_members, member) - table.insert(filtered_members, score) - count = count + 1 - if count >= limit then - break - end - end - end - - -- 提取有效数据 - local result_members, result_scores = {}, {} - for i=1,#filtered_members,2 do - table.insert(result_members, filtered_members[i]) - table.insert(result_scores, filtered_members[i+1]) - end - - if #result_members == 0 then return {0,{},{},{}} end - - -- 获取Hash数据 - local hash_data = redis.call('HMGET', KEYS[2], unpack(result_members)) - return {#result_members, result_members, result_scores, hash_data}"; - - // 调整范围构造逻辑(移除排他符号) - string rangeStart, rangeEnd; - if (descending) - { - rangeStart = lastScore.HasValue ? lastScore.Value.ToString() : "+inf"; - rangeEnd = "-inf"; - } - else - { - rangeStart = lastScore.HasValue ? lastScore.Value.ToString() : "-inf"; - rangeEnd = "+inf"; - } - - var scriptResult = (object[])await Instance.EvalAsync(luaScript, - new[] { redisZSetScoresIndexCacheKey, redisHashCacheKey }, - new object[] - { - descending ? "ZREVRANGEBYSCORE" : "ZRANGEBYSCORE", - rangeStart, - rangeEnd, - (pageSize + 1).ToString(), // 获取pageSize+1条以判断是否有下一页 - lastScore?.ToString() ?? "", - lastMember ?? "" - }); - - if ((long)scriptResult[0] == 0) - return new BusCacheGlobalPagedResult { Items = new List() }; - - // 处理结果集 - var members = ((object[])scriptResult[1]).Cast().ToList(); - var scores = ((object[])scriptResult[2]).Cast().Select(decimal.Parse).ToList(); - var hashData = ((object[])scriptResult[3]).Cast().ToList(); - - var validItems = members.AsParallel() - .Select((m, i) => - { - try { return BusJsonSerializer.Deserialize(hashData[i]); } - catch { return null; } - }) - .Where(x => x != null) - .ToList(); - - var hasNext = validItems.Count > pageSize; - var actualItems = hasNext ? validItems.Take(pageSize) : validItems; - - //分页锚点索引 - decimal? nextScore = null; - string nextMember = null; - if (hasNext && actualItems.Any()) - { - var lastIndex = actualItems.Count() - 1; // 使用actualItems的最后一个索引 - nextScore = scores[lastIndex]; - nextMember = members[lastIndex]; - } - - return new BusCacheGlobalPagedResult - { - Items = actualItems.ToList(), - HasNext = hasNext, - NextScore = nextScore, - NextMember = nextMember, - TotalCount = await GetTotalCount(redisZSetScoresIndexCacheKey), - PageSize = pageSize, - }; - } - - - /// - /// 通过ZSET索引获取数据,支持10万级别数据处理,控制在13秒以内。 - /// - /// - /// ZSET索引缓存Key - /// 主数据存储Hash缓存Key - /// 分页尺寸 - /// 最后一个索引 - /// 最后一个唯一标识 - /// 排序方式 - /// - public async Task> GetAllPagedData2( - string redisCacheDeviceGroupSetIndexKey, - string redisCacheDeviceInfoHashKey, - int pageSize = 1000, - decimal? lastScore = null, - string lastMember = null, - bool descending = true) - where T : DeviceCacheBasicModel - { - // 参数校验增强 - if (string.IsNullOrWhiteSpace(redisCacheDeviceInfoHashKey) || - string.IsNullOrWhiteSpace(redisCacheDeviceGroupSetIndexKey)) - { - _logger.LogError($"{nameof(GetAllPagedData)} 参数异常,-101"); - return new BusCacheGlobalPagedResult { Items = new List() }; - } - - pageSize = Math.Clamp(pageSize, 1, 10000); - - var luaScript = @" - local command = ARGV[1] - local range_start = ARGV[2] - local range_end = ARGV[3] - local limit = tonumber(ARGV[4]) - local last_score = ARGV[5] - local last_member = ARGV[6] - - -- 获取原始数据 - local members - if command == 'ZRANGEBYSCORE' then - members = redis.call(command, KEYS[1], range_start, range_end, 'WITHSCORES', 'LIMIT', 0, limit * 2) - else - members = redis.call('ZREVRANGEBYSCORE', KEYS[1], range_start, range_end, 'WITHSCORES', 'LIMIT', 0, limit * 2) - end - - -- 过滤数据 - local filtered_members = {} - local count = 0 - for i = 1, #members, 2 do - local member = members[i] - local score = members[i+1] - local include = true - if last_score ~= '' and last_member ~= '' then - if command == 'ZRANGEBYSCORE' then - -- 升序:score > last_score 或 (score == last_score 且 member > last_member) - if score == last_score then - include = member > last_member - else - include = tonumber(score) > tonumber(last_score) - end - else - -- 降序:score < last_score 或 (score == last_score 且 member < last_member) - if score == last_score then - include = member < last_member - else - include = tonumber(score) < tonumber(last_score) - end - end - end - if include then - table.insert(filtered_members, member) - table.insert(filtered_members, score) - count = count + 1 - if count >= limit then - break - end - end - end - - -- 提取有效数据 - local result_members, result_scores = {}, {} - for i=1,#filtered_members,2 do - table.insert(result_members, filtered_members[i]) - table.insert(result_scores, filtered_members[i+1]) - end - - if #result_members == 0 then return {0,{},{},{}} end - - -- 获取Hash数据 - local hash_data = redis.call('HMGET', KEYS[2], unpack(result_members)) - return {#result_members, result_members, result_scores, hash_data}"; - - // 调整范围构造逻辑(移除排他符号) - string rangeStart, rangeEnd; - if (descending) - { - rangeStart = lastScore.HasValue ? lastScore.Value.ToString() : "+inf"; - rangeEnd = "-inf"; - } - else - { - rangeStart = lastScore.HasValue ? lastScore.Value.ToString() : "-inf"; - rangeEnd = "+inf"; - } - - var scriptResult = (object[])await Instance.EvalAsync(luaScript, - new[] { redisCacheDeviceGroupSetIndexKey, redisCacheDeviceInfoHashKey }, - new object[] - { - descending ? "ZREVRANGEBYSCORE" : "ZRANGEBYSCORE", - rangeStart, - rangeEnd, - (pageSize + 1).ToString(), // 获取pageSize+1条以判断是否有下一页 - lastScore?.ToString() ?? "", - lastMember ?? "" - }); - - if ((long)scriptResult[0] == 0) - return new BusCacheGlobalPagedResult { Items = new List() }; - - // 处理结果集 - var members = ((object[])scriptResult[1]).Cast().ToList(); - var scores = ((object[])scriptResult[2]).Cast().Select(decimal.Parse).ToList(); - var hashData = ((object[])scriptResult[3]).Cast().ToList(); - - var validItems = members.AsParallel() - .Select((m, i) => - { - try { return BusJsonSerializer.Deserialize(hashData[i]); } - catch { return null; } - }) - .Where(x => x != null) - .ToList(); - - var hasNext = validItems.Count > pageSize; - var actualItems = hasNext ? validItems.Take(pageSize) : validItems; - - //分页锚点索引 - decimal? nextScore = null; - string nextMember = null; - if (hasNext && actualItems.Any()) - { - var lastIndex = actualItems.Count() - 1; // 使用actualItems的最后一个索引 - nextScore = scores[lastIndex]; - nextMember = members[lastIndex]; - } - - return new BusCacheGlobalPagedResult - { - Items = actualItems.ToList(), - HasNext = hasNext, - NextScore = nextScore, - NextMember = nextMember, - TotalCount = await GetTotalCount(redisCacheDeviceGroupSetIndexKey), - PageSize = pageSize, - }; - } - - - /// - /// 游标分页查询 - /// - /// 排序索引ZSET缓存Key - /// 分页数量 - /// 上一个索引 - /// 上一个标识 - /// 排序方式 - /// - private async Task<(List Members, bool HasNext, decimal? NextScore, string NextMember)> GetPagedMembers( - string redisZSetScoresIndexCacheKey, - int pageSize, - decimal? lastScore, - string lastMember, - bool descending) - { - // 根据排序方向初始化参数 - long initialScore = descending ? long.MaxValue : 0; - decimal? currentScore = lastScore ?? initialScore; - string currentMember = lastMember; - var members = new List(pageSize + 1); - - // 使用游标分页查询 - while (members.Count < pageSize + 1 && currentScore.HasValue) - { - var (batch, hasMore) = await GetNextBatch( - redisZSetScoresIndexCacheKey, - pageSize + 1 - members.Count, - currentScore.Value, - currentMember, - descending); - - if (!batch.Any()) break; - - members.AddRange(batch); - - // 更新游标 - currentMember = batch.LastOrDefault(); - currentScore = await GetNextScore(redisZSetScoresIndexCacheKey, currentMember, descending); - } - - // 处理分页结果 - bool hasNext = members.Count > pageSize; - var resultMembers = members.Take(pageSize).ToList(); - - return ( - resultMembers, - hasNext, - currentScore, - currentMember - ); - } - - /// - /// 批量获取指定分页的数据 - /// - /// - /// Hash表缓存key - /// Hash表字段集合 - /// - private async Task> BatchGetData( - string redisHashCacheKey, - IEnumerable members) - where T : DeviceCacheBasicModel - { - using var pipe = Instance.StartPipe(); - - foreach (var member in members) - { - pipe.HGet(redisHashCacheKey, member); - } - - var results = pipe.EndPipe(); - return await Task.FromResult(members.Zip(results, (k, v) => new { k, v }) - .ToDictionary(x => x.k, x => (T)x.v)); - } - - /// - /// 处理下一个分页数据 - /// - /// - /// - /// - /// - /// - /// - private async Task<(string[] Batch, bool HasMore)> GetNextBatch( - string zsetKey, - int limit, - decimal score, - string excludeMember, - bool descending) - { - var query = descending - ? await Instance.ZRevRangeByScoreAsync( - zsetKey, - max: score, - min: 0, - offset: 0, - count: limit) - : await Instance.ZRangeByScoreAsync( - zsetKey, - min: score, - max: long.MaxValue, - offset: 0, - count: limit); - - return (query, query.Length >= limit); - } - - /// - /// 获取下一页游标 - /// - /// 排序索引ZSET缓存Key - /// 最后一个唯一标识 - /// 排序方式 - /// - private async Task GetNextScore( - string redisZSetScoresIndexCacheKey, - string lastMember, - bool descending) - { - if (string.IsNullOrEmpty(lastMember)) return null; - - var score = await Instance.ZScoreAsync(redisZSetScoresIndexCacheKey, lastMember); - if (!score.HasValue) return null; - - // 根据排序方向调整score - return descending - ? score.Value - 1 // 降序时下页查询小于当前score - : score.Value + 1; // 升序时下页查询大于当前score - } - - /// - /// 获取指定ZSET区间内的总数量 - /// - /// - /// - /// - /// - public async Task GetCount(string zsetKey, long min, long max) - { - // 缓存计数优化 - var cacheKey = $"{zsetKey}_count_{min}_{max}"; - var cached = await Instance.GetAsync(cacheKey); - - if (cached.HasValue) - return cached.Value; - - var count = await Instance.ZCountAsync(zsetKey, min, max); - await Instance.SetExAsync(cacheKey, 60, count); // 缓存60秒 - return count; - } - - /// - /// 获取指定ZSET的总数量 - /// - /// - /// - private async Task GetTotalCount(string redisZSetScoresIndexCacheKey) - { - // 缓存计数优化 - var cacheKey = $"{redisZSetScoresIndexCacheKey}_total_count"; - var cached = await Instance.GetAsync(cacheKey); - - if (cached.HasValue) - return cached.Value; - - var count = await Instance.ZCountAsync(redisZSetScoresIndexCacheKey, 0, decimal.MaxValue); - await Instance.SetExAsync(cacheKey, 30, count); // 缓存30秒 - return count; - } - } -} diff --git a/services/JiShe.CollectBus.Application/Samples/SampleAppService.cs b/services/JiShe.CollectBus.Application/Samples/SampleAppService.cs index c6340ed..19fe3d7 100644 --- a/services/JiShe.CollectBus.Application/Samples/SampleAppService.cs +++ b/services/JiShe.CollectBus.Application/Samples/SampleAppService.cs @@ -1,59 +1,39 @@ -using Confluent.Kafka; -using DeviceDetectorNET.Parser.Device; -using FreeSql.Internal.CommonProvider; -using JiShe.CollectBus.Ammeters; -using JiShe.CollectBus.Application.Contracts; +using JiShe.CollectBus.Ammeters; using JiShe.CollectBus.Common.Consts; using JiShe.CollectBus.Common.DeviceBalanceControl; -using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.Common.Extensions; using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.FreeSql; -using JiShe.CollectBus.IoTDB.Context; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IoTDB.Model; -using JiShe.CollectBus.IoTDB.Options; -using JiShe.CollectBus.IoTDB.Provider; -using JiShe.CollectBus.IotSystems.Ammeters; using JiShe.CollectBus.IotSystems.Devices; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; using JiShe.CollectBus.IotSystems.PrepayModel; -using JiShe.CollectBus.Kafka.Attributes; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Kafka.Producer; using JiShe.CollectBus.Protocol.Interfaces; +using JiShe.ServicePro.FreeSqlProvider; +using JiShe.ServicePro.IoTDBManagement.Options; +using JiShe.ServicePro.IoTDBManagement.SessionPools; +using JiShe.ServicePro.Kafka.Internal; +using JiShe.ServicePro.Kafka.Producer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.Metrics; -using System.Threading.Tasks; -using TouchSocket.Core; using TouchSocket.Sockets; -using static IdentityModel.ClaimComparer; namespace JiShe.CollectBus.Samples; public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaSubscribe { private readonly ILogger _logger; - private readonly IIoTDbProvider _iotDBProvider; - private readonly IoTDBRuntimeContext _dbContext; + private readonly IoTDBSessionPoolProvider _iotDBProvider; private readonly IoTDbOptions _options; private readonly IRedisDataCacheService _redisDataCacheService; private readonly IProducerService _producerService; private readonly ITcpService _tcpService; - public SampleAppService(IIoTDbProvider iotDBProvider, IOptions options, - IoTDBRuntimeContext dbContext, ILogger logger, IRedisDataCacheService redisDataCacheService, IProducerService producerService, ITcpService tcpService) + public SampleAppService(IoTDBSessionPoolProvider iotDBProvider, IOptions options, + ILogger logger, IRedisDataCacheService redisDataCacheService, IProducerService producerService, ITcpService tcpService) { _iotDBProvider = iotDBProvider; _options = options.Value; - _dbContext = dbContext; _logger = logger; _redisDataCacheService = redisDataCacheService; _producerService = producerService; @@ -73,26 +53,14 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS List values = new List() { $"{dataTime:yy}", $"{dataTime:MM}", $"{dataTime:dd}", $"{dataTime:HH}", $"{dataTime:mm}", }; - ElectricityMeterTreeModel meter = new ElectricityMeterTreeModel() - { - SystemName = "energy", - DeviceId = "402440506s", - DeviceType = "Ammeter", - Current = 10, - MeterModel = "DDZY-1980", - ProjectId = "10059", - Voltage = 10, - IssuedMessageHexString = "messageHexString", - Timestamps = testTime// DateTimeOffset.UtcNow.ToUnixTimeNanoseconds()//testTime.GetDateTimeOffset().ToUnixTimeNanoseconds(), - }; + //ElectricityMeterTreeModelExtension.GetCurrent() //SourceEntityAccessorFactory.SetCurrent(meter); //ElectricityMeterTreeModelAccessor. //TableModelSingleMeasuringEntityExtension //TableModelSingleMeasuringEntityAccessor.GetSystemName(meter); - //ElectricityMeterAccessor - await _iotDBProvider.InsertAsync(meter); + //ElectricityMeterAccessor } /// @@ -103,51 +71,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS public async Task UseTableSessionPool(DateTime time) { var testTime = time; - ElectricityMeterTreeModel meter2 = new ElectricityMeterTreeModel() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "Ammeter", - Current = 10, - MeterModel = "DDZY-1980", - ProjectId = "10059", - Voltage = 10, - Timestamps = DateTimeOffset.Now.ToUnixTimeNanoseconds(), - }; - - await _iotDBProvider.GetSessionPool(false).InsertAsync(meter2); - - ElectricityMeter meter = new ElectricityMeter() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "Ammeter", - Current = 10, - MeterModel = "DDZY-1980", - ProjectId = "10059", - Voltage = 10, - CurrentdDateTime = DateTime.Now, - Timestamps = DateTimeOffset.Now.ToUnixTimeNanoseconds(), - }; - await _iotDBProvider.GetSessionPool(true).InsertAsync(meter); - - QueryCondition conditions = new QueryCondition() - { - Field = "DeviceId", - Operator = "=", - Value = meter.DeviceId - }; - - - var query = new IoTDBQueryOptions() - { - TableNameOrTreePath = nameof(ElectricityMeter), - PageIndex = 1, - PageSize = 1, - Conditions = new List() { conditions }, - }; - - var pageResult = await _iotDBProvider.GetSessionPool(true).QueryAsync(query); + } @@ -159,42 +83,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS public async Task UseTableSessionPool3(DateTime time) { var testTime = time; - ElectricityMeterTreeModel meter2 = new ElectricityMeterTreeModel() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "Ammeter", - Current = 10, - MeterModel = "DDZY-1980", - ProjectId = "10059", - Voltage = 10, - IssuedMessageHexString = "dsdfsfd", - Timestamps = DateTimeOffset.UtcNow.ToUnixTimeNanoseconds(), - - }; - - await _iotDBProvider.InsertAsync(meter2); - - - ElectricityMeter meter3 = new ElectricityMeter() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "Ammeter", - Current = 10, - MeterModel = "DDZY-1980", - ProjectId = "10059", - Voltage = 10, - Currentd = 22, - IssuedMessageHexString = "dsdfsfd", - Timestamps = DateTimeOffset.Now.ToUnixTimeNanoseconds(), - }; - - //var dd = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - //var dd3 = DateTimeOffset.Now.ToUnixTimeMicroseconds(); - //var dd2 = DateTimeOffset.Now.ToUnixTimeNanoseconds(); - - await _iotDBProvider.InsertAsync(meter3); + } /// @@ -205,20 +94,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS [HttpGet] public async Task TestTreeModelSingleMeasuringEntity(string measuring, string value, DateTime time) { - time = DateTime.Now; - //System.Reflection.PropertyInfo; - //System.Reflection.FieldInfo - //TreeModelSingleMeasuringEntityAccessor - var meter = new TreeModelSingleMeasuringEntity() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "1", - ProjectId = "10059", - Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(), - SingleMeasuring = (measuring, 34.534m) - }; - await _iotDBProvider.InsertAsync(meter); + } /// @@ -229,17 +105,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS [HttpGet] public async Task TestTreeModelSingleMeasuringEntity2(string measuring, int value, DateTime time) { - time = DateTime.Now; - var meter = new TreeModelSingleMeasuringEntity() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "Ammeter", - ProjectId = "10059", - Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(), - SingleMeasuring = (measuring, true) - }; - await _iotDBProvider.InsertAsync(meter); + } /// @@ -251,37 +117,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS public async Task TestTreeModelSingleMeasuringEntityQuery() { - var time = DateTime.Now; - - var meter = new TreeModelSingleMeasuringEntity() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "Ammeter", - ProjectId = "10059", - Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(), - SingleMeasuring = ("measuring", true) - }; - - QueryCondition conditions = new QueryCondition() - { - Field = "DeviceId", - Operator = "=", - Value = meter.DeviceId - }; - - - var query = new IoTDBQueryOptions() - { - TableNameOrTreePath = meter.DevicePath, - PageIndex = 1, - PageSize = 1, - Conditions = new List() { conditions }, - }; - - var pageResult = await _iotDBProvider.QueryAsync(query); - - await _iotDBProvider.InsertAsync(meter); + } /// @@ -294,17 +130,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS { time = DateTime.Now; - var meter = new TableModelSingleMeasuringEntity() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "Ammeter", - ProjectId = "10059", - Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(), - SingleColumn = (measuring, value) - }; - _dbContext.UseTableSessionPool = true; - await _iotDBProvider.InsertAsync(meter); + } /// @@ -317,46 +143,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS { time = DateTime.Now; - var meter = new TableModelSingleMeasuringEntity() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "Ammeter", - ProjectId = "10059", - Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(), - SingleColumn = (measuring, value) - }; - _dbContext.UseTableSessionPool = true; - await _iotDBProvider.InsertAsync(meter); - - var meter3 = new TableModelSingleMeasuringEntity() - { - SystemName = "energy", - DeviceId = "402440506", - DeviceType = "Ammeter", - ProjectId = "10059", - Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(), - SingleColumn = ("DeviceResult", true) - }; - _dbContext.UseTableSessionPool = true; - - QueryCondition conditions = new QueryCondition() - { - Field = "DeviceId", - Operator = "=", - Value = meter.DeviceId - }; - - - var query = new IoTDBQueryOptions() - { - TableNameOrTreePath = meter.DevicePath, - PageIndex = 1, - PageSize = 1, - Conditions = new List() { conditions }, - }; - - var pageResult = await _iotDBProvider.QueryAsync(query); + } /// @@ -512,7 +299,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS return aa == null; } - [KafkaSubscribe(ProtocolConst.TESTTOPIC)] + [KafkaSubscribe(KafkaTopicConsts.TESTTOPIC)] public async Task KafkaSubscribeAsync(object obj) { @@ -531,7 +318,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS ArgumentException.ThrowIfNullOrWhiteSpace(input.Address); ArgumentException.ThrowIfNullOrWhiteSpace(input.Frame); input.Frame = input.Frame.Replace(" ", ""); - await _producerService.ProduceAsync(ProtocolConst.TESTSENDTOPIC, input); + await _producerService.ProduceAsync(KafkaTopicConsts.TESTSENDTOPIC, input); return await Task.FromResult(true); } @@ -541,7 +328,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS /// /// /// - [KafkaSubscribe(ProtocolConst.TESTSENDTOPIC), ApiExplorerSettings(IgnoreApi = true)] + [KafkaSubscribe(KafkaTopicConsts.TESTSENDTOPIC), ApiExplorerSettings(IgnoreApi = true)] public async Task KafkaSubscribeTestSendAsync(KafkaSendDto dto) { diff --git a/services/JiShe.CollectBus.Application/Samples/TestAppService.cs b/services/JiShe.CollectBus.Application/Samples/TestAppService.cs index 1975d5a..af8bbde 100644 --- a/services/JiShe.CollectBus.Application/Samples/TestAppService.cs +++ b/services/JiShe.CollectBus.Application/Samples/TestAppService.cs @@ -1,19 +1,24 @@ -using Cassandra; -using JiShe.CollectBus.Cassandra; +using IdentityModel.Client; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.DynamicModule; using JiShe.CollectBus.Interceptors; using JiShe.CollectBus.IotSystems.MessageIssueds; using JiShe.CollectBus.IotSystems.Protocols; +using JiShe.CollectBus.Protocol; using JiShe.CollectBus.Protocol.Interfaces; +using JiShe.CollectBus.Protocol3761; using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using TouchSocket.Sockets; +using Volo.Abp; using Volo.Abp.Modularity; namespace JiShe.CollectBus.Samples; @@ -22,102 +27,104 @@ namespace JiShe.CollectBus.Samples; public class TestAppService : CollectBusAppService { private readonly ILogger _logger; - private readonly ICassandraRepository _messageReceivedCassandraRepository; - private readonly ICassandraProvider _cassandraProvider; + //private readonly ICassandraRepository _messageReceivedCassandraRepository; + //private readonly ICassandraProvider _cassandraProvider; private readonly IProtocolService _protocolService; private readonly IServiceProvider _serviceProvider; private readonly IDynamicModuleManager _dynamicModuleManager; - + private readonly IServiceCollection _serviceDescriptors; public TestAppService( ILogger logger, - ICassandraRepository messageReceivedCassandraRepository, - ICassandraProvider cassandraProvider, IProtocolService protocolService,IServiceProvider serviceProvider, IDynamicModuleManager dynamicModuleManager) + //ICassandraRepository messageReceivedCassandraRepository, + //ICassandraProvider cassandraProvider, + IProtocolService protocolService,IServiceProvider serviceProvider, IDynamicModuleManager dynamicModuleManager, IServiceCollection serviceDescriptors) { _logger = logger; - _messageReceivedCassandraRepository = messageReceivedCassandraRepository; - _cassandraProvider = cassandraProvider; + //_messageReceivedCassandraRepository = messageReceivedCassandraRepository; + //_cassandraProvider = cassandraProvider; _protocolService = protocolService; _serviceProvider = serviceProvider; _dynamicModuleManager = dynamicModuleManager; + _serviceDescriptors = serviceDescriptors; } public async Task AddMessageOfCassandra() { - var stopwatch = Stopwatch.StartNew(); - for (int i = 1; i <= 10000; i++) - { - var str = Guid.NewGuid().ToString(); - await _messageReceivedCassandraRepository.InsertAsync(new MessageIssued - { - ClientId = str, - DeviceNo = i.ToString(), - MessageId = str, - Type = IssuedEventType.Data, - Id = str, - Message = str.GetBytes() - }); - } - stopwatch.Stop(); - _logger.LogWarning($"插入 {10000} 条记录完成,耗时: {stopwatch.ElapsedMilliseconds} 毫秒"); + //var stopwatch = Stopwatch.StartNew(); + //for (int i = 1; i <= 10000; i++) + //{ + // var str = Guid.NewGuid().ToString(); + // await _messageReceivedCassandraRepository.InsertAsync(new MessageIssued + // { + // ClientId = str, + // DeviceNo = i.ToString(), + // MessageId = str, + // Type = IssuedEventType.Data, + // Id = str, + // Message = str.GetBytes() + // }); + //} + //stopwatch.Stop(); + //_logger.LogWarning($"插入 {10000} 条记录完成,耗时: {stopwatch.ElapsedMilliseconds} 毫秒"); } public async Task AddMessageOfBulkInsertCassandra() { - var records = new List(); - var prepared = await _cassandraProvider.Session.PrepareAsync( - $"INSERT INTO {_cassandraProvider.CassandraConfig.Keyspace}.{nameof(MessageIssued)} (id, clientid, message, deviceno,type,messageid) VALUES (?, ?, ?, ?, ?, ?)"); + //var records = new List(); + //var prepared = await _cassandraProvider.Session.PrepareAsync( + // $"INSERT INTO {_cassandraProvider.CassandraConfig.Keyspace}.{nameof(MessageIssued)} (id, clientid, message, deviceno,type,messageid) VALUES (?, ?, ?, ?, ?, ?)"); - for (int i = 1; i <= 100000; i++) - { - var str = Guid.NewGuid().ToString(); - records.Add(new MessageIssued - { - ClientId = str, - DeviceNo = i.ToString(), - MessageId = str, - Type = IssuedEventType.Data, - Id = str, - Message = str.GetBytes() - }); - } - var stopwatch = Stopwatch.StartNew(); - await BulkInsertAsync(_cassandraProvider.Session, prepared, records); - stopwatch.Stop(); - _logger.LogWarning($"插入 {100000} 条记录完成,耗时: {stopwatch.ElapsedMilliseconds} 毫秒"); + //for (int i = 1; i <= 100000; i++) + //{ + // var str = Guid.NewGuid().ToString(); + // records.Add(new MessageIssued + // { + // ClientId = str, + // DeviceNo = i.ToString(), + // MessageId = str, + // Type = IssuedEventType.Data, + // Id = str, + // Message = str.GetBytes() + // }); + //} + //var stopwatch = Stopwatch.StartNew(); + //await BulkInsertAsync(_cassandraProvider.Session, prepared, records); + //stopwatch.Stop(); + //_logger.LogWarning($"插入 {100000} 条记录完成,耗时: {stopwatch.ElapsedMilliseconds} 毫秒"); } - private static async Task BulkInsertAsync(ISession session, PreparedStatement prepared, List records) - { - var tasks = new List(); - var batch = new BatchStatement(); + //private static async Task BulkInsertAsync(ISession session, PreparedStatement prepared, List records) + //{ + // var tasks = new List(); + // var batch = new BatchStatement(); - for (int i = 0; i < records.Count; i++) - { - var record = records[i]; - var boundStatement = prepared.Bind( - record.Id, - record.ClientId, - record.Message, - record.DeviceNo, - (int)record.Type, - record.MessageId); + // for (int i = 0; i < records.Count; i++) + // { + // var record = records[i]; + // var boundStatement = prepared.Bind( + // record.Id, + // record.ClientId, + // record.Message, + // record.DeviceNo, + // (int)record.Type, + // record.MessageId); - // 设置一致性级别为ONE以提高性能 - boundStatement.SetConsistencyLevel(ConsistencyLevel.One); + // // 设置一致性级别为ONE以提高性能 + // boundStatement.SetConsistencyLevel(ConsistencyLevel.One); - batch.Add(boundStatement); + // batch.Add(boundStatement); - // 当达到批处理大小时执行 - if (batch.Statements.Count() >= 1000 || i == records.Count - 1) - { - tasks.Add(session.ExecuteAsync(batch)); - batch = new BatchStatement(); - } - } + // // 当达到批处理大小时执行 + // if (batch.Statements.Count() >= 1000 || i == records.Count - 1) + // { + // tasks.Add(session.ExecuteAsync(batch)); + // batch = new BatchStatement(); + // } + // } - // 等待所有批处理完成 - await Task.WhenAll(tasks); - } + // // 等待所有批处理完成 + // await Task.WhenAll(tasks); + //} [LogIntercept] public virtual Task LogInterceptorTest(string str) @@ -136,8 +143,76 @@ public class TestAppService : CollectBusAppService public async Task ReloadPluginsAsync() { var aa = Assembly.LoadFile( - @"D:\Codes\CollectBusV5\JiShe.CollectBus\web\JiShe.CollectBus.Host\bin\Debug\net8.0\Plugins\JiShe.CollectBus.Protocol.Test.dll"); + @"E:\Devlopment\JiShe.CollectBusDev01\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); + await _dynamicModuleManager.LoadModuleAsync(module); + } + + + public async Task TestPlugins() + { + var executor = _serviceProvider.GetRequiredService(); + return await executor.ExecuteAsync("TestServer", "TestServer"); + + } + + /// + /// 注册策略 + /// + public void RegisterProtocolAnalysis() + { + // 扫描并注册所有策略 + var strategyMetadata = new Dictionary<(string, Type), Type>(); + _serviceDescriptors.AddTransient(); + + // 批量注册 + var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); + if (string.IsNullOrWhiteSpace(assemblyPath)) + { + return; + } + var dllFiles = Directory.GetFiles(Path.Combine(assemblyPath, "Plugins"), "*.dll"); + foreach (var file in dllFiles) + { + // 跳过已加载的程序集 + var assemblyName = AssemblyName.GetAssemblyName(file); + var existingAssembly = AppDomain.CurrentDomain.GetAssemblies() + .FirstOrDefault(a => a.GetName().FullName == assemblyName.FullName); + var assembly = existingAssembly ?? Assembly.LoadFrom(file); + // 实现IAnalysisStrategy接口 + var analysisStrategyTypes = assembly.GetTypes().Where(t => !t.IsAbstract && !t.IsInterface && t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IAnalysisStrategy<>))); + if (!analysisStrategyTypes.Any()) + continue; + foreach (var analysisStrategyType in analysisStrategyTypes) + { + // 通过反射获取静态元数据 + var strategyType = analysisStrategyType.Name; + var genericArgs = analysisStrategyType.GetInterface($"IAnalysisStrategy`1")!.GetGenericArguments(); + var inputType = genericArgs[0]; + // 注册策略实现 + _serviceDescriptors.AddTransient(analysisStrategyType); + strategyMetadata[(strategyType, inputType)] = analysisStrategyType; + + } + } + + // 注册元数据字典 + _serviceDescriptors.AddSingleton(strategyMetadata); + + // 注册策略解析工厂 + _serviceDescriptors.AddTransient>(provider => (name, inputType) => + { + var metadata = provider.GetRequiredService>(); + if (metadata.TryGetValue((name, inputType), out var strategyType)) + { + return provider.GetRequiredService(strategyType); + } + else + { + var logger = provider.GetRequiredService>(); + logger.LogWarning($"未能找到解析策略:{name}-{inputType}"); + return null; + } + }); } } diff --git a/services/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs b/services/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs index 85a71d8..8e04734 100644 --- a/services/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs +++ b/services/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs @@ -1,37 +1,21 @@ -using JiShe.CollectBus.Application.Contracts; -using JiShe.CollectBus.Common; +using JiShe.CollectBus.Common; using JiShe.CollectBus.Common.Consts; using JiShe.CollectBus.Common.DeviceBalanceControl; -using JiShe.CollectBus.Common.Encrypt; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.Common.Extensions; -using JiShe.CollectBus.Common.Helpers; using JiShe.CollectBus.Common.Models; using JiShe.CollectBus.DataChannels; -using JiShe.CollectBus.EnergySystems.Entities; using JiShe.CollectBus.GatherItem; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IoTDB.Model; -using JiShe.CollectBus.IoTDB.Options; -using JiShe.CollectBus.IoTDB.Provider; using JiShe.CollectBus.IotSystems.Ammeters; -using JiShe.CollectBus.IotSystems.Devices; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.Kafka.Internal; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol.Models; +using JiShe.ServicePro.Core; +using JiShe.ServicePro.Encrypt; using Mapster; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; using Volo.Abp.Guids; -using static FreeSql.Internal.GlobalFilter; -using static Microsoft.AspNetCore.Razor.Language.TagHelperMetadata; -using static Thrift.Protocol.Utilities.TJSONProtocolConstants; namespace JiShe.CollectBus.ScheduledMeterReading { @@ -41,7 +25,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading public abstract class BasicScheduledMeterReadingService : CollectBusAppService, IScheduledMeterReadingService { private readonly ILogger _logger; - private readonly IIoTDbProvider _dbProvider; + private readonly IoTDBSessionPoolProvider _dbProvider; private readonly IDataChannelManageService _dataChannelManage; private readonly IRedisDataCacheService _redisDataCacheService; private readonly IProtocolService _protocolService; @@ -55,7 +39,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading ILogger logger, IDataChannelManageService dataChannelManage, IRedisDataCacheService redisDataCacheService, - IIoTDbProvider dbProvider, + IoTDBSessionPoolProvider dbProvider, IProtocolService protocolService, IGuidGenerator guidGenerator, IOptions kafkaOptions, @@ -141,7 +125,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading //_logger.LogInformation($"{nameof(AmmeterScheduledAutomaticVerificationTime)} 电表自动校时,非自动校时时间"); //return; - _ = CreateMeterPublishTask( + _ = CreateMeterPublishTask( timeDensity: timeDensity, nextTaskTime: currentTime, meterType: MeterTypeEnum.Ammeter, @@ -154,12 +138,12 @@ namespace JiShe.CollectBus.ScheduledMeterReading _logger.LogWarning($"电表自动校时 {data.Name} 任务数据构建失败:{data.Serialize()}"); return; } - _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (ProtocolConst.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); + _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (KafkaTopicConsts.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); }); } else if (string.Equals(currentTimeStr, _applicationOptions.AutomaticTerminalVersionTime, StringComparison.CurrentCultureIgnoreCase))//集中器版本号读取 { - _ = CreateMeterPublishTask( + _ = CreateMeterPublishTask( timeDensity: timeDensity, nextTaskTime: currentTime, meterType: MeterTypeEnum.Ammeter, @@ -171,12 +155,12 @@ namespace JiShe.CollectBus.ScheduledMeterReading _logger.LogWarning($"集中器终端版本信息 {data.Name} 任务数据构建失败:{data.Serialize()}"); return; } - _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (ProtocolConst.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); + _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (KafkaTopicConsts.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); }); } else if (string.Equals(currentTimeStr, _applicationOptions.AutomaticTelematicsModuleTime, StringComparison.CurrentCultureIgnoreCase))//SIM卡读取 { - _ = CreateMeterPublishTask( + _ = CreateMeterPublishTask( timeDensity: timeDensity, nextTaskTime: currentTime, meterType: MeterTypeEnum.Ammeter, @@ -188,12 +172,12 @@ namespace JiShe.CollectBus.ScheduledMeterReading _logger.LogWarning($"集中器SIM卡读取 {data.Name} 任务数据构建失败:{data.Serialize()}"); return; } - _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (ProtocolConst.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); + _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (KafkaTopicConsts.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); }); } else if (string.Equals(currentTimeStr, _applicationOptions.AutomaticMonthFreezeTime, StringComparison.CurrentCultureIgnoreCase))//月冻结 { - _ = CreateMeterPublishTask( + _ = CreateMeterPublishTask( timeDensity: timeDensity, nextTaskTime: currentTime, meterType: MeterTypeEnum.Ammeter, @@ -205,12 +189,12 @@ namespace JiShe.CollectBus.ScheduledMeterReading _logger.LogWarning($"电表月冻结 {data.Name} 任务数据构建失败:{data.Serialize()}"); return; } - _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (ProtocolConst.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); + _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (KafkaTopicConsts.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); }); } else if (string.Equals(currentTimeStr, _applicationOptions.AutomaticDayFreezeTime, StringComparison.CurrentCultureIgnoreCase))//日冻结 { - _ = CreateMeterPublishTask( + _ = CreateMeterPublishTask( timeDensity: timeDensity, nextTaskTime: currentTime, meterType: MeterTypeEnum.Ammeter, @@ -222,7 +206,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading _logger.LogWarning($"电表日冻结 {data.Name} 任务数据构建失败:{data.Serialize()}"); return; } - _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (ProtocolConst.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); + _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (KafkaTopicConsts.AmmeterSubscriberWorkerOtherIssuedEventName, tempTask)); }); } else @@ -248,7 +232,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading timeDensity = 15; } - _ = CreateMeterPublishTask( + _ = CreateMeterPublishTask( timeDensity: timeDensity, nextTaskTime: currentTaskTime, meterType: MeterTypeEnum.Ammeter, @@ -260,13 +244,13 @@ namespace JiShe.CollectBus.ScheduledMeterReading _logger.LogWarning($"电表 {data.Name} 任务数据构建失败:{data.Serialize()}"); return; } - _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, tempTask)); + _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (KafkaTopicConsts.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, tempTask)); }); } else if (meteryType == MeterTypeEnum.WaterMeter.ToString()) { - _ = CreateMeterPublishTask( + _ = CreateMeterPublishTask( timeDensity: timeDensity, nextTaskTime: currentTaskTime, meterType: MeterTypeEnum.WaterMeter, @@ -279,7 +263,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading _logger.LogWarning($"水表 {data.Name} 任务数据构建失败:{data.Serialize()}"); return; } - _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (ProtocolConst.WatermeterSubscriberWorkerAutoReadingIssuedEventName, tempTask)); + _ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (KafkaTopicConsts.WatermeterSubscriberWorkerAutoReadingIssuedEventName, tempTask)); }); } else @@ -305,7 +289,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading // _logger.LogWarning($"{nameof(AmmeterScheduledAutoValveControl)}电表定时阀控没有可操作的任务"); // return; //} - //_ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (ProtocolConst.AmmeterSubscriberWorkerAutoValveControlIssuedEventName, autoValveControlTask)); + //_ = _dataChannelManage.ScheduledMeterTaskWriterAsync(DataChannelManage.TaskDataChannel.Writer, (KafkaTopicConsts.AmmeterSubscriberWorkerAutoValveControlIssuedEventName, autoValveControlTask)); } #region 电表采集处理 @@ -315,7 +299,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// /// 采集端Code /// - public virtual Task> GetAmmeterInfoList(string gatherCode = "") + public virtual Task> GetAmmeterInfoList(string gatherCode = "") { throw new NotImplementedException($"{nameof(GetAmmeterInfoList)}请根据不同系统类型进行实现"); } @@ -332,19 +316,17 @@ namespace JiShe.CollectBus.ScheduledMeterReading try { // 创建取消令牌源 - //var cts = new CancellationTokenSource(); - - await _dbProvider.GetSessionPool(true).InitTableSessionModelAsync(); + //var cts = new CancellationTokenSource(); _ = _dataChannelManage.ScheduledMeterTaskReadingAsync(DataChannelManage.TaskDataChannel.Reader); // //此处代码不要删除 //#if DEBUG - // var redisCacheDeviceInfoHashKeyTemp = $"CollectBus:Energy:JiSheCollectBus2:DeviceInfo"; + // var redisCacheDeviceCacheInfoHashKeyTemp = $"CollectBus:Energy:JiSheCollectBus2:DeviceCacheInfo"; // var timer1 = Stopwatch.StartNew(); - // Dictionary> keyValuePairsTemps = FreeRedisProvider.Instance.HGetAll>(redisCacheDeviceInfoHashKeyTemp); - // List meterInfos = new List(); + // Dictionary> keyValuePairsTemps = FreeRedisProvider.Instance.HGetAll>(redisCacheDeviceCacheInfoHashKeyTemp); + // List meterInfos = new List(); // List focusAddressDataLista = new List(); // foreach (var item in keyValuePairsTemps) // { @@ -410,12 +392,12 @@ namespace JiShe.CollectBus.ScheduledMeterReading } //设备hash缓存key - string redisCacheDeviceInfoHashKey = $"{string.Format(RedisConst.CacheDeviceInfoHashKey, SystemType, ServerTagName)}"; + string redisCacheDeviceCacheInfoHashKey = $"{string.Format(RedisConst.CacheDeviceInfoHashKey, SystemType, ServerTagName)}"; //设备分组集合key string redisCacheDeviceGroupSetIndexKey = $"{string.Format(RedisConst.CacheDeviceGroupSetIndexKey, SystemType, ServerTagName)}"; - Dictionary> keyValuePairs = new Dictionary>(); + Dictionary> keyValuePairs = new Dictionary>(); //处理设备缓存信息 foreach (var ammeter in meterInfos) @@ -466,17 +448,17 @@ namespace JiShe.CollectBus.ScheduledMeterReading if (!keyValuePairs.ContainsKey(ammeter.FocusAddress)) { - keyValuePairs[ammeter.FocusAddress] = new List() { ammeter.Adapt() }; + keyValuePairs[ammeter.FocusAddress] = new List() { ammeter.Adapt() }; } else { - keyValuePairs[ammeter.FocusAddress].Add(ammeter.Adapt()); + keyValuePairs[ammeter.FocusAddress].Add(ammeter.Adapt()); } } - await _redisDataCacheService.BatchInsertDataAsync( + await _redisDataCacheService.BatchInsertDataAsync( redisCacheDeviceGroupSetIndexKey, - redisCacheDeviceInfoHashKey, + redisCacheDeviceCacheInfoHashKey, keyValuePairs); //初始化设备组负载控制 @@ -500,116 +482,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading throw ex; } } - - /// - /// 1分钟采集电表数据,只获取任务数据下发,不构建任务 - /// - /// - public virtual async Task AmmeterScheduledMeterOneMinuteReading() - { - int timeDensity = 5; - var redisCacheKey = string.Format(RedisConst.CacheTasksToBeIssuedKey, SystemType, ServerTagName, MeterTypeEnum.Ammeter, timeDensity); - var taskInfo = await FreeRedisProvider.Instance.GetAsync(redisCacheKey); - - if (taskInfo == null) - { - _logger.LogError($"{nameof(AmmeterScheduledMeterFifteenMinuteReading)} {timeDensity}分钟获取任务下发信息失败,请检查Redis中是否有对应的任务下发信息"); - return; - } - - var pendingCopyReadTime = taskInfo.LastTaskTime.GetDateTimeOffset().ToUnixTimeNanoseconds(); - - var conditions = new List(); - conditions.Add(new QueryCondition() - { - Field = "PendingCopyReadTime", - Operator = "=", - IsNumber = true, - Value = pendingCopyReadTime - }); - - _ = CreateMeterKafkaTaskMessage(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, new IoTDBQueryOptions() - { - TableNameOrTreePath = DevicePathBuilder.GetTableName(), - PageIndex = 1, - PageSize = pageSize, - Conditions = conditions, - }); - - } - - /// - /// 5分钟采集电表数据 - /// - /// - public virtual async Task AmmeterScheduledMeterFiveMinuteReading() - { - int timeDensity = 5; - var redisCacheKey = string.Format(RedisConst.CacheTasksToBeIssuedKey, SystemType, ServerTagName, MeterTypeEnum.Ammeter, timeDensity); - var taskInfo = await FreeRedisProvider.Instance.GetAsync(redisCacheKey); - - if (taskInfo == null) - { - _logger.LogError($"{nameof(AmmeterScheduledMeterFifteenMinuteReading)} {timeDensity}分钟获取任务下发信息失败,请检查Redis中是否有对应的任务下发信息"); - return; - } - - var pendingCopyReadTime = taskInfo.LastTaskTime.GetDateTimeOffset().ToUnixTimeNanoseconds(); - - var conditions = new List(); - conditions.Add(new QueryCondition() - { - Field = "PendingCopyReadTime", - Operator = "=", - IsNumber = true, - Value = pendingCopyReadTime - }); - - _ = CreateMeterKafkaTaskMessage(ProtocolConst.AmmeterSubscriberWorkerFiveMinuteIssuedEventName, new IoTDBQueryOptions() - { - TableNameOrTreePath = DevicePathBuilder.GetTableName(), - PageIndex = 1, - PageSize = pageSize, - Conditions = conditions, - }); - } - - /// - /// 15分钟采集电表数据 - /// - /// - public virtual async Task AmmeterScheduledMeterFifteenMinuteReading() - { - int timeDensity = 15; - var redisCacheKey = string.Format(RedisConst.CacheTasksToBeIssuedKey, SystemType, ServerTagName, MeterTypeEnum.Ammeter, timeDensity); - var taskInfo = await FreeRedisProvider.Instance.GetAsync(redisCacheKey); - - if (taskInfo == null) - { - _logger.LogError($"{nameof(AmmeterScheduledMeterFifteenMinuteReading)} {timeDensity}分钟获取任务下发信息失败,请检查Redis中是否有对应的任务下发信息"); - return; - } - - var pendingCopyReadTime = taskInfo.LastTaskTime.GetDateTimeOffset().ToUnixTimeNanoseconds(); - - var conditions = new List(); - conditions.Add(new QueryCondition() - { - Field = "PendingCopyReadTime", - Operator = "=", - IsNumber = true, - Value = pendingCopyReadTime - }); - - _ = CreateMeterKafkaTaskMessage(ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, new IoTDBQueryOptions() - { - TableNameOrTreePath = DevicePathBuilder.GetTableName(), - PageIndex = 1, - PageSize = pageSize, - Conditions = conditions, - }); - } - + /// /// 创建电表待发送的任务数据 /// @@ -618,7 +491,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - private async Task> AmmerterCreatePublishTaskAction(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps) + private async Task> AmmerterCreatePublishTaskAction(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps) { var currentTime = DateTime.Now; @@ -802,7 +675,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - public virtual async Task> AmmeterScheduledAutomaticVerificationTime(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps) + public virtual async Task> AmmeterScheduledAutomaticVerificationTime(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps) { var currentTime = DateTime.Now; string currentTimeStr = $"{currentTime:HH:mm:00}"; @@ -883,7 +756,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - public virtual async Task> AmmeterScheduledGetAutomaticDayFreezeData(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps) + public virtual async Task> AmmeterScheduledGetAutomaticDayFreezeData(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps) { var currentTime = DateTime.Now; string currentTimeStr = $"{currentTime:HH:mm:00}"; @@ -964,7 +837,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - public virtual async Task> AmmeterScheduledGetAutomaticMonthFreezeData(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps) + public virtual async Task> AmmeterScheduledGetAutomaticMonthFreezeData(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps) { var currentTime = DateTime.Now; string currentTimeStr = $"{currentTime:HH:mm:00}"; @@ -1098,7 +971,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading IsNumber = false, Value = false }); - //await CreateMeterKafkaTaskMessage(ProtocolConst.AmmeterSubscriberWorkerRetryEventName, new IoTDBQueryOptions() + //await CreateMeterKafkaTaskMessage(KafkaTopicConsts.AmmeterSubscriberWorkerRetryEventName, new IoTDBQueryOptions() //{ // TableNameOrTreePath = DevicePathBuilder.GetTableName(), // PageIndex = 1, @@ -1128,7 +1001,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// /// 采集端Code /// - public virtual Task> GetWatermeterInfoList(string gatherCode = "") + public virtual Task> GetWatermeterInfoList(string gatherCode = "") { throw new NotImplementedException($"{nameof(GetWatermeterInfoList)}请根据不同系统类型进行实现"); } @@ -1217,19 +1090,19 @@ namespace JiShe.CollectBus.ScheduledMeterReading } //设备hash缓存key - string redisCacheDeviceInfoHashKey = $"{string.Format(RedisConst.CacheDeviceInfoHashKey, SystemType, ServerTagName)}"; + string redisCacheDeviceCacheInfoHashKey = $"{string.Format(RedisConst.CacheDeviceInfoHashKey, SystemType, ServerTagName)}"; //设备分组集合key string redisCacheDeviceGroupSetIndexKey = $"{string.Format(RedisConst.CacheDeviceGroupSetIndexKey, SystemType, ServerTagName)}"; - Dictionary> keyValuePairs = new Dictionary>(); + Dictionary> keyValuePairs = new Dictionary>(); foreach (var subItem in meterInfos) { deviceIds.Add(subItem.MeterId.ToString()); if (!keyValuePairs.ContainsKey(subItem.FocusAddress)) { - keyValuePairs[subItem.FocusAddress] = new List() { subItem }; + keyValuePairs[subItem.FocusAddress] = new List() { subItem }; } else { @@ -1239,9 +1112,9 @@ namespace JiShe.CollectBus.ScheduledMeterReading - await _redisDataCacheService.BatchInsertDataAsync( + await _redisDataCacheService.BatchInsertDataAsync( redisCacheDeviceGroupSetIndexKey, - redisCacheDeviceInfoHashKey, + redisCacheDeviceCacheInfoHashKey, keyValuePairs); //初始化设备组负载控制 @@ -1258,46 +1131,8 @@ namespace JiShe.CollectBus.ScheduledMeterReading _logger.LogInformation($"{nameof(InitAmmeterCacheData)} 初始化水表缓存数据完成"); } - - /// - /// 水表数据采集 - /// - /// - public virtual async Task WatermeterScheduledMeterAutoReadding() - { - //获取缓存中的水表信息 - int timeDensity = 60;//水表目前只有一个采集频率 60分钟 - var redisCacheKey = string.Format(RedisConst.CacheTasksToBeIssuedKey, SystemType, ServerTagName, MeterTypeEnum.WaterMeter, timeDensity); - var taskInfo = await FreeRedisProvider.Instance.GetAsync(redisCacheKey); - - if (taskInfo == null) - { - _logger.LogError($"{nameof(AmmeterScheduledMeterFifteenMinuteReading)} {timeDensity}分钟获取任务下发信息失败,请检查Redis中是否有对应的任务下发信息"); - return; - } - - var pendingCopyReadTime = taskInfo.LastTaskTime.GetDateTimeOffset().ToUnixTimeNanoseconds(); - - var conditions = new List(); - conditions.Add(new QueryCondition() - { - Field = "PendingCopyReadTime", - Operator = "=", - IsNumber = true, - Value = pendingCopyReadTime - }); - - //_ = CreateMeterKafkaTaskMessage(ProtocolConst.WatermeterSubscriberWorkerAutoReadingIssuedEventName, new IoTDBQueryOptions() - //{ - // TableNameOrTreePath = DevicePathBuilder.GetTableName(), - // PageIndex = 1, - // PageSize = pageSize, - // Conditions = conditions, - //}); - - _logger.LogInformation($"{nameof(WatermeterScheduledMeterAutoReadding)} {timeDensity}分钟采集水表数据处理完成"); - } - + + /// /// 创建水表待发送的任务数据 /// @@ -1307,7 +1142,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 时间格式的任务批次名称 /// private async Task> WatermeterCreatePublishTaskAction(int timeDensity - , DeviceInfo watermeter, int groupIndex, DateTime timestamps) + , DeviceCacheInfo watermeter, int groupIndex, DateTime timestamps) { var currentTime = DateTime.Now; @@ -1414,7 +1249,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - public virtual async Task> ConcentratorScheduledAutomaticGetTerminalVersion(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps) + public virtual async Task> ConcentratorScheduledAutomaticGetTerminalVersion(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps) { var currentTime = DateTime.Now; string currentTimeStr = $"{currentTime:HH:mm:00}"; @@ -1493,7 +1328,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 集中器所在分组 /// 采集频率对应的时间戳 /// - public virtual async Task> ConcentratorScheduledAutomaticGetTelematicsModule(int timeDensity, DeviceInfo ammeterInfo, int groupIndex, DateTime timestamps) + public virtual async Task> ConcentratorScheduledAutomaticGetTelematicsModule(int timeDensity, DeviceCacheInfo ammeterInfo, int groupIndex, DateTime timestamps) { var currentTime = DateTime.Now; string currentTimeStr = $"{currentTime:HH:mm:00}"; @@ -1591,7 +1426,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading //获取对应频率中的所有电表信息 //设备hash缓存key - string redisCacheDeviceInfoHashKey = $"{string.Format(RedisConst.CacheDeviceInfoHashKey, SystemType, ServerTagName)}"; + string redisCacheDeviceCacheInfoHashKey = $"{string.Format(RedisConst.CacheDeviceInfoHashKey, SystemType, ServerTagName)}"; //设备分组集合key string redisCacheDeviceGroupSetIndexKey = $"{string.Format(RedisConst.CacheDeviceGroupSetIndexKey, SystemType, ServerTagName)}"; @@ -1604,7 +1439,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading //{ // var page = await _redisDataCacheService.GetAllPagedData2( // redisCacheDeviceGroupSetIndexKey, - // redisCacheDeviceInfoHashKey, + // redisCacheDeviceCacheInfoHashKey, // pageSize: 1000, // lastScore: cursor, // lastMember: member); @@ -1634,7 +1469,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading // return; //} - Dictionary> keyValuePairs = FreeRedisProvider.Instance.HGetAll>(redisCacheDeviceInfoHashKey); + Dictionary> keyValuePairs = FreeRedisProvider.Instance.HGetAll>(redisCacheDeviceCacheInfoHashKey); timer.Stop(); @@ -1667,70 +1502,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading timer.Stop(); _logger.LogError($"{nameof(CreateMeterPublishTask)} {meterType} {timeDensity}分钟采集待下发任务创建完成,耗时{timer.ElapsedMilliseconds}毫秒,总共{meterInfos.Count}表计信息"); } - - - /// - /// 创建Kafka消息 - /// - /// - /// kafka主题名称 - /// 任务查询条件 - /// - protected async Task CreateMeterKafkaTaskMessage(string kafkaTopicName, IoTDBQueryOptions options) where T : IoTEntity, new() - { - if (string.IsNullOrWhiteSpace(kafkaTopicName)) - { - _logger.LogInformation($"{nameof(CreateMeterKafkaTaskMessage)} Kafka消息推送主题不能为空,-101"); - return; - } - int pageNumber = 103; - bool hasNext; - var stopwatch = Stopwatch.StartNew(); - - do - { - var stopwatch2 = Stopwatch.StartNew(); - - options.PageIndex = pageNumber++; - - var pageResult = await _dbProvider.QueryAsync(options); - - hasNext = pageResult.HasNext; - - _ = DeviceGroupBalanceControl.ProcessWithThrottleAsync( - items: pageResult.Items.ToList(), - deviceIdSelector: data => data.DeviceId, - processor: (data, groupIndex) => - { - _ = KafkaProducerIssuedMessageAction(kafkaTopicName, data, groupIndex); - } - ); - stopwatch2.Stop(); - _logger.LogWarning($"{nameof(CreateMeterKafkaTaskMessage)} {kafkaTopicName}主题的任务 {options.TableNameOrTreePath} 路径批次{options.PageIndex}任务数据读取完成,共消耗{stopwatch2.ElapsedMilliseconds}毫秒。"); - } while (hasNext); - - stopwatch.Stop(); - _logger.LogWarning($"{nameof(CreateMeterKafkaTaskMessage)} {kafkaTopicName}主题的任务 {options.TableNameOrTreePath} 路径任务推送完成,共消耗{stopwatch.ElapsedMilliseconds}毫秒。"); - } - - /// - /// Kafka 推送消息 - /// - /// 主题名称 - /// 任务记录 - /// 对应分区,也就是集中器号所在的分组序号 - /// - protected async Task KafkaProducerIssuedMessageAction(string topicName, - T taskRecord, int partition) where T : class - { - if (string.IsNullOrWhiteSpace(topicName) || taskRecord == null) - { - throw new Exception($"{nameof(KafkaProducerIssuedMessageAction)} 推送消息失败,参数异常,-101"); - } - - // await _dataChannelManage.ProduceAsync(topicName, taskRecord, partition); - } - + /// /// 构建报文保存对象 /// @@ -1744,7 +1516,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// 数据包类型 /// Guid生成器 /// - protected MeterReadingTelemetryPacketInfo CreateAmmeterPacketInfo(DeviceInfo ammeterInfo, long timestamps, ProtocolBuildResponse builderResponse, string itemCode, string subItemCode, DateTime pendingCopyReadTime, DateTime creationTime, TelemetryPacketTypeEnum packetType, IGuidGenerator guidGenerator) + protected MeterReadingTelemetryPacketInfo CreateAmmeterPacketInfo(DeviceCacheInfo ammeterInfo, long timestamps, ProtocolBuildResponse builderResponse, string itemCode, string subItemCode, DateTime pendingCopyReadTime, DateTime creationTime, TelemetryPacketTypeEnum packetType, IGuidGenerator guidGenerator) { try { @@ -1752,7 +1524,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading return new MeterReadingTelemetryPacketInfo() { SystemName = SystemType, - ProjectId = $"{ammeterInfo.ProjectID}", + ProjectId = $"{ammeterInfo.ProjectId}", DeviceType = $"{MeterTypeEnum.Ammeter}", DeviceId = $"{ammeterInfo.MeterId}", Timestamps = timestamps, diff --git a/services/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs b/services/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs index 762d001..a76c551 100644 --- a/services/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs +++ b/services/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs @@ -1,26 +1,17 @@ -using JiShe.CollectBus.Application.Contracts; -using JiShe.CollectBus.Common; +using JiShe.CollectBus.Common; using JiShe.CollectBus.Common.Consts; using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Extensions; using JiShe.CollectBus.Common.Helpers; using JiShe.CollectBus.DataChannels; -using JiShe.CollectBus.FreeSql; using JiShe.CollectBus.GatherItem; -using JiShe.CollectBus.IoTDB.Interface; using JiShe.CollectBus.IotSystems.Ammeters; -using JiShe.CollectBus.IotSystems.Devices; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.Kafka.Internal; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol.Models; +using JiShe.ServicePro.Consts; +using JiShe.ServicePro.Core; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Volo.Abp.Guids; @@ -43,7 +34,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading ILogger logger, IDataChannelManageService dataChannelManage, IRedisDataCacheService redisDataCacheService, - IIoTDbProvider dbProvider, + IoTDBSessionPoolProvider dbProvider, IProtocolService protocolService, IGuidGenerator guidGenerator, IOptions kafkaOptions, @@ -94,17 +85,17 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// //[HttpGet] //[Route($"ammeter/list")] - public override async Task> GetAmmeterInfoList(string gatherCode = "V4-Gather-8890") + public override async Task> GetAmmeterInfoList(string gatherCode = "V4-Gather-8890") { #if DEBUG - var redisCacheDeviceInfoHashKeyTemp = $"CollectBus:Energy:JiSheCollectBus109:DeviceInfo"; + var redisCacheDeviceCacheInfoHashKeyTemp = $"CollectBus:Energy:JiSheCollectBus109:DeviceCacheInfo"; - List ammeterInfos = FreeRedisProvider.Instance.Get>(redisCacheDeviceInfoHashKeyTemp);//542400504 + List ammeterInfos = FreeRedisProvider.Instance.Get>(redisCacheDeviceCacheInfoHashKeyTemp);//542400504 if (ammeterInfos == null || ammeterInfos.Count <= 0) { - ammeterInfos = new List(); - //ammeterInfos.Add(new DeviceInfo() + ammeterInfos = new List(); + //ammeterInfos.Add(new DeviceCacheInfo() //{ // Baudrate = 2400, // FocusAddress = "442400040", @@ -124,7 +115,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading // Password = "000000", //}); - //ammeterInfos.Add(new DeviceInfo() + //ammeterInfos.Add(new DeviceCacheInfo() //{ // Baudrate = 2400, // FocusAddress = "442400039", @@ -144,7 +135,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading // Password = "000000", //}); - //ammeterInfos.Add(new DeviceInfo() + //ammeterInfos.Add(new DeviceCacheInfo() //{ // Baudrate = 2400, // FocusAddress = "402440506", @@ -165,7 +156,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading //}); - //ammeterInfos.Add(new DeviceInfo() + //ammeterInfos.Add(new DeviceCacheInfo() //{ // Baudrate = 2400, // FocusAddress = "942411321", @@ -186,7 +177,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading //}); - //ammeterInfos.Add(new DeviceInfo() + //ammeterInfos.Add(new DeviceCacheInfo() //{ // Baudrate = 2400, // FocusAddress = "942411319", @@ -206,7 +197,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading // MeteringPort = MeteringPortConst.MeteringPortTwo, //}); - ammeterInfos.Add(new DeviceInfo() + ammeterInfos.Add(new DeviceCacheInfo() { Baudrate = 2400, FocusAddress = "322011149", @@ -221,12 +212,17 @@ namespace JiShe.CollectBus.ScheduledMeterReading TimeDensity = 15, BrandType = "DTS1980", Password = "000000", - ProjectID = 1, MeterType = MeterTypeEnum.Ammeter, MeteringPort = MeteringPortConst.MeteringPortTwo, + SystemName = SystemType, + IoTDataType = IOTDBDataTypeConst.Data, + ProjectId = "1", + DeviceType = JiShe.ServicePro.Enums.MeterTypeEnum.Ammeter.ToString(), + DeviceId = "78973", + }); - FreeRedisProvider.Instance.Set(redisCacheDeviceInfoHashKeyTemp, ammeterInfos); + FreeRedisProvider.Instance.Set(redisCacheDeviceCacheInfoHashKeyTemp, ammeterInfos); } return ammeterInfos; @@ -285,7 +281,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading } return await SqlProvider.Instance.Change(DbEnum.EnergyDB) .Ado - .QueryAsync(sql); + .QueryAsync(sql); } catch (Exception ex) { @@ -425,7 +421,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading FreeRedisProvider.Instance.Set(redisCacheDeviceSettingInfoHashKey, settingInfos); } - List meterInfos = await GetAmmeterInfoList(); + List meterInfos = await GetAmmeterInfoList(); #else //获取电表阀控配置 var settingInfos = await GetAmmeterAutoValveControlSetting(currentTimeStr); @@ -436,10 +432,10 @@ namespace JiShe.CollectBus.ScheduledMeterReading } //设备hash缓存key - string redisCacheDeviceInfoHashKey = $"{string.Format(RedisConst.CacheDeviceInfoHashKey, SystemType, ServerTagName)}"; + string redisCacheDeviceCacheInfoHashKey = $"{string.Format(RedisConst.CacheDeviceInfoHashKey, SystemType, ServerTagName)}"; - Dictionary> keyValuePairsTemps = FreeRedisProvider.Instance.HGetAll>(redisCacheDeviceInfoHashKey); - List meterInfos = new List(); + Dictionary> keyValuePairsTemps = FreeRedisProvider.Instance.HGetAll>(redisCacheDeviceCacheInfoHashKey); + List meterInfos = new List(); List focusAddressDataLista = new List(); foreach (var item in keyValuePairsTemps) { @@ -575,19 +571,19 @@ namespace JiShe.CollectBus.ScheduledMeterReading /// //[HttpGet] //[Route($"ammeter/list")] - public override async Task> GetWatermeterInfoList(string gatherCode = "V4-Gather-8890") + public override async Task> GetWatermeterInfoList(string gatherCode = "V4-Gather-8890") { try { #if DEBUG - var redisCacheDeviceInfoHashKeyTemp = $"CollectBus:Energy:JiSheCollectBus119:DeviceInfo"; + var redisCacheDeviceCacheInfoHashKeyTemp = $"CollectBus:Energy:JiSheCollectBus119:DeviceCacheInfo"; - List deviceInfos = FreeRedisProvider.Instance.Get>(redisCacheDeviceInfoHashKeyTemp); + List DeviceCacheInfos = FreeRedisProvider.Instance.Get>(redisCacheDeviceCacheInfoHashKeyTemp); - if (deviceInfos == null || deviceInfos.Count <= 0) + if (DeviceCacheInfos == null || DeviceCacheInfos.Count <= 0) { - deviceInfos = new List(); - deviceInfos.Add(new DeviceInfo() + DeviceCacheInfos = new List(); + DeviceCacheInfos.Add(new DeviceCacheInfo() { Baudrate = 2400, FocusAddress = "322011149", @@ -602,18 +598,22 @@ namespace JiShe.CollectBus.ScheduledMeterReading BrandType = "云集", MeterTypeName = "水表", MeterType = MeterTypeEnum.WaterMeter, - ProjectID = 1, MeteringPort = MeteringPortConst.MeteringPortTwo, Password = "000000", LinkType = "RS-485", TimesRate = 1.0000m, Protocol = 30, + SystemName = SystemType, + IoTDataType = IOTDBDataTypeConst.Data, + ProjectId = "1", + DeviceType = JiShe.ServicePro.Enums.MeterTypeEnum.WaterMeter.ToString(), + DeviceId = "1025", }); - FreeRedisProvider.Instance.Set(redisCacheDeviceInfoHashKeyTemp, deviceInfos); + FreeRedisProvider.Instance.Set(redisCacheDeviceCacheInfoHashKeyTemp, DeviceCacheInfos); } - - return deviceInfos; + + return DeviceCacheInfos; #else string sql = $@"SELECT A.ID as MeterId, @@ -656,7 +656,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading } return await SqlProvider.Instance.Change(DbEnum.EnergyDB) .Ado - .QueryAsync(sql); + .QueryAsync(sql); #endif } catch (Exception) diff --git a/services/JiShe.CollectBus.Application/Subscribers/ServiceCommunicationChannelSubscriberService.cs b/services/JiShe.CollectBus.Application/Subscribers/ServiceCommunicationChannelSubscriberService.cs new file mode 100644 index 0000000..8ee60e6 --- /dev/null +++ b/services/JiShe.CollectBus.Application/Subscribers/ServiceCommunicationChannelSubscriberService.cs @@ -0,0 +1,91 @@ +using JiShe.CollectBus.Protocol.Interfaces; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TouchSocket.Sockets; + +namespace JiShe.CollectBus.Subscribers +{ + public class ServiceCommunicationChannelSubscriberService : CollectBusAppService, IServiceCommunicationChannelSubscriberService, IKafkaSubscribe + { + private readonly ILogger _logger; + private readonly ITcpService _tcpService; + private readonly IServiceProvider _serviceProvider; + private readonly IoTDBSessionPoolProvider _dbProvider; + private readonly IProtocolService _protocolService; + + + public ServiceCommunicationChannelSubscriberService(ILogger logger, + ITcpService tcpService, + IServiceProvider serviceProvider, + IoTDBSessionPoolProvider dbProvider, + IProtocolService protocolService) + { + _logger = logger; + _tcpService = tcpService; + _serviceProvider = serviceProvider; + _dbProvider = dbProvider; + _protocolService = protocolService; + } + + + /// + /// 数据通讯通道消息消费订阅 + /// + /// + /// + [KafkaSubscribe(KafkaTopicConsts.ServiceCommunicationChannelTopic)] + public async Task ServiceCommunicationChannelIssuedEvent(Dictionary issuedMessage) + { + Logger.LogWarning($"数据通讯通道消息消费订阅解析:{issuedMessage.Serialize()}"); + var tempFirstKeyInfo = issuedMessage.FirstOrDefault(); + if (tempFirstKeyInfo.Value == null) + { + return SubscribeAck.Fail(); + } + + ServiceCommunicationTypeEnum serviceCommunication = tempFirstKeyInfo.Key; + bool tempResult = false; + switch (serviceCommunication) + { + case ServiceCommunicationTypeEnum.ArchivalDataIssued: + if(!string.IsNullOrWhiteSpace(tempFirstKeyInfo.Value)) + { + FocusCacheInfos focusCacheInfo = tempFirstKeyInfo.Value.Deserialize(); + tempResult = await SendArchivalDataIssued(focusCacheInfo); + } + break; + default: + throw new Exception("暂不支持该数据通讯通道消息消费订阅解析"); + } + + return tempResult == true ? SubscribeAck.Success() : SubscribeAck.Fail(); + } + + protected async Task SendArchivalDataIssued(FocusCacheInfos focusCacheInfo) + { + try + { + var checkResult = _tcpService.ClientExists(focusCacheInfo.FocusAddress); + if (checkResult) + { + string issuedMessageHexString = ""; + await _tcpService.SendAsync(focusCacheInfo.FocusAddress, Convert.FromHexString(issuedMessageHexString)); + + return true; + } + else + { + return false; + } + } + catch (Exception) + { + throw; + } + } + } +} diff --git a/services/JiShe.CollectBus.Application/Subscribers/SubscriberAnalysisAppService.cs b/services/JiShe.CollectBus.Application/Subscribers/SubscriberAnalysisAppService.cs index 7b3517b..8e99a56 100644 --- a/services/JiShe.CollectBus.Application/Subscribers/SubscriberAnalysisAppService.cs +++ b/services/JiShe.CollectBus.Application/Subscribers/SubscriberAnalysisAppService.cs @@ -1,21 +1,13 @@ -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.IoTDB.Interface; +using JiShe.CollectBus.Common.Helpers; using JiShe.CollectBus.IotSystems.MessageReceiveds; -using JiShe.CollectBus.Kafka.Attributes; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Protocol.Contracts; +using JiShe.CollectBus.Protocol; +using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto; +using JiShe.CollectBus.Protocol.Dto; using JiShe.CollectBus.Protocol.Interfaces; using JiShe.CollectBus.Protocol3761; -using JiShe.CollectBus.Repository.MeterReadingRecord; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using System; -using System.Threading.Tasks; -using JiShe.CollectBus.Protocol; using TouchSocket.Sockets; -using JiShe.CollectBus.Protocol.Dto; -using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto; namespace JiShe.CollectBus.Subscribers { @@ -24,20 +16,19 @@ namespace JiShe.CollectBus.Subscribers private readonly ILogger _logger; private readonly ITcpService _tcpService; private readonly IServiceProvider _serviceProvider; - private readonly IMeterReadingRecordRepository _meterReadingRecordsRepository; - private readonly IIoTDbProvider _dbProvider; + private readonly IoTDBSessionPoolProvider _dbProvider; private readonly IProtocolService _protocolService; + public SubscriberAnalysisAppService(ILogger logger, ITcpService tcpService, IServiceProvider serviceProvider, - IIoTDbProvider dbProvider, - IMeterReadingRecordRepository meterReadingRecordsRepository, IProtocolService protocolService) + IoTDBSessionPoolProvider dbProvider, + IProtocolService protocolService) { _logger = logger; _tcpService = tcpService; _serviceProvider = serviceProvider; - _meterReadingRecordsRepository = meterReadingRecordsRepository; _dbProvider = dbProvider; _protocolService = protocolService; } @@ -47,7 +38,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN00HReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN00HReceivedEventName)] public async Task ReceivedAFN00Event(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -83,7 +74,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN01HReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN01HReceivedEventName)] public async Task ReceivedAFN01Event(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -120,7 +111,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN02HReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN02HReceivedEventName)] public async Task ReceivedAFN02Event(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -162,7 +153,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN03HReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN03HReceivedEventName)] public async Task ReceivedAFN03Event(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -200,7 +191,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN04HReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN04HReceivedEventName)] public async Task ReceivedAFN04Event(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -238,7 +229,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN05HReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN05HReceivedEventName)] public async Task ReceivedAFN05Event(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -276,7 +267,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN09HReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN09HReceivedEventName)] public async Task ReceivedAFN09Event(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -313,7 +304,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN0AHReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN0AHReceivedEventName)] public async Task ReceivedAFN0AEvent(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -350,7 +341,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN0BHReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN0BHReceivedEventName)] public async Task ReceivedAFN0BEvent(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -387,7 +378,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN0CHReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN0CHReceivedEventName)] public async Task ReceivedAFN0CEvent(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -425,7 +416,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN0DHReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN0DHReceivedEventName)] public async Task ReceivedAFN0DEvent(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -463,7 +454,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN0EHReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN0EHReceivedEventName)] public async Task ReceivedAFN0EEvent(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); @@ -501,7 +492,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.SubscriberAFN10HReceivedEventNameTemp)] + [KafkaSubscribe(KafkaTopicConsts.SubscriberAFN10HReceivedEventName)] public async Task ReceivedAFN0HEvent(MessageProtocolAnalysis receivedMessage) { var protocolPlugin = await _protocolService.GetProtocolServiceAsync(receivedMessage.DeviceNo); diff --git a/services/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs b/services/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs deleted file mode 100644 index 670fd7c..0000000 --- a/services/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs +++ /dev/null @@ -1,172 +0,0 @@ -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Models; -using JiShe.CollectBus.Interceptors; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IotSystems.MessageReceiveds; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.Kafka.Attributes; -using JiShe.CollectBus.Kafka.Internal; -using JiShe.CollectBus.Protocol.Interfaces; -using JiShe.CollectBus.Protocol3761; -using JiShe.CollectBus.Repository.MeterReadingRecord; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using TouchSocket.Sockets; -using Volo.Abp.Domain.Repositories; - -namespace JiShe.CollectBus.Subscribers -{ - public class SubscriberAppService : CollectBusAppService, ISubscriberAppService, IKafkaSubscribe - { - private readonly ILogger _logger; - private readonly ITcpService _tcpService; - private readonly IServiceProvider _serviceProvider; - private readonly IRepository _messageReceivedLoginEventRepository; - private readonly IRepository _messageReceivedHeartbeatEventRepository; - private readonly IMeterReadingRecordRepository _meterReadingRecordsRepository; - private readonly IIoTDbProvider _dbProvider; - private readonly IProtocolService _protocolService; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// The TCP service. - /// The service provider. - /// The message received login event repository. - /// The message received heartbeat event repository. - /// The device repository. - public SubscriberAppService(ILogger logger, - ITcpService tcpService, - IServiceProvider serviceProvider, - IRepository messageReceivedLoginEventRepository, - IRepository messageReceivedHeartbeatEventRepository, - IIoTDbProvider dbProvider, - IMeterReadingRecordRepository meterReadingRecordsRepository, IProtocolService protocolService) - { - _logger = logger; - _tcpService = tcpService; - _serviceProvider = serviceProvider; - _messageReceivedLoginEventRepository = messageReceivedLoginEventRepository; - _messageReceivedHeartbeatEventRepository = messageReceivedHeartbeatEventRepository; - _meterReadingRecordsRepository = meterReadingRecordsRepository; - _dbProvider = dbProvider; - _protocolService = protocolService; - } - - [LogIntercept] - [KafkaSubscribe(ProtocolConst.SubscriberLoginIssuedEventName, EnableBatch = true)] - public async Task LoginIssuedEvent(List issuedEventMessages) - { - bool isAck = true; - foreach (var issuedEventMessage in issuedEventMessages) - { - //var loginEntity = await _messageReceivedLoginEventRepository.FirstOrDefaultAsync(a => a.MessageId == issuedEventMessage.MessageId); - //if (loginEntity == null) - //{ - // isAck=false; - // break; - //} - - //loginEntity.AckTime = Clock.Now; - //loginEntity.IsAck = true; - //await _messageReceivedLoginEventRepository.UpdateAsync(loginEntity); - } - // TODO:暂时ACK,等后续处理是否放到私信队列中 - return isAck? SubscribeAck.Success(): SubscribeAck.Fail(); - } - - [KafkaSubscribe(ProtocolConst.SubscriberHeartbeatIssuedEventName, EnableBatch = true)] - public async Task HeartbeatIssuedEvent(List issuedEventMessages) - { - bool isAck = true; - //foreach (var issuedEventMessage in issuedEventMessages) - //{ - // var heartbeatEntity = await _messageReceivedHeartbeatEventRepository.FirstOrDefaultAsync(a => a.MessageId == issuedEventMessage.MessageId); - // if (heartbeatEntity == null) - // { - // isAck = false; - // break; - // } - // heartbeatEntity.AckTime = Clock.Now; - // heartbeatEntity.IsAck = true; - // await _messageReceivedHeartbeatEventRepository.UpdateAsync(heartbeatEntity); - // } - // TODO:暂时ACK,等后续处理是否放到私信队列中 - return isAck ? SubscribeAck.Success() : SubscribeAck.Fail(); - } - - [KafkaSubscribe(ProtocolConst.SubscriberReceivedEventName)] - public async Task ReceivedEvent(MessageProtocolAnalysis receivedMessage) - { - var currentTime = Clock.Now; - - var protocolPlugin = _serviceProvider.GetKeyedService("StandardProtocolPlugin"); - if (protocolPlugin == null) - { - _logger.LogError("协议不存在!"); - } - else - { - //todo 会根据不同的协议进行解析,然后做业务处理 - //TB3761? tB3761 = protocolPlugin.Analysis3761(receivedMessage.MessageHexString); - //if (tB3761 == null) - //{ - // Logger.LogError($"数据解析失败:{receivedMessage.Serialize()}"); - // return SubscribeAck.Success(); - //} - //if (tB3761.DT == null || tB3761.AFN_FC == null) - //{ - // Logger.LogError($"数据解析失败:{receivedMessage.Serialize()}"); - // return SubscribeAck.Success(); - //} - - //报文入库 - var entity = new MeterReadingRecords() - { - ReceivedMessageHexString = receivedMessage.MessageHexString, - AFN = (AFN)receivedMessage.Data?.AFN_FC.AFN!, - Fn = receivedMessage.Data.DT.Fn, - Pn = 0, - FocusAddress = "", - MeterAddress = "", - }; - - //如果没数据,则插入,有数据则更新 - var updateEntity = await _meterReadingRecordsRepository.FirOrDefaultAsync(entity, currentTime); - if (updateEntity == null) - { - await _meterReadingRecordsRepository.InsertAsync(entity, currentTime); - } - - - //_dbProvider.InsertAsync(); - //todo 查找是否有下发任务 - - //await _messageReceivedEventRepository.InsertAsync(receivedMessage); - - - } - return SubscribeAck.Success(); - } - - [KafkaSubscribe(ProtocolConst.SubscriberHeartbeatReceivedEventName, EnableBatch = true)] - public async Task ReceivedHeartbeatEvent(List receivedHeartbeatMessages) - { - await _messageReceivedHeartbeatEventRepository.InsertManyAsync(receivedHeartbeatMessages); - return SubscribeAck.Success(); - } - - [KafkaSubscribe(ProtocolConst.SubscriberLoginReceivedEventName,EnableBatch =true)] - public async Task ReceivedLoginEvent(List receivedLoginMessages) - { - await _messageReceivedLoginEventRepository.InsertManyAsync(receivedLoginMessages); - return SubscribeAck.Success(); - } - - } -} diff --git a/services/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs b/services/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs index ee5f646..11d7a5c 100644 --- a/services/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs +++ b/services/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs @@ -1,15 +1,6 @@ -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Helpers; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using JiShe.CollectBus.Kafka.Attributes; -using JiShe.CollectBus.Kafka.Internal; +using JiShe.CollectBus.Common.Helpers; using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; using TouchSocket.Sockets; -using static FreeSql.Internal.GlobalFilter; namespace JiShe.CollectBus.Subscribers { @@ -20,7 +11,7 @@ namespace JiShe.CollectBus.Subscribers { private readonly ILogger _logger; private readonly ITcpService _tcpService; - private readonly IIoTDbProvider _dbProvider; + private readonly IoTDBSessionPoolProvider _dbProvider; /// /// Initializes a new instance of the class. @@ -30,7 +21,7 @@ namespace JiShe.CollectBus.Subscribers /// IoTDB数据驱动 public WorkerSubscriberAppService(ILogger logger, ITcpService tcpService, - IIoTDbProvider dbProvider) + IoTDBSessionPoolProvider dbProvider) { _logger = logger; _tcpService = tcpService; @@ -45,7 +36,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName)] + [KafkaSubscribe(KafkaTopicConsts.AmmeterSubscriberWorkerOneMinuteIssuedEventName)] public async Task AmmeterScheduledMeterOneMinuteReadingIssuedEvent(MeterReadingTelemetryPacketInfo receivedMessage) { _logger.LogError($"1分钟采集电表数据下行消息消费队列开始处理:{receivedMessage.Serialize()}"); @@ -58,7 +49,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.AmmeterSubscriberWorkerFiveMinuteIssuedEventName, EnableBatch = true, BatchSize = 500)] + [KafkaSubscribe(KafkaTopicConsts.AmmeterSubscriberWorkerFiveMinuteIssuedEventName, EnableBatch = true, BatchSize = 500)] public async Task AmmeterScheduledMeterFiveMinuteReadingIssuedEvent(MeterReadingTelemetryPacketInfo receivedMessage) { _logger.LogError($"5分钟采集电表数据下行消息消费队列开始处理:{receivedMessage.Serialize()}"); @@ -70,7 +61,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, EnableBatch = true, TaskCount = 30, BatchSize = 500)] + [KafkaSubscribe(KafkaTopicConsts.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, EnableBatch = true, TaskCount = 30, BatchSize = 500)] public async Task AmmeterScheduledMeterFifteenMinuteReadingIssuedEvent(List receivedMessage) { @@ -87,7 +78,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.AmmeterSubscriberWorkerAutoValveControlIssuedEventName, EnableBatch = true, BatchSize = 500)] + [KafkaSubscribe(KafkaTopicConsts.AmmeterSubscriberWorkerAutoValveControlIssuedEventName, EnableBatch = true, BatchSize = 500)] public async Task AmmeterScheduledAutoValveControl(List receivedMessage) { //todo 如果是时段自动阀控,需要检查当前的时间,如果时间在自动阀控时间段内,则发送自动阀控报文,否则不发送,尤其是消息队列阻塞或者延时过长的时候。以免造成生产事故。 @@ -104,7 +95,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.AmmeterSubscriberWorkerOtherIssuedEventName, EnableBatch = true, BatchSize = 500)] + [KafkaSubscribe(KafkaTopicConsts.AmmeterSubscriberWorkerOtherIssuedEventName, EnableBatch = true, BatchSize = 500)] public async Task AmmeterScheduledOther(List receivedMessage) { foreach (var item in receivedMessage) @@ -120,7 +111,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.AmmeterSubscriberWorkerManualValveControlIssuedEventName)] + [KafkaSubscribe(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveControlIssuedEventName)] public async Task AmmeterScheduledManualValveControl(MeterReadingTelemetryPacketInfo receivedMessage) { _logger.LogError($"电表手动阀控下行消息消费队列开始处理:{receivedMessage.Serialize()}"); @@ -134,7 +125,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName)] + [KafkaSubscribe(KafkaTopicConsts.AmmeterSubscriberWorkerManualValveReadingIssuedEventName)] public async Task AmmeterScheduledManualValveReading(MeterReadingTelemetryPacketInfo receivedMessage) { _logger.LogError($"电表手动抄读下行消息消费队列开始处理:{receivedMessage.Serialize()}"); @@ -148,7 +139,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.AmmeterSubscriberWorkerRetryEventName)] + [KafkaSubscribe(KafkaTopicConsts.AmmeterSubscriberWorkerRetryEventName)] public async Task AmmeterScheduledMeterRetryReadingEvent(MeterReadingTelemetryPacketInfo receivedMessage) { _logger.LogInformation("重试电表数据下行消息消费队列开始处理"); @@ -184,7 +175,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.WatermeterSubscriberWorkerAutoReadingIssuedEventName)] + [KafkaSubscribe(KafkaTopicConsts.WatermeterSubscriberWorkerAutoReadingIssuedEventName)] public async Task WatermeterScheduledAutoReading(MeterReadingTelemetryPacketInfo receivedMessage) { _logger.LogError($"水表数据下行消息消费队列开始处理:{receivedMessage.Serialize()}"); @@ -198,7 +189,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.WatermeterSubscriberWorkerAutoValveControlIssuedEventName)] + [KafkaSubscribe(KafkaTopicConsts.WatermeterSubscriberWorkerAutoValveControlIssuedEventName)] public async Task WatermeterScheduleAutoValveControl(MeterReadingTelemetryPacketInfo receivedMessage) { _logger.LogError($"水表自动阀控下行消息消费队列开始处理:{receivedMessage.Serialize()}"); @@ -212,7 +203,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.WatermeterSubscriberWorkerManualValveControlIssuedEventName)] + [KafkaSubscribe(KafkaTopicConsts.WatermeterSubscriberWorkerManualValveControlIssuedEventName)] public async Task WatermeterScheduleManualValveControl(MeterReadingTelemetryPacketInfo receivedMessage) { _logger.LogError($"水表手动阀控下行消息消费队列开始处理:{receivedMessage.Serialize()}"); @@ -226,7 +217,7 @@ namespace JiShe.CollectBus.Subscribers /// /// /// - [KafkaSubscribe(ProtocolConst.WatermeterSubscriberWorkerManualValveReadingIssuedEventName)] + [KafkaSubscribe(KafkaTopicConsts.WatermeterSubscriberWorkerManualValveReadingIssuedEventName)] public async Task WatermeterScheduleManualValveReading(MeterReadingTelemetryPacketInfo receivedMessage) { _logger.LogError($"水表手动抄读下行消息消费队列开始处理:{receivedMessage.Serialize()}"); diff --git a/services/JiShe.CollectBus.Application/Workers/CreateToBeIssueTaskWorker.cs b/services/JiShe.CollectBus.Application/Workers/CreateToBeIssueTaskWorker.cs deleted file mode 100644 index d1de00a..0000000 --- a/services/JiShe.CollectBus.Application/Workers/CreateToBeIssueTaskWorker.cs +++ /dev/null @@ -1,43 +0,0 @@ -//using System; -//using System.Threading; -//using System.Threading.Tasks; -//using Hangfire; -//using JiShe.CollectBus.Common.Consts; -//using JiShe.CollectBus.ScheduledMeterReading; -//using Microsoft.Extensions.Logging; -//using Volo.Abp.BackgroundWorkers.Hangfire; -//using Volo.Abp.DependencyInjection; -//using Volo.Abp.Uow; - -//namespace JiShe.CollectBus.Workers -//{ -// /// -// /// 构建待处理的下发指令任务处理 -// /// -// public class CreateToBeIssueTaskWorker : HangfireBackgroundWorkerBase, ITransientDependency, ICollectWorker -// { -// private readonly ILogger _logger; -// private readonly IScheduledMeterReadingService _scheduledMeterReadingService; - -// /// -// /// Initializes a new instance of the class. -// /// -// /// The logger. -// /// 定时任务 -// public CreateToBeIssueTaskWorker(ILogger logger, IScheduledMeterReadingService scheduledMeterReadingService) -// { -// _logger = logger; -// RecurringJobId = nameof(CreateToBeIssueTaskWorker); -// CronExpression = "0 0/1 * * * *"; -// TimeZone = TimeZoneInfo.Local; -// this._scheduledMeterReadingService = scheduledMeterReadingService; -// } - - -// public override async Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken()) -// { -// _logger.LogError($"{DateTime.Now}"); -// // await _scheduledMeterReadingService.CreateToBeIssueTasks(); -// } -// } -//} diff --git a/services/JiShe.CollectBus.Application/Workers/DataDetectionFifteenMinuteWorker.cs b/services/JiShe.CollectBus.Application/Workers/DataDetectionFifteenMinuteWorker.cs deleted file mode 100644 index 392c801..0000000 --- a/services/JiShe.CollectBus.Application/Workers/DataDetectionFifteenMinuteWorker.cs +++ /dev/null @@ -1,39 +0,0 @@ -//using JiShe.CollectBus.ScheduledMeterReading; -//using Microsoft.Extensions.Logging; -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using System.Text; -//using System.Threading; -//using System.Threading.Tasks; -//using Volo.Abp.BackgroundWorkers.Hangfire; -//using Volo.Abp.DependencyInjection; - -//namespace JiShe.CollectBus.Workers -//{ -// /// -// /// 定时数据检测1小时一次 -// /// -// public class DataDetectionFifteenMinuteWorker : HangfireBackgroundWorkerBase, ITransientDependency, ICollectWorker -// { - -// private readonly ILogger _logger; -// private readonly IScheduledMeterReadingService _scheduledMeterReadingService; - -// public DataDetectionFifteenMinuteWorker(ILogger logger, IScheduledMeterReadingService scheduledMeterReadingService) -// { -// _logger = logger; -// RecurringJobId = nameof(CreateToBeIssueTaskWorker); -// CronExpression = "0 0 0/1 * * ?"; -// TimeZone = TimeZoneInfo.Local; -// this._scheduledMeterReadingService = scheduledMeterReadingService; -// } - - -// public override Task DoWorkAsync(CancellationToken cancellationToken = default) -// { -// //throw new NotImplementedException(); -// return Task.CompletedTask; -// } -// } -//} diff --git a/services/JiShe.CollectBus.Application/Workers/EpiCollectWorker.cs b/services/JiShe.CollectBus.Application/Workers/EpiCollectWorker.cs deleted file mode 100644 index 037e2e2..0000000 --- a/services/JiShe.CollectBus.Application/Workers/EpiCollectWorker.cs +++ /dev/null @@ -1,39 +0,0 @@ -//using System; -//using System.Threading; -//using System.Threading.Tasks; -//using Hangfire; -//using JiShe.CollectBus.Common.Attributes; -//using Microsoft.Extensions.Logging; -//using Volo.Abp.BackgroundWorkers.Hangfire; -//using Volo.Abp.DependencyInjection; -//using Volo.Abp.Uow; - -//namespace JiShe.CollectBus.Workers -//{ -// public class EpiCollectWorker : HangfireBackgroundWorkerBase, ITransientDependency,ICollectWorker -// { -// private readonly ILogger _logger; - -// /// -// /// Initializes a new instance of the class. -// /// -// /// The logger. -// public EpiCollectWorker(ILogger logger) -// { -// _logger = logger; -// RecurringJobId = nameof(EpiCollectWorker); -// CronExpression = Cron.Daily(); - -// } - - -// public override Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken()) -// { -// using (var uow = LazyServiceProvider.LazyGetRequiredService().Begin()) -// { -// Logger.LogInformation("Executed MyLogWorker..!"); -// return Task.CompletedTask; -// } -// } -// } -//} diff --git a/services/JiShe.CollectBus.Application/Workers/ScheduledMeterReadingBackGroundWorkService.cs b/services/JiShe.CollectBus.Application/Workers/ScheduledMeterReadingBackGroundWorkService.cs new file mode 100644 index 0000000..b9d0a25 --- /dev/null +++ b/services/JiShe.CollectBus.Application/Workers/ScheduledMeterReadingBackGroundWorkService.cs @@ -0,0 +1,34 @@ +using JiShe.CollectBus.ScheduledMeterReading; +using JiShe.ServicePro.Core; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.Workers +{ + /// + /// 定时抄表 + /// + /// 抄读服务 + /// + public class ScheduledMeterReadingBackGroundWorkService(IScheduledMeterReadingService scheduledMeterReadingService, ILogger logger) : SystemBackGroundWorkService(logger) + { + protected override TimeSpan GetInterval() + { + // 返回执行间隔时间 + return TimeSpan.FromSeconds(10); + } + + protected override async Task DoWorkAsync(CancellationToken cancellationToken) + { + // 这里编写具体的后台任务逻辑,例如从数据库中读取数据、发送网络请求等 + // 这里的逻辑会每隔一段时间执行一次,时间间隔由 GetInterval 方法返回的值决定 + Logger.LogWarning($"定时抄读后台任务执行中...{DateTime.Now.ToString()}"); + //await scheduledMeterReadingService.CreateToBeIssueTasks(); + } + } +} diff --git a/services/JiShe.CollectBus.Application/Workers/SubscriberFifteenMinuteWorker.cs b/services/JiShe.CollectBus.Application/Workers/SubscriberFifteenMinuteWorker.cs deleted file mode 100644 index a7ca7c9..0000000 --- a/services/JiShe.CollectBus.Application/Workers/SubscriberFifteenMinuteWorker.cs +++ /dev/null @@ -1,48 +0,0 @@ -//using System; -//using System.Threading; -//using System.Threading.Tasks; -//using Hangfire; -//using JiShe.CollectBus.ScheduledMeterReading; -//using Microsoft.Extensions.Logging; -//using Volo.Abp.BackgroundWorkers.Hangfire; -//using Volo.Abp.DependencyInjection; -//using Volo.Abp.Uow; - -//namespace JiShe.CollectBus.Workers -//{ -// /// -// /// 15分钟采集数据 -// /// -// public class SubscriberFifteenMinuteWorker : HangfireBackgroundWorkerBase, ITransientDependency, ICollectWorker -// { -// private readonly ILogger _logger; -// private readonly IScheduledMeterReadingService _scheduledMeterReadingService; - -// /// -// /// Initializes a new instance of the class. -// /// -// /// The logger. -// /// 定时任务 -// public SubscriberFifteenMinuteWorker(ILogger logger, IScheduledMeterReadingService scheduledMeterReadingService) -// { -// _logger = logger; -// RecurringJobId = nameof(SubscriberFifteenMinuteWorker); -// CronExpression = "0 0/15 * * * *"; -// TimeZone = TimeZoneInfo.Local; -// this._scheduledMeterReadingService = scheduledMeterReadingService; -// } - - -// public override async Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken()) -// { -// //await _scheduledMeterReadingService.AmmeterScheduledMeterFifteenMinuteReading(); -// //await _scheduledMeterReadingService.WatermeterScheduledMeterFifteenMinuteReading(); - -// //using (var uow = LazyServiceProvider.LazyGetRequiredService().Begin()) -// //{ -// // Logger.LogInformation("Executed MyLogWorker..!"); -// // return Task.CompletedTask; -// //} -// } -// } -//} diff --git a/services/JiShe.CollectBus.Application/Workers/SubscriberFiveMinuteWorker.cs b/services/JiShe.CollectBus.Application/Workers/SubscriberFiveMinuteWorker.cs deleted file mode 100644 index 119421e..0000000 --- a/services/JiShe.CollectBus.Application/Workers/SubscriberFiveMinuteWorker.cs +++ /dev/null @@ -1,42 +0,0 @@ -//using System; -//using System.Threading; -//using System.Threading.Tasks; -//using Hangfire; -//using JiShe.CollectBus.ScheduledMeterReading; -//using Microsoft.Extensions.Logging; -//using Volo.Abp.BackgroundWorkers.Hangfire; -//using Volo.Abp.DependencyInjection; -//using Volo.Abp.Uow; - -//namespace JiShe.CollectBus.Workers -//{ -// /// -// /// 5分钟采集数据 -// /// -// public class SubscriberFiveMinuteWorker : HangfireBackgroundWorkerBase, ITransientDependency,ICollectWorker -// { -// private readonly ILogger _logger; -// private readonly IScheduledMeterReadingService _scheduledMeterReadingService; - -// /// -// /// Initializes a new instance of the class. -// /// -// /// The logger. -// /// 定时任务 -// public SubscriberFiveMinuteWorker(ILogger logger, IScheduledMeterReadingService scheduledMeterReadingService) -// { -// _logger = logger; -// RecurringJobId = nameof(SubscriberFiveMinuteWorker); -// CronExpression = "0 0/5 * * * *"; -// TimeZone = TimeZoneInfo.Local; -// this._scheduledMeterReadingService = scheduledMeterReadingService; -// } - - -// public override async Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken()) -// { -// //await _scheduledMeterReadingService.AmmeterScheduledMeterFiveMinuteReading(); -// //await _scheduledMeterReadingService.WatermeterScheduledMeterFiveMinuteReading(); -// } -// } -//} diff --git a/services/JiShe.CollectBus.Application/Workers/SubscriberOneMinuteWorker.cs b/services/JiShe.CollectBus.Application/Workers/SubscriberOneMinuteWorker.cs deleted file mode 100644 index 419a681..0000000 --- a/services/JiShe.CollectBus.Application/Workers/SubscriberOneMinuteWorker.cs +++ /dev/null @@ -1,44 +0,0 @@ -//using System; -//using System.Threading; -//using System.Threading.Tasks; -//using Hangfire; -//using JiShe.CollectBus.ScheduledMeterReading; -//using Microsoft.Extensions.Logging; -//using Volo.Abp.BackgroundWorkers.Hangfire; -//using Volo.Abp.DependencyInjection; -//using Volo.Abp.Uow; - -//namespace JiShe.CollectBus.Workers -//{ -// /// -// /// 1分钟采集数据 -// /// -// public class SubscriberOneMinuteWorker : HangfireBackgroundWorkerBase, ITransientDependency,ICollectWorker -// { -// private readonly ILogger _logger; -// private readonly IScheduledMeterReadingService _scheduledMeterReadingService; - -// /// -// /// Initializes a new instance of the class. -// /// -// /// The logger. -// /// 定时任务 -// public SubscriberOneMinuteWorker(ILogger logger, IScheduledMeterReadingService scheduledMeterReadingService) -// { -// _logger = logger; -// RecurringJobId = nameof(SubscriberOneMinuteWorker); -// CronExpression = "0 0/1 * * * *"; -// TimeZone = TimeZoneInfo.Local; -// this._scheduledMeterReadingService = scheduledMeterReadingService; -// } - - -// public override async Task DoWorkAsync(CancellationToken cancellationToken = new CancellationToken()) -// { -// //await _scheduledMeterReadingService.AmmeterScheduledMeterOneMinuteReading(); - -// //await _scheduledMeterReadingService.WatermeterScheduledMeterOneMinuteReading(); - -// } -// } -//} diff --git a/services/JiShe.CollectBus.DbMigrator/CollectBusDbMigratorModule.cs b/services/JiShe.CollectBus.DbMigrator/CollectBusDbMigratorModule.cs index 3767b52..22b1ff0 100644 --- a/services/JiShe.CollectBus.DbMigrator/CollectBusDbMigratorModule.cs +++ b/services/JiShe.CollectBus.DbMigrator/CollectBusDbMigratorModule.cs @@ -1,4 +1,4 @@ -using JiShe.CollectBus.MongoDB; +using JiShe.ServicePro.IoTDBManagement; using Volo.Abp.Autofac; using Volo.Abp.Modularity; @@ -6,7 +6,7 @@ namespace JiShe.CollectBus.DbMigrator; [DependsOn( typeof(AbpAutofacModule), - typeof(CollectBusMongoDbModule), + typeof(IoTDBManagementDomainModule), typeof(CollectBusApplicationContractsModule) )] public class CollectBusDbMigratorModule : AbpModule diff --git a/services/JiShe.CollectBus.DbMigrator/DbMigratorHostedService.cs b/services/JiShe.CollectBus.DbMigrator/DbMigratorHostedService.cs index 46f1edb..b41831c 100644 --- a/services/JiShe.CollectBus.DbMigrator/DbMigratorHostedService.cs +++ b/services/JiShe.CollectBus.DbMigrator/DbMigratorHostedService.cs @@ -7,6 +7,7 @@ using JiShe.CollectBus.Data; using Serilog; using Volo.Abp; using Volo.Abp.Data; +using JiShe.ServicePro.IoTDBManagement.SessionPools; namespace JiShe.CollectBus.DbMigrator; @@ -33,10 +34,16 @@ public class DbMigratorHostedService : IHostedService { await application.InitializeAsync(); + //await application + // .ServiceProvider + // .GetRequiredService() + // .MigrateAsync(); + + //初始化IoTDB表模型 await application .ServiceProvider - .GetRequiredService() - .MigrateAsync(); + .GetRequiredService().GetSessionPool(true) + .InitTableSessionModelAsync(); await application.ShutdownAsync(); diff --git a/services/JiShe.CollectBus.DbMigrator/JiShe.CollectBus.DbMigrator.csproj b/services/JiShe.CollectBus.DbMigrator/JiShe.CollectBus.DbMigrator.csproj index f875a56..71eb5a2 100644 --- a/services/JiShe.CollectBus.DbMigrator/JiShe.CollectBus.DbMigrator.csproj +++ b/services/JiShe.CollectBus.DbMigrator/JiShe.CollectBus.DbMigrator.csproj @@ -4,22 +4,23 @@ Exe - net8.0 + net9.0 enable - - - - - + + + + + + - - + + diff --git a/services/JiShe.CollectBus.DbMigrator/appsettings.json b/services/JiShe.CollectBus.DbMigrator/appsettings.json index 90ea5ae..bc9289e 100644 --- a/services/JiShe.CollectBus.DbMigrator/appsettings.json +++ b/services/JiShe.CollectBus.DbMigrator/appsettings.json @@ -1,5 +1,15 @@ { "ConnectionStrings": { "Default": "mongodb://admin:collectbus_mongodb_jishe@118.190.144.92:37017/JiSheCollectBus?authSource=admin" + }, + "IoTDBOptions": { + "UserName": "root", + "Password": "Lixiao@1980", + "TreeModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ], + "TableModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ], + "PoolSize": 32, + "TableModelDataBaseName": "energy", + "OpenDebugMode": true, + "UseTableSessionPoolByDefault": false } } diff --git a/services/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs b/services/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs deleted file mode 100644 index 796f328..0000000 --- a/services/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs +++ /dev/null @@ -1,41 +0,0 @@ -using JiShe.CollectBus.Analyzers.Shared; -using JiShe.CollectBus.IoTDB.Attributes; -using JiShe.CollectBus.IoTDB.Model; -using System; - -namespace JiShe.CollectBus.Ammeters -{ - [SourceAnalyzers(EntityTypeEnum.TableModel)] - public class ElectricityMeter : IoTEntity - { - [ATTRIBUTEColumn] - public string MeterModel { get; set; } - - /// - /// 下发消息内容 - /// - [FIELDColumn] - public string IssuedMessageHexString { get; set; } - - ///// - ///// 下发消息Id - ///// - //[FIELDColumn] - //public string IssuedMessageId { get; set; } - - [FIELDColumn] - public double Voltage { get; set; } - - [FIELDColumn] - public double Current { get; set; } - - [FIELDColumn] - public double Power { get; set; } - - [FIELDColumn] - public double? Currentd { get; set; } - - [FIELDColumn] - public DateTime? CurrentdDateTime { get; set; } - } -} diff --git a/services/JiShe.CollectBus.Domain/Ammeters/ElectricityMeterTreeModel.cs b/services/JiShe.CollectBus.Domain/Ammeters/ElectricityMeterTreeModel.cs deleted file mode 100644 index 6eb5ef4..0000000 --- a/services/JiShe.CollectBus.Domain/Ammeters/ElectricityMeterTreeModel.cs +++ /dev/null @@ -1,38 +0,0 @@ -using JiShe.CollectBus.Analyzers.Shared; -using JiShe.CollectBus.IoTDB.Attributes; -using JiShe.CollectBus.IoTDB.Model; -using System; - -namespace JiShe.CollectBus.Ammeters -{ - [SourceAnalyzers(EntityTypeEnum.TreeModel)] - public class ElectricityMeterTreeModel : IoTEntity - { - [ATTRIBUTEColumn] - public string MeterModel { get; set; } - - /// - /// 下发消息内容 - /// - [FIELDColumn] - public string IssuedMessageHexString { get; set; } - - ///// - ///// 下发消息Id - ///// - //[FIELDColumn] - //public string IssuedMessageId { get; set; } - - [FIELDColumn] - public double Voltage { get; set; } - - [FIELDColumn] - public double Current { get; set; } - - [FIELDColumn] - public double Power { get; set; } - - [FIELDColumn] - public double? Currentd { get; set; } - } -} diff --git a/services/JiShe.CollectBus.Domain/CollectBusDomainModule.cs b/services/JiShe.CollectBus.Domain/CollectBusDomainModule.cs index b09cbf8..f461cbc 100644 --- a/services/JiShe.CollectBus.Domain/CollectBusDomainModule.cs +++ b/services/JiShe.CollectBus.Domain/CollectBusDomainModule.cs @@ -6,10 +6,12 @@ using Volo.Abp.Caching; using Volo.Abp.AuditLogging; using Volo.Abp.BackgroundJobs; using Volo.Abp.Emailing; +using JiShe.ServicePro.IoTDBManagement; namespace JiShe.CollectBus; [DependsOn( + typeof(IoTDBManagementDomainModule), typeof(AbpCachingModule) )] public class CollectBusDomainModule : AbpModule diff --git a/services/JiShe.CollectBus.Domain/GlobalUsings.cs b/services/JiShe.CollectBus.Domain/GlobalUsings.cs new file mode 100644 index 0000000..61e52b6 --- /dev/null +++ b/services/JiShe.CollectBus.Domain/GlobalUsings.cs @@ -0,0 +1,8 @@ +// Global using directives + +global using System.Text; +global using Volo.Abp.Modularity; +global using JiShe.ServicePro.Enums; +global using JiShe.ServicePro.FreeRedisProvider; +global using JiShe.ServicePro.Consts; +global using JiShe.ServicePro.Core; \ No newline at end of file diff --git a/services/JiShe.CollectBus.Domain/IotSystems/Ammeters/AmmeterAutoValveControlSetting.cs b/services/JiShe.CollectBus.Domain/IotSystems/Ammeters/AmmeterAutoValveControlSetting.cs index 0116def..5a3179f 100644 --- a/services/JiShe.CollectBus.Domain/IotSystems/Ammeters/AmmeterAutoValveControlSetting.cs +++ b/services/JiShe.CollectBus.Domain/IotSystems/Ammeters/AmmeterAutoValveControlSetting.cs @@ -1,4 +1,5 @@ using JiShe.CollectBus.Common.Models; +using JiShe.ServicePro.FreeRedisProvider; using System; using System.Collections.Generic; using System.Linq; diff --git a/services/JiShe.CollectBus.Domain/IotSystems/Ammeters/AmmeterInfo.cs b/services/JiShe.CollectBus.Domain/IotSystems/Ammeters/AmmeterInfo.cs index a7792c3..8a9d854 100644 --- a/services/JiShe.CollectBus.Domain/IotSystems/Ammeters/AmmeterInfo.cs +++ b/services/JiShe.CollectBus.Domain/IotSystems/Ammeters/AmmeterInfo.cs @@ -1,5 +1,7 @@ using FreeSql.DataAnnotations; using JiShe.CollectBus.Common.Models; +using JiShe.ServicePro.Core; +using JiShe.ServicePro.FreeRedisProvider; using System; using System.Collections.Generic; using System.Linq; @@ -20,7 +22,7 @@ namespace JiShe.CollectBus.IotSystems.Ammeters /// ZSet排序索引分数值,具体值可以根据不同业务场景进行定义,例如时间戳 /// [Column(IsIgnore = true)] - public override long ScoreValue => Common.Helpers.CommonHelper.GetFocusScores(FocusAddress,MeteringCode); + public override long ScoreValue => CommonHelper.GetFocusScores(FocusAddress,MeteringCode); /// /// 电表名称 diff --git a/services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceInfo.cs b/services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceInfo.cs deleted file mode 100644 index d21a90f..0000000 --- a/services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceInfo.cs +++ /dev/null @@ -1,174 +0,0 @@ -using FreeSql.DataAnnotations; -using JiShe.CollectBus.Analyzers.Shared; -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Models; -using JiShe.CollectBus.IoTDB.Attributes; -using JiShe.CollectBus.IoTDB.Model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.IotSystems.Devices -{ - /// - /// 设备信息 - /// - public class DeviceInfo : DeviceCacheBasicModel - { - /// - /// 关系映射标识,用于ZSet的Member字段和Set的Value字段,具体值可以根据不同业务场景进行定义 - /// - [Column(IsIgnore = true)] - public override string MemberId => $"{FocusAddress}:{MeteringCode}"; - - /// - /// ZSet排序索引分数值,具体值可以根据不同业务场景进行定义,例如时间戳 - /// - [Column(IsIgnore = true)] - public override long ScoreValue => Common.Helpers.CommonHelper.GetFocusScores(FocusAddress, MeteringCode); - - /// - /// 通讯方案: - /// NB-IOT常德水表、NB-IOT泽联电表、GPRS华立水表、 - /// RS-485、无线、载波 - /// - public string LinkType { get; set; } - - /// - /// HaveValve: 是否带阀 (0 不带阀, 1 带阀) - /// 注意:NULL表示未设置 - /// - public int? HaveValve { get; set; } - - /// - /// 设备类型: 水表\气表、流量计 - /// - public string MeterTypeName { get; set; } - - /// - /// 倍率 - /// - public decimal TimesRate { get; set; } - - /// - /// 电表名称 - /// - public string Name { get; set; } - - /// - /// 集中器地址 - /// - public string Address { get; set; } - - /// - /// 集中器区域代码 - /// - public string AreaCode { get; set; } - - /// - /// 仅当MeterType为电表时,电表类别 (1单相、2三相三线、3三相四线), - /// 07协议: 开合闸指令(1A开闸断电,1C单相表合闸,1B多相表合闸) 645 2007 表 - /// 97协议://true(合闸);false(跳闸) 545 1997 没有单相多相 之分 "true" ? "9966" : "3355" - /// - public int TypeName { get; set; } - - /// - /// 跳合闸状态字段: 0 合闸,1 跳闸 - /// 电表:TripState (0 合闸-通电, 1 断开、跳闸); - /// - public int TripState { get; set; } - - /// - /// 规约 -电表default(30) 1:97协议,30:07协议 - /// - public int? Protocol { get; set; } - - /// - /// 一个集中器下的[MeteringCode]必须唯一。 PN - /// - public int MeteringCode { get; set; } - - /// - /// 表通信地址 - /// - public string MeterAddress { get; set; } - - /// - /// 波特率 default(2400) - /// - public int Baudrate { get; set; } - - /// - /// MeteringPort 端口就几个可以枚举。 - /// - public int MeteringPort { get; set; } - - /// - /// 电表密码 - /// - public string Password { get; set; } - - /// - /// 该电表方案下采集项,JSON格式,如:["0D_80","0D_80"] - /// - [Column(IsIgnore = true)] - public List ItemCodes { get; set; } - - /// - /// State表状态: - /// 0新装(未下发),1运行(档案下发成功时设置状态值1), 2暂停, 100销表(销表后是否重新启用) - /// 特定:State -1 已删除 - /// - public int State { get; set; } - - /// - /// 是否自动采集(0:主动采集,1:自动采集) - /// - public int AutomaticReport { get; set; } - - /// - /// 该电表方案下采集项编号 - /// - public string DataTypes { get; set; } - - /// - /// 品牌型号 - /// - public string BrandType { get; set; } - - /// - /// 采集器编号 - /// - public string GatherCode { get; set; } - - /// - /// 是否特殊表,1是特殊电表 - /// - public int Special { get; set; } - - /// - /// 费率类型,单、多 (SingleRate :单费率(单相表1),多费率(其他0) ,与TypeName字段无关) - /// SingleRate ? "单" : "复" - /// [SingleRate] --0 复费率 false , 1 单费率 true (与PayPlanID保持一致) - ///对应 TB_PayPlan.Type: 1复费率,2单费率 - /// - public bool SingleRate { get; set; } - - /// - /// 项目ID - /// - public int ProjectID { get; set; } - - /// - /// 数据库业务ID - /// - public int DatabaseBusiID { get; set; } - - /// - /// 是否异常集中器 0:正常,1异常 - /// - public int AbnormalState { get; set; } - } -} diff --git a/services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceTableModelDataInfo.cs b/services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceTableModelDataInfo.cs deleted file mode 100644 index c909930..0000000 --- a/services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceTableModelDataInfo.cs +++ /dev/null @@ -1,23 +0,0 @@ -using JiShe.CollectBus.Analyzers.Shared; -using JiShe.CollectBus.IoTDB.Attributes; -using JiShe.CollectBus.IoTDB.Model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.IotSystems.Devices -{ - /// - /// 设备表型数据信息 - /// - [SourceAnalyzers(EntityTypeEnum.TableModel)] - [IgnoreInitTable] - public class DeviceTableModelDataInfo : IoTEntity - { - - [FIELDColumn] - public bool xfdsa { get; set; } - } -} diff --git a/services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceTreeModelDataInfo.cs b/services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceTreeModelDataInfo.cs deleted file mode 100644 index 9fcb850..0000000 --- a/services/JiShe.CollectBus.Domain/IotSystems/Devices/DeviceTreeModelDataInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using JiShe.CollectBus.Analyzers.Shared; -using JiShe.CollectBus.IoTDB.Attributes; -using JiShe.CollectBus.IoTDB.Model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.IotSystems.Devices -{ - /// - /// 设备树模型数据信息 - /// - [SourceAnalyzers(EntityTypeEnum.TreeModel)] - public class DeviceTreeModelDataInfo : IoTEntity - { - - [FIELDColumn] - public bool xfdsa { get; set; } - } -} diff --git a/services/JiShe.CollectBus.Domain/IotSystems/MessageIssueds/ScheduledMeterReadingIssuedEventMessage.cs b/services/JiShe.CollectBus.Domain/IotSystems/MessageIssueds/ScheduledMeterReadingIssuedEventMessage.cs deleted file mode 100644 index 41ba7ea..0000000 --- a/services/JiShe.CollectBus.Domain/IotSystems/MessageIssueds/ScheduledMeterReadingIssuedEventMessage.cs +++ /dev/null @@ -1,38 +0,0 @@ -using JiShe.CollectBus.Common.Enums; -using System; -using Volo.Abp.Domain.Entities; - -namespace JiShe.CollectBus.IotSystems.MessageIssueds -{ - /// - /// 定时抄读Kafka消息实体,1分钟、5分钟、15分钟 - /// - public class ScheduledMeterReadingIssuedEventMessage - { - /// - /// 下发消息内容 - /// - public string MessageHexString { get; set; } - - /// - /// 集中器编号 - /// - public string FocusAddress { get; set; } - - /// - /// 采集时间间隔,通过Kafka主题区分(分钟,如15) - /// - public string TimeDensity { get; set; } - - /// - /// 消息Id - /// - public string MessageId { get; set; } - - /// - /// 最后一次消息Id,用于在消费消息时检查上一个任务是否处理完。 - /// - public string LastMessageId { get; set; } - - } -} diff --git a/services/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingRecords.cs b/services/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingRecords.cs index b1f1112..f0fb069 100644 --- a/services/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingRecords.cs +++ b/services/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingRecords.cs @@ -1,4 +1,5 @@ using JiShe.CollectBus.Common.Enums; +using JiShe.ServicePro.Enums; using System; using System.Collections.Generic; using System.Linq; diff --git a/services/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingTelemetryPacketInfo.cs b/services/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingTelemetryPacketInfo.cs deleted file mode 100644 index b779022..0000000 --- a/services/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingTelemetryPacketInfo.cs +++ /dev/null @@ -1,191 +0,0 @@ -using JiShe.CollectBus.Analyzers.Shared; -using JiShe.CollectBus.IoTDB.Attributes; -using JiShe.CollectBus.IoTDB.Model; -using System; - -namespace JiShe.CollectBus.IotSystems.MeterReadingRecords -{ - /// - /// 抄读任务数据 - /// - [SourceAnalyzers(EntityTypeEnum.TableModel)] - public class MeterReadingTelemetryPacketInfo : IoTEntity - { - /// - /// 排序索引分数值,具体值可以根据不同业务场景进行定义,例如时间戳、或者某一个固定的标识1 - /// - [FIELDColumn] - public string ScoreValue { get; set; } - - /// - /// 数据包类型 - /// - [FIELDColumn] - public int PacketType { get; set; } - - /// - /// 是否手动操作 - /// - [FIELDColumn] - public bool ManualOrNot { get; set; } - - /// - /// 任务数据唯一标记 - /// - [FIELDColumn] - public string TaskMark { get; set; } - - /// - /// 是否超时 - /// - [FIELDColumn] - public bool IsTimeout { get; set; } = false; - - /// - /// 待抄读时间 - /// - [FIELDColumn] - public DateTime PendingCopyReadTime { get; set; } - - /// - /// 集中器Id - /// - [FIELDColumn] - public int FocusId { get; set; } - - - /// - /// 集中器地址 - /// - [FIELDColumn] - public string FocusAddress { get; set; } - - /// - /// 表地址 - /// - [FIELDColumn] - public string MeterAddress { get; set; } - - /// - /// 数据库业务ID - /// - [FIELDColumn] - public int DatabaseBusiID { get; set; } - - /// - /// AFN功能码 - /// - [FIELDColumn] - public int AFN { get; set; } - - /// - /// 抄读功能码 - /// - [FIELDColumn] - public int Fn { get; set; } - - /// - /// 抄读计量点 - /// - [FIELDColumn] - public int Pn { get; set; } - - /// - /// 采集项编码 - /// - [FIELDColumn] - public string ItemCode { get; set; } - - - /// - /// 子项编码,一般用于透明转发的编码 - /// - [FIELDColumn] - public string SubItemCode { get; set; } - - /// - /// 帧序列域 SEQ - /// - [FIELDColumn] - public int Seq { get; set; } - - /// - /// 地址域A3的主站地址MSA - /// - [FIELDColumn] - public int MSA { get; set; } - - /// - /// 是否发送 - /// - [FIELDColumn] - public bool IsSend { get; set; } - - /// - /// 发送次数 - /// - [FIELDColumn] - public int? SendNum { get; set; } - - /// - /// 下次发送时间 - /// - [FIELDColumn] - public DateTime? NextSendTime { get; set; } - - /// - /// 创建时间 - /// - [FIELDColumn] - public DateTime CreationTime { get; set; } - - /// - /// 下发消息内容 - /// - [FIELDColumn] - public string IssuedMessageHexString { get; set; } - - /// - /// 下发消息Id - /// - [FIELDColumn] - public string IssuedMessageId { get; set; } - - /// - /// 集中器采集密度 - /// - [FIELDColumn] - public int FocusDensity { get; set; } - - /// - /// 消息上报内容 - /// - [FIELDColumn] - public string? ReceivedMessageHexString { get; set; } - - /// - /// 消息上报时间 - /// - [FIELDColumn] - public DateTime? ReceivedTime { get; set; } - - /// - /// 上报消息Id - /// - [FIELDColumn] - public string ReceivedMessageId { get; set; } - - /// - /// 上报报文解析备注,异常情况下才有 - /// - [FIELDColumn] - public string ReceivedRemark { get; set; } - - /// - /// 是否已上报 - /// - [FIELDColumn] - public bool IsReceived { get; set; } - - } -} diff --git a/services/JiShe.CollectBus.Domain/IotSystems/Watermeter/WatermeterInfo.cs b/services/JiShe.CollectBus.Domain/IotSystems/Watermeter/WatermeterInfo.cs index eac70a1..3749089 100644 --- a/services/JiShe.CollectBus.Domain/IotSystems/Watermeter/WatermeterInfo.cs +++ b/services/JiShe.CollectBus.Domain/IotSystems/Watermeter/WatermeterInfo.cs @@ -1,6 +1,8 @@ using FreeSql.DataAnnotations; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.Common.Models; +using JiShe.ServicePro.Enums; +using JiShe.ServicePro.FreeRedisProvider; using System; using System.Collections.Generic; using System.Linq; diff --git a/services/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj b/services/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj index e69f3b6..859f325 100644 --- a/services/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj +++ b/services/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj @@ -3,7 +3,7 @@ - net8.0 + net9.0 enable JiShe.CollectBus @@ -20,21 +20,20 @@ - - - - + - - - - - - + + + + + + + + diff --git a/services/JiShe.CollectBus.Domain/Protocol3761/Dto/AnalysisBaseDto.cs b/services/JiShe.CollectBus.Domain/Protocol3761/Dto/AnalysisBaseDto.cs index c02ee76..6a6e8a2 100644 --- a/services/JiShe.CollectBus.Domain/Protocol3761/Dto/AnalysisBaseDto.cs +++ b/services/JiShe.CollectBus.Domain/Protocol3761/Dto/AnalysisBaseDto.cs @@ -1,4 +1,5 @@ using JiShe.CollectBus.Common.Enums; +using JiShe.ServicePro.Enums; using System; using System.Collections.Generic; using System.Data; @@ -44,7 +45,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Protocol.Dto /// /// 项目Id /// - public int ProjectId { get; set; } + public string ProjectId { get; set; } /// /// 设备ID(电表ID,水表ID,气表ID,集中器ID等) diff --git a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusDbContext.cs b/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusDbContext.cs deleted file mode 100644 index bf57b27..0000000 --- a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusDbContext.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Volo.Abp.Data; -using Volo.Abp.EntityFrameworkCore; - -namespace JiShe.CollectBus.EntityFrameworkCore; - -[ConnectionStringName("Default")] -public class CollectBusDbContext : - AbpDbContext -{ - /* Add DbSet properties for your Aggregate Roots / Entities here. */ - - - #region Entities from the modules - - /* Notice: We only implemented IIdentityProDbContext and ISaasDbContext - * and replaced them for this DbContext. This allows you to perform JOIN - * queries for the entities of these modules over the repositories easily. You - * typically don't need that for other modules. But, if you need, you can - * implement the DbContext interface of the needed module and use ReplaceDbContext - * attribute just like IIdentityProDbContext and ISaasDbContext. - * - * More info: Replacing a DbContext of a module ensures that the related module - * uses this DbContext on runtime. Otherwise, it will use its own DbContext class. - */ - - #endregion - - public CollectBusDbContext(DbContextOptions options) - : base(options) - { - - } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - } -} diff --git a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusDbContextFactory.cs b/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusDbContextFactory.cs deleted file mode 100644 index 07cc790..0000000 --- a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusDbContextFactory.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.IO; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Design; -using Microsoft.Extensions.Configuration; - -namespace JiShe.CollectBus.EntityFrameworkCore; - -/* This class is needed for EF Core console commands - * (like Add-Migration and Update-Database commands) */ -public class CollectBusDbContextFactory : IDesignTimeDbContextFactory -{ - public CollectBusDbContext CreateDbContext(string[] args) - { - var configuration = BuildConfiguration(); - - CollectBusEfCoreEntityExtensionMappings.Configure(); - - var builder = new DbContextOptionsBuilder() - .UseMySql(configuration.GetConnectionString("Default"), MySqlServerVersion.LatestSupportedServerVersion); - - return new CollectBusDbContext(builder.Options); - } - - private static IConfigurationRoot BuildConfiguration() - { - var builder = new ConfigurationBuilder() - .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../JiShe.CollectBus.DbMigrator/")) - .AddJsonFile("appsettings.json", optional: false); - - return builder.Build(); - } -} diff --git a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusEfCoreEntityExtensionMappings.cs b/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusEfCoreEntityExtensionMappings.cs deleted file mode 100644 index 8f002f3..0000000 --- a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusEfCoreEntityExtensionMappings.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Volo.Abp.Identity; -using Volo.Abp.ObjectExtending; -using Volo.Abp.Threading; - -namespace JiShe.CollectBus.EntityFrameworkCore; - -public static class CollectBusEfCoreEntityExtensionMappings -{ - private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); - - public static void Configure() - { - CollectBusGlobalFeatureConfigurator.Configure(); - CollectBusModuleExtensionConfigurator.Configure(); - - OneTimeRunner.Run(() => - { - /* You can configure extra properties for the - * entities defined in the modules used by your application. - * - * This class can be used to map these extra properties to table fields in the database. - * - * USE THIS CLASS ONLY TO CONFIGURE EF CORE RELATED MAPPING. - * USE CollectBusModuleExtensionConfigurator CLASS (in the Domain.Shared project) - * FOR A HIGH LEVEL API TO DEFINE EXTRA PROPERTIES TO ENTITIES OF THE USED MODULES - * - * Example: Map a property to a table field: - - ObjectExtensionManager.Instance - .MapEfCoreProperty( - "MyProperty", - (entityBuilder, propertyBuilder) => - { - propertyBuilder.HasMaxLength(128); - } - ); - - * See the documentation for more: - * https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities - */ - }); - } -} diff --git a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusEntityFrameworkCoreModule.cs b/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusEntityFrameworkCoreModule.cs deleted file mode 100644 index bb34e67..0000000 --- a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/CollectBusEntityFrameworkCoreModule.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.AuditLogging.EntityFrameworkCore; -using Volo.Abp.BackgroundJobs.EntityFrameworkCore; -using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.EntityFrameworkCore.MySQL; -using Volo.Abp.FeatureManagement.EntityFrameworkCore; -using Volo.Abp.Identity.EntityFrameworkCore; -using Volo.Abp.Modularity; -using Volo.Abp.PermissionManagement.EntityFrameworkCore; -using Volo.Abp.SettingManagement.EntityFrameworkCore; -using Volo.Abp.BlobStoring.Database.EntityFrameworkCore; - -namespace JiShe.CollectBus.EntityFrameworkCore; - -[DependsOn( - typeof(CollectBusDomainModule), - typeof(AbpPermissionManagementEntityFrameworkCoreModule), - typeof(AbpSettingManagementEntityFrameworkCoreModule), - typeof(AbpEntityFrameworkCoreMySQLModule), - typeof(AbpBackgroundJobsEntityFrameworkCoreModule), - typeof(AbpAuditLoggingEntityFrameworkCoreModule), - typeof(AbpFeatureManagementEntityFrameworkCoreModule), - typeof(AbpIdentityEntityFrameworkCoreModule), - typeof(BlobStoringDatabaseEntityFrameworkCoreModule) - )] -public class CollectBusEntityFrameworkCoreModule : AbpModule -{ - public override void PreConfigureServices(ServiceConfigurationContext context) - { - - CollectBusEfCoreEntityExtensionMappings.Configure(); - } - - public override void ConfigureServices(ServiceConfigurationContext context) - { - context.Services.AddAbpDbContext(options => - { - /* Remove "includeAllEntities: true" to create - * default repositories only for aggregate roots */ - options.AddDefaultRepositories(includeAllEntities: true); - }); - - Configure(options => - { - /* The main point to change your DBMS. - * See also CollectBusDbContextFactory for EF Core tooling. */ - options.UseMySQL(); - }); - } -} diff --git a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/EntityFrameworkCoreCollectBusDbSchemaMigrator.cs b/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/EntityFrameworkCoreCollectBusDbSchemaMigrator.cs deleted file mode 100644 index 2b3509a..0000000 --- a/services/JiShe.CollectBus.EntityFrameworkCore/EntityFrameworkCore/EntityFrameworkCoreCollectBusDbSchemaMigrator.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using JiShe.CollectBus.Data; -using Volo.Abp.DependencyInjection; - -namespace JiShe.CollectBus.EntityFrameworkCore; - -public class EntityFrameworkCoreCollectBusDbSchemaMigrator - : ICollectBusDbSchemaMigrator, ITransientDependency -{ - private readonly IServiceProvider _serviceProvider; - - public EntityFrameworkCoreCollectBusDbSchemaMigrator(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public async Task MigrateAsync() - { - /* We intentionally resolving the CollectBusDbContext - * from IServiceProvider (instead of directly injecting it) - * to properly get the connection string of the current tenant in the - * current scope. - */ - - await _serviceProvider - .GetRequiredService() - .Database - .MigrateAsync(); - } -} diff --git a/services/JiShe.CollectBus.EntityFrameworkCore/FodyWeavers.xml b/services/JiShe.CollectBus.EntityFrameworkCore/FodyWeavers.xml deleted file mode 100644 index 00e1d9a..0000000 --- a/services/JiShe.CollectBus.EntityFrameworkCore/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/services/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.abppkg b/services/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.abppkg deleted file mode 100644 index e1c64f0..0000000 --- a/services/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.abppkg +++ /dev/null @@ -1,3 +0,0 @@ -{ - "role": "lib.ef" -} \ No newline at end of file diff --git a/services/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.csproj b/services/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.csproj deleted file mode 100644 index e6a6338..0000000 --- a/services/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - net8.0 - enable - JiShe.CollectBus - - - - - - - - - - - - - - - - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers - compile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native - - - - diff --git a/services/JiShe.CollectBus.EntityFrameworkCore/Properties/AssemblyInfo.cs b/services/JiShe.CollectBus.EntityFrameworkCore/Properties/AssemblyInfo.cs deleted file mode 100644 index 23df7c8..0000000 --- a/services/JiShe.CollectBus.EntityFrameworkCore/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,2 +0,0 @@ -using System.Runtime.CompilerServices; -[assembly:InternalsVisibleToAttribute("JiShe.CollectBus.EntityFrameworkCore.Tests")] diff --git a/services/JiShe.CollectBus.Migration.Application.Contracts/CollectBusMigrationApplicationContractsModule.cs b/services/JiShe.CollectBus.Migration.Application.Contracts/CollectBusMigrationApplicationContractsModule.cs deleted file mode 100644 index 756ba99..0000000 --- a/services/JiShe.CollectBus.Migration.Application.Contracts/CollectBusMigrationApplicationContractsModule.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Volo.Abp.Application; -using Volo.Abp.Modularity; -using Volo.Abp.Authorization; - -namespace JiShe.CollectBus.Migration; - -[DependsOn( - typeof(CollectBusDomainSharedModule), - typeof(AbpDddApplicationContractsModule), - typeof(AbpAuthorizationModule) - )] -public class CollectBusMigrationApplicationContractsModule : AbpModule -{ - -} diff --git a/services/JiShe.CollectBus.Migration.Application.Contracts/CollectBusMigrationRemoteServiceConsts.cs b/services/JiShe.CollectBus.Migration.Application.Contracts/CollectBusMigrationRemoteServiceConsts.cs deleted file mode 100644 index cc0c233..0000000 --- a/services/JiShe.CollectBus.Migration.Application.Contracts/CollectBusMigrationRemoteServiceConsts.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace JiShe.CollectBus.Migration; - -public class CollectBusMigrationRemoteServiceConsts -{ - public const string RemoteServiceName = "CollectBus"; - - public const string ModuleName = "collectBus"; -} diff --git a/services/JiShe.CollectBus.Migration.Application.Contracts/DataMigration/IDataMigrationService.cs b/services/JiShe.CollectBus.Migration.Application.Contracts/DataMigration/IDataMigrationService.cs deleted file mode 100644 index c6fbb96..0000000 --- a/services/JiShe.CollectBus.Migration.Application.Contracts/DataMigration/IDataMigrationService.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Migration -{ - /// - /// 数据迁移服务 - /// - public interface IDataMigrationService - { - /// - /// 开始迁移 - /// - /// - Task StartMigrationAsync(); - } -} diff --git a/services/JiShe.CollectBus.Migration.Application.Contracts/DataMigration/Options/DataMigrationOptions.cs b/services/JiShe.CollectBus.Migration.Application.Contracts/DataMigration/Options/DataMigrationOptions.cs deleted file mode 100644 index 6e70531..0000000 --- a/services/JiShe.CollectBus.Migration.Application.Contracts/DataMigration/Options/DataMigrationOptions.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Migration -{ - /// - /// 数据迁移配置 - /// - public class DataMigrationOptions - { - /// - /// MongoDb每批处理量 - /// - public int MongoDbDataBatchSize { get; set; } = 1000; - - /// - /// 批量处理通道容量 - /// - public int ChannelCapacity { get; set; } = 100_00; - - /// - /// 数据库 每批处理量 - /// - public int SqlBulkBatchSize { get; set; } = 1000; - - /// - /// 数据库 每批处理超时时间 - /// - public int SqlBulkTimeout { get; set; } = 60; - - /// - /// 处理器数量 - /// - public int ProcessorsCount { get; set; } = 4; - } -} diff --git a/services/JiShe.CollectBus.Migration.Application.Contracts/FodyWeavers.xml b/services/JiShe.CollectBus.Migration.Application.Contracts/FodyWeavers.xml deleted file mode 100644 index 1715698..0000000 --- a/services/JiShe.CollectBus.Migration.Application.Contracts/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/services/JiShe.CollectBus.Migration.Application.Contracts/ICollectWorker.cs b/services/JiShe.CollectBus.Migration.Application.Contracts/ICollectWorker.cs deleted file mode 100644 index e2c4fa8..0000000 --- a/services/JiShe.CollectBus.Migration.Application.Contracts/ICollectWorker.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Migration -{ - public interface ICollectWorker - { - } -} diff --git a/services/JiShe.CollectBus.Migration.Application.Contracts/JiShe.CollectBus.Migration.Application.Contracts.csproj b/services/JiShe.CollectBus.Migration.Application.Contracts/JiShe.CollectBus.Migration.Application.Contracts.csproj deleted file mode 100644 index f3ef574..0000000 --- a/services/JiShe.CollectBus.Migration.Application.Contracts/JiShe.CollectBus.Migration.Application.Contracts.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - net8.0 - enable - JiShe.CollectBus.Migration - True - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/services/JiShe.CollectBus.Migration.Application/CollectBusMigrationAppService.cs b/services/JiShe.CollectBus.Migration.Application/CollectBusMigrationAppService.cs deleted file mode 100644 index 5ec9a4c..0000000 --- a/services/JiShe.CollectBus.Migration.Application/CollectBusMigrationAppService.cs +++ /dev/null @@ -1,20 +0,0 @@ -using JiShe.CollectBus.FreeRedis; -using JiShe.CollectBus.FreeSql; -using JiShe.CollectBus.Localization; -using Microsoft.AspNetCore.Mvc; -using Volo.Abp.Application.Services; - -namespace JiShe.CollectBus.Migration; - -[ApiExplorerSettings(GroupName = CollectBusDomainSharedConsts.Business)] -public abstract class CollectBusMigrationAppService : ApplicationService -{ - public IFreeSqlProvider SqlProvider => LazyServiceProvider.LazyGetRequiredService(); - protected IFreeRedisProvider FreeRedisProvider => LazyServiceProvider.LazyGetService()!; - - protected CollectBusMigrationAppService() - { - LocalizationResource = typeof(CollectBusResource); - ObjectMapperContext = typeof(CollectBusMigrationApplicationModule); - } -} diff --git a/services/JiShe.CollectBus.Migration.Application/CollectBusMigrationApplicationModule.cs b/services/JiShe.CollectBus.Migration.Application/CollectBusMigrationApplicationModule.cs deleted file mode 100644 index 8a82b7e..0000000 --- a/services/JiShe.CollectBus.Migration.Application/CollectBusMigrationApplicationModule.cs +++ /dev/null @@ -1,70 +0,0 @@ -using JiShe.CollectBus.FreeRedis; -using JiShe.CollectBus.FreeSql; -using JiShe.CollectBus.IoTDB; -using Microsoft.Extensions.DependencyInjection; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using Volo.Abp; -using Volo.Abp.Application; -using Volo.Abp.AspNetCore.Mvc; -using Volo.Abp.AutoMapper; -using Volo.Abp.Modularity; - -namespace JiShe.CollectBus.Migration; - -[DependsOn( - typeof(CollectBusDomainModule), - typeof(CollectBusMigrationApplicationContractsModule), - typeof(AbpDddApplicationModule), - typeof(AbpAutoMapperModule), - typeof(CollectBusFreeRedisModule), - typeof(CollectBusFreeSqlModule), - typeof(CollectBusIoTDbModule) -)] -public class CollectBusMigrationApplicationModule : AbpModule -{ - public override void ConfigureServices(ServiceConfigurationContext context) - { - var configuration = context.Services.GetConfiguration(); - - context.Services.AddAutoMapperObjectMapper(); - Configure(options => { options.AddMaps(true); }); - - } - - public override async Task OnApplicationInitializationAsync( - ApplicationInitializationContext context) - { - //var assembly = Assembly.GetExecutingAssembly(); - //var types = assembly.GetTypes().Where(t => typeof(ICollectWorker).IsAssignableFrom(t) && !t.IsInterface) - // .ToList(); - //foreach (var type in types) await context.AddBackgroundWorkerAsync(type); - - //Task.Run(() => - //{ - // //默认初始化表计信息 - // var dbContext = context.ServiceProvider.GetRequiredService(); - // dbContext.InitAmmeterCacheData(); - // //await dbContext.InitWatermeterCacheData(); - //}).ConfigureAwait(false); - - ////下发任务通道构建 - //DataChannelManage.TaskDataChannel = Channel.CreateUnbounded>>(); - - - //// 日志存储通道构建 - //DataChannelManage.LogSaveChannel = Channel.CreateUnbounded(); - - //// 日志刷新通道构建 - //DataChannelManage.LogRefreshChannel = Channel.CreateUnbounded(); - - //// 启动通道任务 - //var _dataChannelManage = context.ServiceProvider.GetRequiredService(); - //_ = _dataChannelManage.LogSaveAsync(DataChannelManage.LogSaveChannel.Reader); - - ////默认初始化表计信息 - //var dbContext = context.ServiceProvider.GetRequiredService(); - //await dbContext.InitAmmeterCacheData(); - } -} \ No newline at end of file diff --git a/services/JiShe.CollectBus.Migration.Application/CollectBusMigrationlicationAutoMapperProfile.cs b/services/JiShe.CollectBus.Migration.Application/CollectBusMigrationlicationAutoMapperProfile.cs deleted file mode 100644 index 4e1a5c6..0000000 --- a/services/JiShe.CollectBus.Migration.Application/CollectBusMigrationlicationAutoMapperProfile.cs +++ /dev/null @@ -1,13 +0,0 @@ -using AutoMapper; - -namespace JiShe.CollectBus; - -public class CollectBusMigrationlicationAutoMapperProfile : Profile -{ - public CollectBusMigrationlicationAutoMapperProfile() - { - /* You can configure your AutoMapper mapping configuration here. - * Alternatively, you can split your mapping configurations - * into multiple profile classes for a better organization. */ - } -} diff --git a/services/JiShe.CollectBus.Migration.Application/DataMigration/DataMigrationService.cs b/services/JiShe.CollectBus.Migration.Application/DataMigration/DataMigrationService.cs deleted file mode 100644 index b85d5b0..0000000 --- a/services/JiShe.CollectBus.Migration.Application/DataMigration/DataMigrationService.cs +++ /dev/null @@ -1,152 +0,0 @@ -using JiShe.CollectBus.IotSystems.MeterReadingRecords; -using LiteDB; -using Microsoft.Extensions.Options; -using System; -using System.Data; -using System.Linq; -using System.Threading.Channels; -using System.Threading.Tasks; -using Volo.Abp.Domain.Repositories; - -namespace JiShe.CollectBus.Migration.DataMigration -{ - /// - /// 数据迁移服务 - /// - public class DataMigrationService: CollectBusMigrationAppService, IDataMigrationService - { - private readonly IRepository _meterReadingRecordsRepository; - private readonly DataMigrationOptions _options; - - - public DataMigrationService(IOptions options, - IRepository meterReadingRecordsRepository) - { - _options = options.Value; - _meterReadingRecordsRepository = meterReadingRecordsRepository; - } - - /// - /// 开始迁移 - /// - /// - public async Task StartMigrationAsync() - { - var rawDataChannel = Channel.CreateBounded(new BoundedChannelOptions(_options.ChannelCapacity) - { - SingleWriter = false, - SingleReader = false, - FullMode = BoundedChannelFullMode.Wait - }); - - var cleanDataChannel = Channel.CreateBounded(new BoundedChannelOptions(_options.ChannelCapacity) - { - SingleWriter = false, - SingleReader = false, - FullMode = BoundedChannelFullMode.Wait - }); - - // 启动生产者和消费者 - var producer = Task.Run(() => ProduceDataAsync(rawDataChannel.Writer)); - - var processors = Enumerable.Range(0, _options.ProcessorsCount) - .Select(_ => Task.Run(() => ProcessDataAsync(rawDataChannel.Reader, cleanDataChannel.Writer))) - .ToArray(); - - var consumer = Task.Run(() => ConsumeDataAsync(cleanDataChannel.Reader)); - - await Task.WhenAll(new[] { producer }.Union(processors).Union(new[] { consumer })); - } - - /// - /// 生产者,生产数据,主要是从MongoDB中读取数据 - /// - /// - /// - private async Task ProduceDataAsync(ChannelWriter writer) - { - //while (true) - //{ - // var queryable = await _meterReadingRecordsRepository.GetQueryableAsync(); - // var batchRecords = queryable.Where(d => d.MigrationStatus == Common.Enums.RecordsDataMigrationStatusEnum.NotStarted) - // .Take(_options.MongoDbDataBatchSize) - // .ToArray(); - - // if (batchRecords == null || batchRecords.Length == 0) - // { - // writer.Complete(); - // break; - // } - - // await writer.WriteAsync(batchRecords); - //} - } - - /// - /// 清洗数据 - /// - /// - /// - /// - private async Task ProcessDataAsync(ChannelReader reader, ChannelWriter writer) - { - await foreach (var batch in reader.ReadAllAsync()) - { - //var dataTable = new DataTable(); - //dataTable.Columns.Add("Id", typeof(string)); - //dataTable.Columns.Add("CleanName", typeof(string)); - //dataTable.Columns.Add("ProcessedTime", typeof(DateTime)); - - //foreach (var doc in batch) - //{ - // // 业务清洗逻辑 - // var cleanName = doc["name"].AsString.Trim().ToUpper(); - // dataTable.Rows.Add( - // doc["_id"].ToString(), - // cleanName, - // DateTime.UtcNow); - //} - - //await writer.WriteAsync(dataTable); - - // 批量更新标记 - //var ids = batch.Select(d => d.Id).ToArray(); - //foreach (var item in batch) - //{ - // item.MigrationStatus = Common.Enums.RecordsDataMigrationStatusEnum.InProgress; - // item.MigrationTime = DateTime.Now; - //} - - //await _meterReadingRecordsRepository.UpdateManyAsync(batch); - } - writer.Complete(); - } - - /// - /// 消费清洗后的数据入库 - /// - /// - /// - private async Task ConsumeDataAsync(ChannelReader reader) - { - //await using var connection = new SqlConnection(_sqlConnectionString); - //await connection.OpenAsync(); - - //await foreach (var dataTable in reader.ReadAllAsync()) - //{ - // using var bulkCopy = new SqlBulkCopy(connection) - // { - // DestinationTableName = "CleanData", - // BatchSize = 5000, - // BulkCopyTimeout = 300 - // }; - - // bulkCopy.ColumnMappings.Add("Id", "Id"); - // bulkCopy.ColumnMappings.Add("CleanName", "CleanName"); - // bulkCopy.ColumnMappings.Add("ProcessedTime", "ProcessedTime"); - - // await bulkCopy.WriteToServerAsync(dataTable); - //} - } - } -} diff --git a/services/JiShe.CollectBus.Migration.Application/FodyWeavers.xml b/services/JiShe.CollectBus.Migration.Application/FodyWeavers.xml deleted file mode 100644 index 1715698..0000000 --- a/services/JiShe.CollectBus.Migration.Application/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/services/JiShe.CollectBus.Migration.Application/JiShe.CollectBus.Migration.Application.csproj b/services/JiShe.CollectBus.Migration.Application/JiShe.CollectBus.Migration.Application.csproj deleted file mode 100644 index bce2a34..0000000 --- a/services/JiShe.CollectBus.Migration.Application/JiShe.CollectBus.Migration.Application.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - net8.0 - enable - JiShe.CollectBus.Migration - True - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/services/JiShe.CollectBusEPO.Application.Contracts/CollectBusEPOApplicationContractsModule.cs b/services/JiShe.CollectBusEPO.Application.Contracts/CollectBusEPOApplicationContractsModule.cs deleted file mode 100644 index 1f538b1..0000000 --- a/services/JiShe.CollectBusEPO.Application.Contracts/CollectBusEPOApplicationContractsModule.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JiShe.CollectBus; - -namespace JiShe.CollectBusEPO -{ - [DependsOn( - typeof(CollectBusDomainSharedModule), - typeof(AbpObjectExtendingModule) - )] - public class CollectBusEPOApplicationContractsModule : AbpModule - { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - CollectBusEPODtoExtensions.Configure(); - } - } -} diff --git a/services/JiShe.CollectBusEPO.Application.Contracts/CollectBusEPODtoExtensions.cs b/services/JiShe.CollectBusEPO.Application.Contracts/CollectBusEPODtoExtensions.cs deleted file mode 100644 index 02e750a..0000000 --- a/services/JiShe.CollectBusEPO.Application.Contracts/CollectBusEPODtoExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace JiShe.CollectBusEPO -{ - public static class CollectBusEPODtoExtensions - { - private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); - - public static void Configure() - { - OneTimeRunner.Run(() => - { - /* You can add extension properties to DTOs - * defined in the depended modules. - * - * Example: - * - * ObjectExtensionManager.Instance - * .AddOrUpdateProperty("Title"); - * - * See the documentation for more: - * https://docs.abp.io/en/abp/latest/Object-Extensions - */ - }); - } - } -} \ No newline at end of file diff --git a/services/JiShe.CollectBusEPO.Application.Contracts/Files/FileUploadOutputDto.cs b/services/JiShe.CollectBusEPO.Application.Contracts/Files/FileUploadOutputDto.cs deleted file mode 100644 index 4ddfa20..0000000 --- a/services/JiShe.CollectBusEPO.Application.Contracts/Files/FileUploadOutputDto.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace JiShe.CollectBusEPO.Files; - -public class FileUploadOutputDto -{ - public string Name { get; set; } - public string Path { get; set; } -} diff --git a/services/JiShe.CollectBusEPO.Application.Contracts/GlobalUsings.cs b/services/JiShe.CollectBusEPO.Application.Contracts/GlobalUsings.cs deleted file mode 100644 index 7e82668..0000000 --- a/services/JiShe.CollectBusEPO.Application.Contracts/GlobalUsings.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Global using directives - -global using System; -global using System.Threading.Tasks; -global using Volo.Abp.Application.Services; -global using Volo.Abp.Authorization.Permissions; -global using Volo.Abp.DependencyInjection; -global using Volo.Abp.Localization; -global using Volo.Abp.Modularity; -global using Volo.Abp.ObjectExtending; -global using Volo.Abp.Threading; \ No newline at end of file diff --git a/services/JiShe.CollectBusEPO.Application.Contracts/JiShe.CollectBusEPO.Application.Contracts.csproj b/services/JiShe.CollectBusEPO.Application.Contracts/JiShe.CollectBusEPO.Application.Contracts.csproj deleted file mode 100644 index 23e52c4..0000000 --- a/services/JiShe.CollectBusEPO.Application.Contracts/JiShe.CollectBusEPO.Application.Contracts.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - net8.0 - JiShe.CollectBusEPO - - - - - - - - - - - - - diff --git a/services/JiShe.CollectBusEPO.Application.Contracts/Jobs/IRecurringJob.cs b/services/JiShe.CollectBusEPO.Application.Contracts/Jobs/IRecurringJob.cs deleted file mode 100644 index 6f4df53..0000000 --- a/services/JiShe.CollectBusEPO.Application.Contracts/Jobs/IRecurringJob.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace JiShe.CollectBusEPO.Jobs -{ - public interface IRecurringJob : ITransientDependency - { - /// - /// 执行任务 - /// - /// - Task ExecuteAsync(); - } -} \ No newline at end of file diff --git a/services/JiShe.CollectBusEPO.Application.Contracts/Permissions/CollectBusEPOPermissionDefinitionProvider.cs b/services/JiShe.CollectBusEPO.Application.Contracts/Permissions/CollectBusEPOPermissionDefinitionProvider.cs deleted file mode 100644 index 1b673bd..0000000 --- a/services/JiShe.CollectBusEPO.Application.Contracts/Permissions/CollectBusEPOPermissionDefinitionProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -using JiShe.CollectBus.Localization; - -namespace JiShe.CollectBusEPO.Permissions -{ - public class CollectBusEPOPermissionDefinitionProvider : PermissionDefinitionProvider - { - public override void Define(IPermissionDefinitionContext context) - { - var myGroup = context.AddGroup(CollectBusEPOPermissions.GroupName, L("Permission:CollectBus")); - } - - private static LocalizableString L(string name) - { - return LocalizableString.Create(name); - } - } -} \ No newline at end of file diff --git a/services/JiShe.CollectBusEPO.Application.Contracts/Permissions/CollectBusEPOPermissions.cs b/services/JiShe.CollectBusEPO.Application.Contracts/Permissions/CollectBusEPOPermissions.cs deleted file mode 100644 index 7acba86..0000000 --- a/services/JiShe.CollectBusEPO.Application.Contracts/Permissions/CollectBusEPOPermissions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Volo.Abp.Reflection; - -namespace JiShe.CollectBusEPO.Permissions -{ - public static class CollectBusEPOPermissions - { - public const string GroupName = "CollectBusEPO"; - - public static string[] GetAll() - { - return ReflectionHelper.GetPublicConstantsRecursively(typeof(CollectBusEPOPermissions)); - } - - } -} \ No newline at end of file diff --git a/services/JiShe.CollectBusEPO.Application/CollectBusEPOAppService.cs b/services/JiShe.CollectBusEPO.Application/CollectBusEPOAppService.cs deleted file mode 100644 index 79d0366..0000000 --- a/services/JiShe.CollectBusEPO.Application/CollectBusEPOAppService.cs +++ /dev/null @@ -1,30 +0,0 @@ -using JiShe.CollectBus; -using JiShe.CollectBus.FreeRedis; -using JiShe.CollectBus.FreeSql; -using JiShe.CollectBus.Localization; -using Microsoft.AspNetCore.Mvc; -using Volo.Abp.Threading; - -namespace JiShe.CollectBusEPO -{ - /* Inherit your application services from this class. - */ - [Authorize] - [ApiExplorerSettings(GroupName = CollectBusDomainSharedConsts.Business)] - public abstract class CollectBusEPOAppService : ApplicationService - { - public IFreeSqlProvider SqlProvider => LazyServiceProvider.LazyGetRequiredService(); - protected IFreeRedisProvider FreeRedisProvider => LazyServiceProvider.LazyGetService()!; - private ICancellationTokenProvider CancellationTokenProvider => - LazyServiceProvider.LazyGetService(NullCancellationTokenProvider.Instance); - - protected CollectBusEPOAppService() - { - LocalizationResource = typeof(CollectBusResource); - ObjectMapperContext = typeof(CollectBusEPOApplicationModule); - } - - - - } -} diff --git a/services/JiShe.CollectBusEPO.Application/CollectBusEPOApplicationModule.cs b/services/JiShe.CollectBusEPO.Application/CollectBusEPOApplicationModule.cs deleted file mode 100644 index 47e8079..0000000 --- a/services/JiShe.CollectBusEPO.Application/CollectBusEPOApplicationModule.cs +++ /dev/null @@ -1,32 +0,0 @@ -using JiShe.CollectBus; -using JiShe.CollectBus.FreeRedis; -using JiShe.CollectBus.FreeSql; -using JiShe.CollectBus.IoTDB; -using JiShe.CollectBus.Kafka; -using Volo.Abp.AspNetCore.Mvc; -using Volo.Abp.AuditLogging; -using Volo.Abp.BackgroundJobs; - -namespace JiShe.CollectBusEPO -{ - [DependsOn( - typeof(CollectBusDomainModule), - typeof(CollectBusFreeRedisModule), - typeof(CollectBusFreeSqlModule), - typeof(CollectBusKafkaModule), - typeof(CollectBusIoTDbModule) - )] - public class CollectBusEPOApplicationModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => { options.AddMaps(); }); - - Configure(options => - { - options.ConventionalControllers.Create(typeof(CollectBusEPOApplicationModule).Assembly); - }); - } - - } -} \ No newline at end of file diff --git a/services/JiShe.CollectBusEPO.Application/EntityHandler.cs b/services/JiShe.CollectBusEPO.Application/EntityHandler.cs deleted file mode 100644 index 7ca2822..0000000 --- a/services/JiShe.CollectBusEPO.Application/EntityHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Volo.Abp.Domain.Entities.Auditing; -using Volo.Abp.Domain.Entities.Events; -using Volo.Abp.EventBus; -using Volo.Abp.MultiTenancy; - -namespace JiShe.CollectBusEPO; - -public class EntityHandler : ILocalEventHandler>>, - ITransientDependency -{ - public IAbpLazyServiceProvider LazyServiceProvider { get; set; } - - protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService(); - - - public Task HandleEventAsync(EntityCreatedEventData> eventData) - { - // TO DO SOMETHING - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/services/JiShe.CollectBusEPO.Application/GlobalUsings.cs b/services/JiShe.CollectBusEPO.Application/GlobalUsings.cs deleted file mode 100644 index eacabe1..0000000 --- a/services/JiShe.CollectBusEPO.Application/GlobalUsings.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Global using directives - -global using Microsoft.AspNetCore.Authorization; -global using System; -global using System.Threading.Tasks; -global using Volo.Abp.Application.Services; -global using Volo.Abp.AutoMapper; -global using Volo.Abp.DependencyInjection; -global using Volo.Abp.Modularity; diff --git a/services/JiShe.CollectBusEPO.Application/JiShe.CollectBusEPO.Application.csproj b/services/JiShe.CollectBusEPO.Application/JiShe.CollectBusEPO.Application.csproj deleted file mode 100644 index 0bb7da1..0000000 --- a/services/JiShe.CollectBusEPO.Application/JiShe.CollectBusEPO.Application.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - - net8.0 - JiShe.CollectBusEPO - - - - - - - - - - - diff --git a/services/JiShe.CollectBusEPO.Application/Properties/AssemblyInfo.cs b/services/JiShe.CollectBusEPO.Application/Properties/AssemblyInfo.cs deleted file mode 100644 index f46e19e..0000000 --- a/services/JiShe.CollectBusEPO.Application/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,2 +0,0 @@ -using System.Runtime.CompilerServices; -[assembly:InternalsVisibleToAttribute("JiShe.MicroService.Application.Tests")] diff --git a/shared/JiShe.CollectBus.Analyzers.Shared/EntityMemberInfo.cs b/shared/JiShe.CollectBus.Analyzers.Shared/EntityMemberInfo.cs deleted file mode 100644 index 523a5df..0000000 --- a/shared/JiShe.CollectBus.Analyzers.Shared/EntityMemberInfo.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace JiShe.CollectBus.Analyzers.Shared -{ - /// - /// 实体成员信息 - /// - public sealed class EntityMemberInfo - { - /// - /// 名称或者路径 - /// - public string NameOrPath { get; set; } - - /// - /// 声明的类型 - /// - public Type DeclaredType { get; set; } - - /// - /// 声明的类型的名称 - /// - public string DeclaredTypeName { get; } - - /// - /// 获取值 - /// - public Func Getter { get; } - - /// - /// 设置值 - /// - public Action Setter { get; } - - /// - /// 自定义Attribute集合 - /// - public List CustomAttributes { get; set; } - - - public EntityMemberInfo( - string nameOrPath, - Type declaredType, - string declaredTypeName, - Func getter, - Action setter) - { - NameOrPath = nameOrPath; - this.DeclaredType = declaredType; - DeclaredTypeName = declaredTypeName; - Getter = getter; - Setter = setter; - } - - public object GetValue(object entity) => Getter(entity); - public void SetValue(object entity, object value) => Setter(entity, value); - } -} diff --git a/shared/JiShe.CollectBus.Analyzers.Shared/EntityTypeEnum.cs b/shared/JiShe.CollectBus.Analyzers.Shared/EntityTypeEnum.cs deleted file mode 100644 index bddaa86..0000000 --- a/shared/JiShe.CollectBus.Analyzers.Shared/EntityTypeEnum.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace JiShe.CollectBus.Analyzers.Shared -{ - /// - /// 实体类型枚举 - /// - public enum EntityTypeEnum - { - /// - /// IoTDB树模型 - /// - TreeModel = 1, - - /// - /// IoTDB表模型 - /// - TableModel = 2, - - /// - /// 其他情况 - /// - Other = 3 - } -} diff --git a/shared/JiShe.CollectBus.Analyzers.Shared/ISourceEntityAccessor.cs b/shared/JiShe.CollectBus.Analyzers.Shared/ISourceEntityAccessor.cs deleted file mode 100644 index 2c3a5b0..0000000 --- a/shared/JiShe.CollectBus.Analyzers.Shared/ISourceEntityAccessor.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text; - -namespace JiShe.CollectBus.Analyzers.Shared -{ - public interface ISourceEntityAccessor - { - /// - /// 实体类名称 - /// - string EntityName { get; } - - /// - /// 实体类型 - /// - EntityTypeEnum? EntityType { get;} - - /// - /// 获取属性值 - /// - /// - /// - /// - object GetPropertyValue(T entity, string propertyName); - - /// - /// 设置属性值 - /// - /// - /// - /// - void SetPropertyValue(T entity, string propertyName, object value); - - /// - /// 属性名称集合 - /// - List PropertyNameList { get; } - - /// - /// 属性信息集合 - /// - List MemberList { get; } - } -} diff --git a/shared/JiShe.CollectBus.Analyzers.Shared/JiShe.CollectBus.Analyzers.Shared.csproj b/shared/JiShe.CollectBus.Analyzers.Shared/JiShe.CollectBus.Analyzers.Shared.csproj deleted file mode 100644 index 3ae7562..0000000 --- a/shared/JiShe.CollectBus.Analyzers.Shared/JiShe.CollectBus.Analyzers.Shared.csproj +++ /dev/null @@ -1,7 +0,0 @@ - - - - netstandard2.0 - - - diff --git a/shared/JiShe.CollectBus.Analyzers.Shared/SourceAnalyzersAttribute.cs b/shared/JiShe.CollectBus.Analyzers.Shared/SourceAnalyzersAttribute.cs deleted file mode 100644 index ad02607..0000000 --- a/shared/JiShe.CollectBus.Analyzers.Shared/SourceAnalyzersAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace JiShe.CollectBus.Analyzers.Shared -{ - /// - /// 标记需要生成源码的类 - /// - [AttributeUsage(AttributeTargets.Class)] - public class SourceAnalyzersAttribute : Attribute - { - public EntityTypeEnum EntityType { get; } - - - public SourceAnalyzersAttribute(EntityTypeEnum entityType) - { - EntityType = entityType; - } - } -} diff --git a/shared/JiShe.CollectBus.Analyzers.Shared/SourceEntityAccessorFactory2.cs b/shared/JiShe.CollectBus.Analyzers.Shared/SourceEntityAccessorFactory2.cs deleted file mode 100644 index 5f28270..0000000 --- a/shared/JiShe.CollectBus.Analyzers.Shared/SourceEntityAccessorFactory2.cs +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/shared/JiShe.CollectBus.Common/Consts/CommonConst.cs b/shared/JiShe.CollectBus.Common/Consts/CommonConst.cs deleted file mode 100644 index c3f1e9a..0000000 --- a/shared/JiShe.CollectBus.Common/Consts/CommonConst.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Consts -{ - /// - /// 常用常量管理 - /// - public class CommonConst - { - - /// - /// Kafka - /// - public const string Kafka = "Kafka"; - - /// - /// 服务器标识 - /// - public const string ServerTagName = $"{Kafka}:ServerTagName"; - - /// - /// Kafka副本数量 - /// - public const string KafkaReplicationFactor = $"{Kafka}:KafkaReplicationFactor"; - - /// - /// Kafka主题分区数量 - /// - public const string NumPartitions = $"{Kafka}:NumPartitions"; - - /// - /// 首次采集时间 - /// - public const string FirstCollectionTime = "FirstCollectionTime"; - - } -} diff --git a/shared/JiShe.CollectBus.Common/Consts/IOTDBDataTypeConst.cs b/shared/JiShe.CollectBus.Common/Consts/IOTDBDataTypeConst.cs deleted file mode 100644 index 872c099..0000000 --- a/shared/JiShe.CollectBus.Common/Consts/IOTDBDataTypeConst.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Consts -{ - /// - /// IOTDB数据树类型 - /// - public class IOTDBDataTypeConst - { - /// - /// 数据 - /// - public const string Data = "Data"; - - /// - /// 事件 - /// - public const string Event = "Event"; - - /// - /// 状态 - /// - public const string Status = "Status"; - - /// - /// 日志 - /// - public const string Log = "Log"; - - /// - /// 参数 - /// - public const string Param= "Param"; - } -} diff --git a/shared/JiShe.CollectBus.Common/Consts/MeteringPortConst.cs b/shared/JiShe.CollectBus.Common/Consts/MeteringPortConst.cs deleted file mode 100644 index c65b027..0000000 --- a/shared/JiShe.CollectBus.Common/Consts/MeteringPortConst.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Consts -{ - /// - /// 计量端口常量 - /// - public class MeteringPortConst - { - /// - /// 计量端口1 - /// - public const int MeteringPortOne = 1; - - /// - /// 计量端口2 - /// - public const int MeteringPortTwo = 2; - } -} diff --git a/shared/JiShe.CollectBus.Common/Consts/ProtocolConst.cs b/shared/JiShe.CollectBus.Common/Consts/ProtocolConst.cs deleted file mode 100644 index 0758899..0000000 --- a/shared/JiShe.CollectBus.Common/Consts/ProtocolConst.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Consts -{ - public class ProtocolConst - { - public const string SubscriberGroup = "jishe.collectbus"; - /// - /// 心跳下行消息主题 - /// - public const string SubscriberHeartbeatIssuedEventName = "issued.heartbeat.event"; - /// - /// 登录下行消息主题 - /// - public const string SubscriberLoginIssuedEventName = "issued.login.event"; - - /// - /// 上行消息主题,测试使用 - /// - public const string SubscriberReceivedEventName = "received.event"; - - /// - /// 心跳上行消息主题 - /// - public const string SubscriberHeartbeatReceivedEventName = "received.heartbeat.event"; - /// - /// 登录上行消息主题 - /// - public const string SubscriberLoginReceivedEventName = "received.login.event"; - - #region 电表消息主题 - /// - /// 1分钟采集电表数据下行消息主题 - /// - public const string AmmeterSubscriberWorkerOneMinuteIssuedEventName = "issued.auto.one.ammeter.event"; - /// - /// 5分钟采集电表数据下行消息主题 - /// - public const string AmmeterSubscriberWorkerFiveMinuteIssuedEventName = "issued.auto.five.ammeter.event"; - /// - /// 15分钟采集电表数据下行消息主题 - /// - public const string AmmeterSubscriberWorkerFifteenMinuteIssuedEventName = "issued.auto.fifteen.ammeter.event"; - - /// - /// 其他采集数据下行消息主题,日冻结,月冻结、集中器版本号、SIM卡号、定时校时等 - /// - public const string AmmeterSubscriberWorkerOtherIssuedEventName = "issued.auto.other.ammeter.event"; - - /// - /// 电表自动阀控 - /// - public const string AmmeterSubscriberWorkerAutoValveControlIssuedEventName = "issued.auto.control.ammeter.event"; - - /// - /// 电表手动阀控 - /// - public const string AmmeterSubscriberWorkerManualValveControlIssuedEventName = "issued.manual.control.ammeter.event"; - - /// - /// 电表手动抄读 - /// - public const string AmmeterSubscriberWorkerManualValveReadingIssuedEventName = "issued.manual.reading.ammeter.event"; - - /// - /// 采集电表数据重试下行消息主题 - /// - public const string AmmeterSubscriberWorkerRetryEventName = "issued.retry.ammeter.event"; - - #endregion - - #region 水表消息主题 - /// - /// 水表数据下行消息主题,由于水表采集频率不高,所以一个主题就够 - /// - public const string WatermeterSubscriberWorkerAutoReadingIssuedEventName = "issued.auto.reading.watermeter.event"; - - /// - /// 水表自动阀控 - /// - public const string WatermeterSubscriberWorkerAutoValveControlIssuedEventName = "issued.auto.control.watermeter.event"; - - /// - /// 水表手动阀控 - /// - public const string WatermeterSubscriberWorkerManualValveControlIssuedEventName = "issued.manual.control.watermeter.event"; - - /// - /// 水表手动抄读 - /// - public const string WatermeterSubscriberWorkerManualValveReadingIssuedEventName = "issued.manual.reading.watermeter.event"; - #endregion - - /// - /// AFN上行主题格式 - /// - public const string AFNTopicNameFormat = "received.afn{0}h.event"; - - /// - /// AFN00H上行主题格式 - /// - public const string SubscriberAFN00HReceivedEventNameTemp = "received.afn00h.event"; - - /// - /// AFN01H上行主题格式 - /// - public const string SubscriberAFN01HReceivedEventNameTemp = "received.afn01h.event"; - - /// - /// AFN02H上行主题格式 - /// - public const string SubscriberAFN02HReceivedEventNameTemp = "received.afn02h.event"; - - /// - /// AFN03H上行主题格式 - /// - public const string SubscriberAFN03HReceivedEventNameTemp = "received.afn03h.event"; - - /// - /// AFN04H上行主题格式 - /// - public const string SubscriberAFN04HReceivedEventNameTemp = "received.afn04h.event"; - - /// - /// AFN05H上行主题格式 - /// - public const string SubscriberAFN05HReceivedEventNameTemp = "received.afn05h.event"; - - /// - /// AFN09H上行主题格式 - /// - public const string SubscriberAFN09HReceivedEventNameTemp = "received.afn09h.event"; - - /// - /// AFN0AH上行主题格式 - /// - public const string SubscriberAFN0AHReceivedEventNameTemp = "received.afn10h.event"; - - /// - /// AFN0BH上行主题格式 - /// - public const string SubscriberAFN0BHReceivedEventNameTemp = "received.afn11h.event"; - - /// - /// AFN0CH上行主题格式 - /// - public const string SubscriberAFN0CHReceivedEventNameTemp = "received.afn12h.event"; - - /// - /// AFN0DH上行主题格式 - /// - public const string SubscriberAFN0DHReceivedEventNameTemp = "received.afn13h.event"; - - /// - /// AFN0EH上行主题格式 - /// - public const string SubscriberAFN0EHReceivedEventNameTemp = "received.afn14h.event"; - - /// - /// AFN10H上行主题格式 - /// - public const string SubscriberAFN10HReceivedEventNameTemp = "received.afn16h.event"; - - - /// - /// 测试主题格式 - /// - public const string TESTTOPIC = "test-topic"; - - /// - /// 测试下发主题格式 - /// - public const string TESTSENDTOPIC = "test-send-topic"; - } -} diff --git a/shared/JiShe.CollectBus.Common/Consts/RedisConst.cs b/shared/JiShe.CollectBus.Common/Consts/RedisConst.cs deleted file mode 100644 index bb143ef..0000000 --- a/shared/JiShe.CollectBus.Common/Consts/RedisConst.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Consts -{ - public class RedisConst - { - /// - /// 缓存基础目录 - /// - public const string CacheBasicDirectoryKey = "CollectBus:"; - - /// - /// 1分钟采集间隔 - /// - public const string OneMinuteAcquisitionTimeInterval = "One"; - - /// - /// 5分钟采集间隔 - /// - public const string FiveMinuteAcquisitionTimeInterval = "Five"; - - /// - /// 15分钟采集间隔 - /// - public const string FifteenMinuteAcquisitionTimeInterval = "Fifteen"; - - /// - /// 设备信息缓存数据,{0}=>系统类型,{1}=>应用服务部署标记 - /// - public const string CacheDeviceInfoHashKey = $"{CacheBasicDirectoryKey}{"{0}:{1}"}:DeviceInfo"; - - /// - /// 设备信息缓存Set缓存Key,{0}=>系统类型,{1}=>应用服务部署标记 - /// - public const string CacheDeviceGroupSetIndexKey = $"{CacheBasicDirectoryKey}{"{0}:{1}"}:DeviceGroupIndex"; - - public const string TaskInfo = "TaskInfo"; - /// - /// 缓存待下发的指令生产任务数据,{0}=>系统类型,{1}=>应用服务部署标记,{2}=>表计类别,{3}=>采集频率 - /// - public const string CacheTasksToBeIssuedKey = $"{CacheBasicDirectoryKey}{"{0}:{1}"}:{TaskInfo}:{"{2}"}:{"{3}"}"; - - public const string TelemetryPacket = "TelemetryPacket"; - /// - /// 缓存表计下发指令数据集,{0}=>系统类型,{1}=>应用服务部署标记,{2}=>表计类别,{3}=>采集频率,{4}=>集中器所在分组,{5}=>时间格式的任务批次 - /// - public const string CacheTelemetryPacketInfoHashKey = $"{CacheBasicDirectoryKey}{"{0}:{1}"}:{TelemetryPacket}:{"{2}"}:{"{3}"}:{"{4}"}:{"{5}"}"; - - /// - /// 缓存表计下发指令数据集索引Set缓存Key,{0}=>系统类型,{1}=>应用服务部署标记,{2}=>表计类别,{3}=>采集频率,{4}=>集中器所在分组,{5}=>时间格式的任务批次 - /// - public const string CacheTelemetryPacketInfoSetIndexKey = $"{CacheBasicDirectoryKey}{"{0}:{1}"}:{TelemetryPacket}:{"{2}"}:SetIndex:{"{3}"}:{"{4}"}:{"{5}"}"; - - /// - /// 缓存表计下发指令数据集排序索引ZSET缓存Key,{0}=>系统类型,{1}=>应用服务部署标记,{2}=>表计类别,{3}=>采集频率,{4}=>集中器所在分组,{5}=>时间格式的任务批次 - /// - public const string CacheTelemetryPacketInfoZSetScoresIndexKey = $"{CacheBasicDirectoryKey}{"{0}:{1}"}:{TelemetryPacket}:{"{2}"}:ZSetScoresIndex:{"{3}"}:{"{4}"}:{"{5}"}"; - - ///// - ///// 缓存设备平衡关系映射结果,{0}=>系统类型,{1}=>应用服务部署标记 - ///// - //public const string CacheDeviceBalanceRelationMapResultKey = $"{CacheBasicDirectoryKey}{"{0}:{1}"}:RelationMap"; - - public const string CacheAmmeterFocusKey = $"{CacheBasicDirectoryKey}CacheAmmeterFocusKey"; - - /// - /// 协议池缓存标识 - /// - public const string ProtocolKey = $"{CacheBasicDirectoryKey}Protocols"; - } -} diff --git a/shared/JiShe.CollectBus.Common/DeviceBalanceControl/DeviceGroupBalanceControl.cs b/shared/JiShe.CollectBus.Common/DeviceBalanceControl/DeviceGroupBalanceControl.cs deleted file mode 100644 index 470efe7..0000000 --- a/shared/JiShe.CollectBus.Common/DeviceBalanceControl/DeviceGroupBalanceControl.cs +++ /dev/null @@ -1,431 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; - -namespace JiShe.CollectBus.Common.DeviceBalanceControl -{ - /// - /// 设备组负载控制 - /// - public class DeviceGroupBalanceControl - { - private static readonly object _syncRoot = new object(); - - private static volatile CacheState _currentCache; - - /// - /// 使用ConcurrentDictionary保证线程安全的设备分组映射 - /// - private sealed class CacheState - { - public readonly ConcurrentDictionary BalancedMapping; - public readonly List[] CachedGroups; - - public CacheState(int groupCount) - { - BalancedMapping = new ConcurrentDictionary(); - CachedGroups = new List[groupCount]; - for (int i = 0; i < groupCount; i++) - { - CachedGroups[i] = new List(); - } - } - } - - /// - /// 初始化或增量更新缓存 - /// - public static void InitializeCache(List deviceList, int groupCount = 30) - { - if (deviceList == null || deviceList.Count <= 0) - { - throw new ArgumentException($"{nameof(InitializeCache)} 设备分组初始化失败,设备数据为空"); - } - - if (groupCount > 60 || groupCount <= 0) - { - groupCount = 60; - } - - lock (_syncRoot) - { - // 首次初始化 - if (_currentCache == null) - { - var newCache = new CacheState(groupCount); - UpdateCacheWithDevices(newCache, deviceList, groupCount); - _currentCache = newCache; - } - // 后续增量更新 - else - { - if (_currentCache.CachedGroups.Length != groupCount) - { - throw new ArgumentException($"{nameof(InitializeCache)} 设备分组初始化完成以后,分组数量不能更改"); - } - - var clonedCache = CloneExistingCache(); - UpdateCacheWithDevices(clonedCache, deviceList, groupCount); - _currentCache = clonedCache; - } - } - } - - /// - /// 带锁的缓存克隆(写入时复制) - /// - private static CacheState CloneExistingCache() - { - var oldCache = _currentCache; - var newCache = new CacheState(oldCache.CachedGroups.Length); - - // 复制已有映射 - foreach (var kvp in oldCache.BalancedMapping) - { - newCache.BalancedMapping.TryAdd(kvp.Key, kvp.Value); - } - - // 复制分组数据 - for (int i = 0; i < oldCache.CachedGroups.Length; i++) - { - newCache.CachedGroups[i].AddRange(oldCache.CachedGroups[i]); - } - - return newCache; - } - - /// - /// 更新设备到缓存 - /// - private static void UpdateCacheWithDevices(CacheState cache, List deviceList, int groupCount) - { - foreach (var deviceId in deviceList) - { - // 原子操作:如果设备不存在则计算分组 - cache.BalancedMapping.GetOrAdd(deviceId, id => - { - int groupId = GetGroupId(id, groupCount); - lock (cache.CachedGroups[groupId]) - { - cache.CachedGroups[groupId].Add(id); - } - return groupId; - }); - } - } - - /// - /// 并行处理泛型数据集(支持动态线程分配) - /// - /// 已经分组的设备信息 - /// 部分或者全部的已经分组的设备集合 - /// 从泛型对象提取deviceId - /// 处理委托(参数:当前对象,线程ID) - /// 可选线程限制 - /// - /// - public static async Task ProcessGenericListAsync( - List items, Func deviceIdSelector, Action processor, int? maxThreads = null) - { - var cache = _currentCache ?? throw new InvalidOperationException("缓存未初始化"); - - // 创建分组任务队列 - var groupQueues = new ConcurrentQueue[cache.CachedGroups.Length]; - for (int i = 0; i < groupQueues.Length; i++) - { - groupQueues[i] = new ConcurrentQueue(); - } - - // 阶段1:分发数据到分组队列 - Parallel.ForEach(items, item => - { - var deviceId = deviceIdSelector(item); - if (cache.BalancedMapping.TryGetValue(deviceId, out int groupId)) - { - groupQueues[groupId].Enqueue(item); - } - }); - - if ((maxThreads.HasValue && maxThreads.Value > cache.CachedGroups.Length) || maxThreads.HasValue == false) - { - maxThreads = cache.CachedGroups.Length; - } - - // 阶段2:并行处理队列 - var options = new ParallelOptions - { - MaxDegreeOfParallelism = maxThreads.Value, - }; - - await Task.Run(() => - { - Parallel.For(0, cache.CachedGroups.Length, options, async groupId => - { - var queue = groupQueues[groupId]; - while (queue.TryDequeue(out T item)) - { - processor(item, groupId); - } - }); - }); - } - - - /// - /// 智能节流处理(CPU友好型) - /// - /// 已经分组的设备信息 - /// 部分或者全部的已经分组的设备集合 - /// 从泛型对象提取deviceId - /// 处理委托(参数:当前对象,分组ID) - /// 可选最佳并发度 - /// - /// - public static async Task ProcessWithThrottleAsync( - List items, - Func deviceIdSelector, - Action processor, - int? maxConcurrency = null) - { - var cache = _currentCache ?? throw new InvalidOperationException("缓存未初始化"); - //var timer = Stopwatch.StartNew(); - - // 自动计算最佳并发度 - int recommendedThreads = CalculateOptimalThreadCount(); - if ((maxConcurrency.HasValue && maxConcurrency.Value > cache.CachedGroups.Length) || maxConcurrency.HasValue == false) - { - maxConcurrency = cache.CachedGroups.Length; - } - - int actualThreads = maxConcurrency ?? recommendedThreads; - - - // 创建节流器 - using var throttler = new SemaphoreSlim(initialCount: actualThreads); - - // 使用LongRunning避免线程池饥饿 - var tasks = items.Select(async item => - { - await throttler.WaitAsync(); - try - { - var deviceId = deviceIdSelector(item); - if (cache.BalancedMapping.TryGetValue(deviceId, out int groupId)) - { - // 分组级处理(保持顺序性) - await ProcessItemAsync(item, processor, groupId); - } - } - finally - { - throttler.Release(); - } - }); - - await Task.WhenAll(tasks); - //timer.Stop(); - //Console.WriteLine($"任务处理完成,耗时:{timer.ElapsedMilliseconds}ms"); - } - - /// - /// 自动计算最优线程数 - /// - public static int CalculateOptimalThreadCount() - { - int coreCount = Environment.ProcessorCount; - return Math.Min( - coreCount * 8, // 超线程优化 - _currentCache?.CachedGroups.Length ?? 60 - ); - } - - /// - /// 分组异步处理(带节流) - /// - private static async Task ProcessItemAsync(T item, Action processor, int groupId) - { - // 使用内存缓存降低CPU负载 - await Task.Yield(); // 立即释放当前线程 - - // 分组处理上下文 - var context = ExecutionContext.Capture(); - ThreadPool.QueueUserWorkItem(_ => - { - ExecutionContext.Run(context!, state => - { - processor(item,groupId); - }, null); - }); - } - - - /// - /// 通过 deviceId 获取所在的分组集合 - /// - public static List GetGroup(string deviceId) - { - var cache = _currentCache; - if (cache == null) - throw new InvalidOperationException("缓存未初始化"); - - return cache.CachedGroups[cache.BalancedMapping[deviceId]]; - } - - /// - /// 通过 deviceId 获取分组Id - /// - public static int GetDeviceGroupId(string deviceId) - { - var cache = _currentCache; - if (cache == null) - throw new InvalidOperationException("缓存未初始化"); - - return cache.BalancedMapping[deviceId]; - } - - - /// - /// 创建均衡映射表 - /// - /// 数据集合 - /// 分组数量 - /// 允许的最大偏差百分比 - /// - public static Dictionary CreateBalancedMapping(List deviceList, int groupCount, int maxDeviation = 5) - { - var mapping = new Dictionary(); - int targetPerGroup = deviceList.Count / groupCount; - int maxAllowed = (int)(targetPerGroup * (1 + maxDeviation / 100.0)); - - // 初始化分组计数器 - int[] groupCounters = new int[groupCount]; - - foreach (var deviceId in deviceList) - { - int preferredGroup = GetGroupId(deviceId, groupCount); - - // 如果首选分组未满,直接分配 - if (groupCounters[preferredGroup] < maxAllowed) - { - mapping[deviceId] = preferredGroup; - groupCounters[preferredGroup]++; - } - else - { - // 寻找当前最空闲的分组 - int fallbackGroup = Array.IndexOf(groupCounters, groupCounters.Min()); - mapping[deviceId] = fallbackGroup; - groupCounters[fallbackGroup]++; - } - } - - return mapping; - } - - /// - /// 分析分组分布 - /// - /// - /// - /// - public static Dictionary AnalyzeDistribution(List deviceList, int groupCount) - { - Dictionary distribution = new Dictionary(); - foreach (var deviceId in deviceList) - { - int groupId = GetGroupId(deviceId, groupCount); - distribution[groupId] = distribution.TryGetValue(groupId, out var count) ? count + 1 : 1; - } - return distribution; - } - - /// - /// 获取设备ID对应的分组ID - /// - /// - /// - /// - public static int GetGroupId(string deviceId, int groupCount) - { - int hash = Fnv1aHash(deviceId); - // 双重取模确保分布均匀 - return (hash % groupCount + groupCount) % groupCount; - } - - /// - /// FNV-1a哈希算法 - /// - /// - /// - public static int Fnv1aHash(string input) - { - const uint fnvPrime = 16777619; - const uint fnvOffsetBasis = 2166136261; - - uint hash = fnvOffsetBasis; - foreach (char c in input) - { - hash ^= (byte)c; - hash *= fnvPrime; - } - return (int)hash; - } - - /// - /// CRC16算法实现 - /// - /// - /// - public static ushort CRC16Hash(byte[] bytes) - { - ushort crc = 0xFFFF; - for (int i = 0; i < bytes.Length; i++) - { - crc ^= bytes[i]; - for (int j = 0; j < 8; j++) - { - if ((crc & 0x0001) == 1) - { - crc = (ushort)((crc >> 1) ^ 0xA001); - } - else - { - crc >>= 1; - } - } - } - return crc; - } - - /// - /// 打印分组统计数据 - /// - public static void PrintDistributionStats() - { - var cache = _currentCache; - if (cache == null) - { - Console.WriteLine("缓存未初始化"); - return; - } - - var stats = cache.CachedGroups - .Select((group, idx) => new { GroupId = idx, Count = group.Count }) - .OrderBy(x => x.GroupId); - - Console.WriteLine("分组数据量统计:"); - foreach (var stat in stats) - { - Console.WriteLine($"Group {stat.GroupId}: {stat.Count} 条数据"); - } - - Console.WriteLine($"总共: {stats.Sum(d=>d.Count)} 条数据"); - } - - } -} diff --git a/shared/JiShe.CollectBus.Common/Encrypt/EncryptUtil.cs b/shared/JiShe.CollectBus.Common/Encrypt/EncryptUtil.cs deleted file mode 100644 index 72cdf41..0000000 --- a/shared/JiShe.CollectBus.Common/Encrypt/EncryptUtil.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Encrypt -{ - /// - /// 各种加密辅助类 - /// - public static class EncryptUtil - { - #region MD5加密 - - /// - /// MD5加密 - /// - public static string Md5Fun(this string value) - { - if (value == null) - { - throw new ArgumentNullException("未将对象引用设置到对象的实例。"); - } - - var encoding = Encoding.UTF8; - MD5 md5 = MD5.Create(); - return HashAlgorithmBase(md5, value, encoding); - } - - /// - /// 加权MD5加密 - /// - public static string Md5Fun(this string value, string salt) - { - return salt == null ? value.Md5Fun() : (value + "『" + salt + "』").Md5Fun(); - } - - #endregion - - /// - /// HashAlgorithm 加密统一方法 - /// - private static string HashAlgorithmBase(HashAlgorithm hashAlgorithmObj, string source, Encoding encoding) - { - byte[] btStr = encoding.GetBytes(source); - byte[] hashStr = hashAlgorithmObj.ComputeHash(btStr); - return hashStr.Bytes2Str(); - } - - /// - /// 转换成字符串 - /// - private static string Bytes2Str(this IEnumerable source, string formatStr = "{0:X2}") - { - StringBuilder pwd = new StringBuilder(); - foreach (byte btStr in source) - { - pwd.AppendFormat(formatStr, btStr); - } - return pwd.ToString(); - } - } -} diff --git a/shared/JiShe.CollectBus.Common/Enums/MeterTypeEnum.cs b/shared/JiShe.CollectBus.Common/Enums/MeterTypeEnum.cs deleted file mode 100644 index dc78e45..0000000 --- a/shared/JiShe.CollectBus.Common/Enums/MeterTypeEnum.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Enums -{ - /// - /// 表计类型 - /// 电表= 1,水表= 2,燃气表= 3,热能表= 4,水表流量计=5,燃气表流量计=6 - /// - public enum MeterTypeEnum - { - /// - /// 电表 - /// - Ammeter = 1, - /// - /// 水表 - /// - WaterMeter = 2, - /// - /// 燃气表 - /// - Gasmeter = 3, - /// - /// 热能表 - /// - HeatMeter = 4, - /// - /// 水表流量计 - /// - WaterMeterFlowmeter = 5, - /// - /// 燃气表流量计 - /// - GasmeterFlowmeter = 6, - /// - /// 特殊电表 - /// - SpecialAmmeter = 7, - /// - /// 传感器 - /// - Sensor = 8, - - /// - /// 采集器 - /// - Collector = 9, - - /// - /// 集中器 - /// - Focus = 10, - } -} diff --git a/shared/JiShe.CollectBus.Common/Extensions/DateTimeOffsetExtensions.cs b/shared/JiShe.CollectBus.Common/Extensions/DateTimeOffsetExtensions.cs deleted file mode 100644 index 0f07e9c..0000000 --- a/shared/JiShe.CollectBus.Common/Extensions/DateTimeOffsetExtensions.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; - -namespace JiShe.CollectBus.Common.Extensions -{ - public static class DateTimeOffsetExtensions - { - - /// - /// 获取当前时间毫秒级时间戳 - /// - /// - public static long GetCurrentTimeMillis() - { - return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - } - - /// - /// 将Unix时间戳转换为日期时间 - /// - /// - /// - public static DateTime FromUnixMillis(long millis) - { - return DateTimeOffset.FromUnixTimeMilliseconds(millis).DateTime; - } - - /// - /// 将 DateTime 时间转换为 DateTimeOffset 时间 - /// - /// - /// - public static DateTimeOffset GetDateTimeOffset(this DateTime rawDateTime) - { - //确保 Kind 为 Local(如果是 Unspecified) - DateTime localDateTime = rawDateTime.Kind == DateTimeKind.Unspecified - ? DateTime.SpecifyKind(rawDateTime, DateTimeKind.Local) - : rawDateTime; - - // 转换为 DateTimeOffset(自动应用本地时区偏移) - return new DateTimeOffset(localDateTime); - } - } -} diff --git a/shared/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs b/shared/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs deleted file mode 100644 index f54a3a4..0000000 --- a/shared/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; - -namespace JiShe.CollectBus.Common.Extensions -{ - public static class EnumExtensions - { - /// - /// 将枚举转换为字典 - /// - /// - /// - public static Dictionary ToDictionary() where TEnum : Enum - { - return Enum.GetValues(typeof(TEnum)) - .Cast() - .ToDictionary( - e => e.ToString(), - e => Convert.ToInt32(e) - ); - } - - /// - /// 将枚举转换为字典 - /// - /// - /// - public static Dictionary ToEnumDictionary() where TEnum : Enum - { - return Enum.GetValues(typeof(TEnum)) - .Cast() - .ToDictionary( - e => e.ToString(), - e => e - ); - } - - /// - /// 将枚举转换为字典 - /// - /// - /// - public static Dictionary ToValueNameDictionary() where TEnum : Enum - { - return Enum.GetValues(typeof(TEnum)) - .Cast() - .ToDictionary( - e => Convert.ToInt32(e), - e => e.ToString() - ); - } - - /// - /// 将枚举转换为字典 - /// - /// - /// - public static Dictionary ToNameValueDictionary() where TEnum : Enum - { - return Enum.GetValues(typeof(TEnum)) - .Cast() - .ToDictionary( - e => e.ToString(), - e => Convert.ToInt32(e) - ); - } - - /// - /// 将枚举转换为字典 - /// - /// - /// - public static Dictionary ToEnumNameDictionary() where TEnum : Enum - { - return Enum.GetValues(typeof(TEnum)) - .Cast() - .ToDictionary( - e => e, - e => e.ToString() - ); - } - - } -} diff --git a/shared/JiShe.CollectBus.Common/Extensions/EnumerableExtensions.cs b/shared/JiShe.CollectBus.Common/Extensions/EnumerableExtensions.cs deleted file mode 100644 index b17e9c2..0000000 --- a/shared/JiShe.CollectBus.Common/Extensions/EnumerableExtensions.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; - -namespace JiShe.CollectBus.Common.Extensions -{ - /// - /// Extension methods for . - /// - public static class EnumerableExtensions - { - /// - /// Concatenates the members of a constructed collection of type System.String, using the specified separator between each member. - /// This is a shortcut for string.Join(...) - /// - /// A collection that contains the strings to concatenate. - /// The string to use as a separator. separator is included in the returned string only if values has more than one element. - /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. - [Description("Enumerable转换String")] - public static string JoinAsString(this IEnumerable source, string separator) - { - return string.Join(separator, source); - } - - /// - /// Concatenates the members of a collection, using the specified separator between each member. - /// This is a shortcut for string.Join(...) - /// - /// A collection that contains the objects to concatenate. - /// The string to use as a separator. separator is included in the returned string only if values has more than one element. - /// The type of the members of values. - /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. - public static string JoinAsString(this IEnumerable source, string separator) - { - return string.Join(separator, source); - } - - /// - /// Filters a by given predicate if given condition is true. - /// - /// Enumerable to apply filtering - /// A boolean value - /// Predicate to filter the enumerable - /// Filtered or not filtered enumerable based on - [Description("Enumerable筛选")] - public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) - { - return condition - ? source.Where(predicate) - : source; - } - - /// - /// Filters a by given predicate if given condition is true. - /// - /// Enumerable to apply filtering - /// A boolean value - /// Predicate to filter the enumerable - /// Filtered or not filtered enumerable based on - public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) - { - return condition - ? source.Where(predicate) - : source; - } - - /// - /// 分批 - /// - /// - /// - /// - /// - public static IEnumerable> Batch( - this IEnumerable source, - int batchSize) - { - var buffer = new List(batchSize); - foreach (var item in source) - { - buffer.Add(item); - if (buffer.Count == batchSize) - { - yield return buffer; - buffer = new List(batchSize); - } - } - if (buffer.Count > 0) - yield return buffer; - } - - //public static IEnumerable> Batch(this IEnumerable source, int batchSize) - //{ - // if (batchSize <= 0) - // throw new ArgumentOutOfRangeException(nameof(batchSize)); - - // using var enumerator = source.GetEnumerator(); - // while (enumerator.MoveNext()) - // { - // yield return GetBatch(enumerator, batchSize); - // } - //} - - //private static IEnumerable GetBatch(IEnumerator enumerator, int batchSize) - //{ - // do - // { - // yield return enumerator.Current; - // batchSize--; - // } while (batchSize > 0 && enumerator.MoveNext()); - //} - } -} diff --git a/shared/JiShe.CollectBus.Common/Extensions/LockExtensions.cs b/shared/JiShe.CollectBus.Common/Extensions/LockExtensions.cs deleted file mode 100644 index cff7df7..0000000 --- a/shared/JiShe.CollectBus.Common/Extensions/LockExtensions.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; - -namespace JiShe.CollectBus.Common.Extensions -{ - /// - /// Extension methods to make locking easier. - /// - public static class LockExtensions - { - /// - /// Executes given by locking given object. - /// - /// Source object (to be locked) - /// Action (to be executed) - public static void Locking(this object source, Action action) - { - lock (source) - { - action(); - } - } - - /// - /// Executes given by locking given object. - /// - /// Type of the object (to be locked) - /// Source object (to be locked) - /// Action (to be executed) - public static void Locking(this T source, Action action) where T : class - { - lock (source) - { - action(source); - } - } - - /// - /// Executes given and returns it's value by locking given object. - /// - /// Return type - /// Source object (to be locked) - /// Function (to be executed) - /// Return value of the - public static TResult Locking(this object source, Func func) - { - lock (source) - { - return func(); - } - } - - /// - /// Executes given and returns it's value by locking given object. - /// - /// Type of the object (to be locked) - /// Return type - /// Source object (to be locked) - /// Function (to be executed) - /// Return value of the - public static TResult Locking(this T source, Func func) where T : class - { - lock (source) - { - return func(source); - } - } - } -} diff --git a/shared/JiShe.CollectBus.Common/Extensions/ProtocolConstExtensions.cs b/shared/JiShe.CollectBus.Common/Extensions/ProtocolConstExtensions.cs deleted file mode 100644 index 1dbb301..0000000 --- a/shared/JiShe.CollectBus.Common/Extensions/ProtocolConstExtensions.cs +++ /dev/null @@ -1,69 +0,0 @@ -using JiShe.CollectBus.Common.Consts; -using JiShe.CollectBus.Common.Enums; -using JiShe.CollectBus.Common.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Extensions -{ - public class ProtocolConstExtensions - { - /// - /// 自动获取 ProtocolConst 类中所有下行 Kafka 主题名称 - /// (通过反射筛选 public const string 且字段名以 "EventName" 结尾的常量) - /// - public static List GetAllTopicNamesByIssued() - { - List topics = typeof(ProtocolConst) - .GetFields(BindingFlags.Public | BindingFlags.Static) - .Where(f => - f.IsLiteral && - !f.IsInitOnly && - f.FieldType == typeof(string) && - f.Name.EndsWith("IssuedEventName")) // 通过命名规则过滤主题字段 - .Select(f => (string)f.GetRawConstantValue()!) - .ToList(); - - return topics; - } - - /// - /// 自动获取 ProtocolConst 类中所有下行 Kafka 主题名称 - /// (通过反射筛选 public const string 且字段名以 "EventName" 结尾的常量) - /// - public static List GetAllTopicNamesByReceived() - { - //固定的上报主题 - var topicList = typeof(ProtocolConst) - .GetFields(BindingFlags.Public | BindingFlags.Static) - .Where(f => - f.IsLiteral && - !f.IsInitOnly && - f.FieldType == typeof(string) && - f.Name.EndsWith("ReceivedEventName")) // 通过命名规则过滤主题字段 - .Select(f => (string)f.GetRawConstantValue()!) - .ToList(); - - //动态上报主题,需根据协议的AFN功能码动态获取 - var afnList = EnumExtensions.ToNameValueDictionary(); - //需要排除的AFN功能码 - var excludeItems = new List() { 6, 7, 8,15 }; - - foreach (var item in afnList) - { - if (excludeItems.Contains(item.Value)) - { - continue; - } - - topicList.Add(string.Format(ProtocolConst.AFNTopicNameFormat, item.Value.ToString().PadLeft(2, '0'))); - } - - return topicList; - } - } -} diff --git a/shared/JiShe.CollectBus.Common/Helpers/BusJsonSerializer.cs b/shared/JiShe.CollectBus.Common/Helpers/BusJsonSerializer.cs deleted file mode 100644 index 713501e..0000000 --- a/shared/JiShe.CollectBus.Common/Helpers/BusJsonSerializer.cs +++ /dev/null @@ -1,204 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Encodings.Web; -using System.Text.Json.Serialization; -using System.Text.Json; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Helpers -{ - /// - /// json帮助类 - /// - public static class BusJsonSerializer - { - /// - /// json对象转换成字符串 - /// - /// 需要序列化的对象 - /// 是否忽略实体中实体,不再序列化里面包含的实体 - /// 配置 - /// - public static string Serialize(this object obj, bool IsIgnore = false, JsonSerializerOptions jsonSerializerOptions = null) - { - if (jsonSerializerOptions == null) - { - jsonSerializerOptions = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.Never, - WriteIndented = false,// 设置格式化输出 - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,// 允许特殊字符 - IgnoreReadOnlyFields = true, - IgnoreReadOnlyProperties = true, - NumberHandling = JsonNumberHandling.AllowReadingFromString, // 允许数字字符串 - AllowTrailingCommas = true, // 忽略尾随逗号 - ReadCommentHandling = JsonCommentHandling.Skip, // 忽略注释 - PropertyNameCaseInsensitive = true, // 属性名称大小写不敏感 - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 属性名称使用驼峰命名规则 - Converters = { new DateTimeJsonConverter() }, // 注册你的自定义转换器, - DefaultBufferSize = 4096, - }; - } - - if (IsIgnore == true) - { - jsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; // 忽略循环引用 - - return JsonSerializer.Serialize(obj, jsonSerializerOptions); - } - else - { - return JsonSerializer.Serialize(obj, jsonSerializerOptions); - } - } - - /// - /// json字符串转换成json对象 - /// - /// - /// - /// 是否忽略实体中实体,不再序列化里面包含的实体 - /// 配置 - /// - public static T? Deserialize(this string json, bool IsIgnore = false, JsonSerializerOptions jsonSerializerOptions = null) - { - if (jsonSerializerOptions == null) - { - jsonSerializerOptions = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.Never, - WriteIndented = false,// 设置格式化输出 - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,// 允许特殊字符 - IgnoreReadOnlyFields = true, - IgnoreReadOnlyProperties = true, - NumberHandling = JsonNumberHandling.AllowReadingFromString, // 允许数字字符串 - AllowTrailingCommas = true, // 忽略尾随逗号 - ReadCommentHandling = JsonCommentHandling.Skip, // 忽略注释 - PropertyNameCaseInsensitive = true, // 属性名称大小写不敏感 - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 属性名称使用驼峰命名规则 - Converters = { new DateTimeJsonConverter() }, // 注册你的自定义转换器, - DefaultBufferSize = 4096, - }; - } - - if (IsIgnore == true) - { - jsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; // 忽略循环引用 - - return json == null ? default(T) : JsonSerializer.Deserialize(json, jsonSerializerOptions); - } - else - { - return json == null ? default(T) : JsonSerializer.Deserialize(json, jsonSerializerOptions); - } - } - - /// - /// json字符串转换成json对象 - /// - /// - /// - /// 是否忽略实体中实体,不再序列化里面包含的实体 - /// 配置 - /// - public static object? Deserialize(this string json, Type type, bool IsIgnore = false, JsonSerializerOptions jsonSerializerOptions = null) - { - if (jsonSerializerOptions == null) - { - jsonSerializerOptions = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.Never, - WriteIndented = false,// 设置格式化输出 - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,// 允许特殊字符 - IgnoreReadOnlyFields = true, - IgnoreReadOnlyProperties = true, - NumberHandling = JsonNumberHandling.AllowReadingFromString, // 允许数字字符串 - AllowTrailingCommas = true, // 忽略尾随逗号 - ReadCommentHandling = JsonCommentHandling.Skip, // 忽略注释 - PropertyNameCaseInsensitive = true, // 属性名称大小写不敏感 - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 属性名称使用驼峰命名规则 - Converters = { new DateTimeJsonConverter() } // 注册你的自定义转换器, - }; - } - - - - if (IsIgnore == true) - { - jsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; // 忽略循环引用 - - return json == null ? null : JsonSerializer.Deserialize(json, type, jsonSerializerOptions); - } - else - { - return json == null ? null : JsonSerializer.Deserialize(json, type, jsonSerializerOptions); - } - } - - /// - /// list json字符串转换成list - /// - /// - /// - /// - public static List? DeserializeToList(this string json) - { - return json == null ? default(List) : Deserialize>(json); - } - } - - - public class DateTimeJsonConverter : JsonConverter - { - private readonly string _dateFormatString; - public DateTimeJsonConverter() - { - _dateFormatString = "yyyy-MM-dd HH:mm:ss"; - } - - public DateTimeJsonConverter(string dateFormatString) - { - _dateFormatString = dateFormatString; - } - - public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return DateTime.Parse(reader.GetString()); - } - - public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString(_dateFormatString)); - } - } - - /// - /// Unix格式时间格式化 - /// - public class UnixTimeConverter : JsonConverter - { - public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.String) - { - if (long.TryParse(reader.GetString(), out long timestamp)) - return DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime; - } - - if (reader.TokenType == JsonTokenType.Number) - { - long timestamp = reader.GetInt64(); - return DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime; - } - return reader.GetDateTime(); - } - - public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) - { - long timestamp = new DateTimeOffset(value).ToUnixTimeSeconds(); - writer.WriteStringValue(timestamp.ToString()); - } - } -} diff --git a/shared/JiShe.CollectBus.Common/Helpers/CommonHelper.cs b/shared/JiShe.CollectBus.Common/Helpers/CommonHelper.cs deleted file mode 100644 index 7c7ef1f..0000000 --- a/shared/JiShe.CollectBus.Common/Helpers/CommonHelper.cs +++ /dev/null @@ -1,936 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; -using JiShe.CollectBus.Common.Attributes; -using System.Collections.Specialized; -using JiShe.CollectBus.Common.Enums; -using Newtonsoft.Json.Linq; - -namespace JiShe.CollectBus.Common.Helpers -{ - public static class CommonHelper - { - /// - /// 获得无符号GUID - /// - /// - public static string GetGUID() - { - return Guid.NewGuid().ToString("N"); - } - - /// - /// 获取时间戳 - /// - /// 是否返回秒,false返回毫秒 - /// - public static long GetTimeStampTen(bool isSeconds) - { - if (isSeconds) - { - return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - } - else - { - return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - } - } - - /// - /// 获取指定长度的随机数 - /// - /// - /// - public static string GetRandomNumber(int length = 8) - { - if (length <= 8) - { - length = 8; - } - - if (length > 31) - { - length = 32; - } - - var randomArray = RandomNumberGenerator.GetBytes(length); - StringBuilder stringBuilder = new StringBuilder(); - foreach (var item in randomArray) - { - stringBuilder.Append(item); - } - - return stringBuilder.ToString().Substring(0, length); - } - - /// - /// C#反射遍历对象属性获取键值对 - /// - /// 对象类型 - /// 对象 - public static Dictionary GetClassProperties(T model) - { - Type t = model.GetType(); - List propertyList = new List(); - - PropertyInfo[] tempPropertyList = t.GetProperties(); - if (tempPropertyList != null && tempPropertyList.Length > 0) - { - propertyList.AddRange(tempPropertyList); - } - - var parentPropertyInfo = t.BaseType?.GetProperties(); - if (parentPropertyInfo != null && parentPropertyInfo.Length > 0) - { - foreach (var item in parentPropertyInfo) - { - if (!propertyList.Any(d => d.Name == item.Name)) //如果子类已经包含父类的属性或者字段,跳过不处理 - { - propertyList.Add(item); - } - } - } - - Dictionary resultData = new Dictionary(); - - foreach (PropertyInfo item in propertyList) - { - resultData.Add(item.Name, item.GetValue(model, null)); - } - - return resultData; - } - - /// - /// C#反射遍历对象属性,将键值对赋值给属性 - /// - public static object SetClassProperties(string typeModel, Dictionary keyValues) - { - if (keyValues.Count <= 0) - { - return null; - } - - Type tType = Type.GetType(typeModel); - - - PropertyInfo[] PropertyList = tType.GetProperties(); - - object objModel = tType.Assembly.CreateInstance(tType.FullName); - foreach (PropertyInfo item in PropertyList) - { - if (keyValues.ContainsKey(item.Name)) - { - object objectValue = keyValues[item.Name]; - item.SetValue(objModel, objectValue); - } - } - - return objModel; - } - - - - /// - /// 获取指定枚举的所有 Attribute 说明以及value组成的键值对 - /// - /// 对象类 - /// false获取字段、true获取属性 - /// - public static List GetEnumAttributeList(Type type, bool getPropertie = false) - { - if (type == null) - { - return null; - } - - List selectResults = new List(); - List memberInfos = new List(); - - if (getPropertie == false) - { - FieldInfo[] fieldArray = type.GetFields(); - if (null == fieldArray || fieldArray.Length <= 0) - { - return null; - } - - memberInfos.AddRange(fieldArray); - //获取父类的字段 - var parentFieldInfo = type.BaseType?.GetFields(); - if (parentFieldInfo != null && parentFieldInfo.Length > 0) - { - foreach (var item in parentFieldInfo) - { - if (!memberInfos.Any(d => d.Name == item.Name)) //如果子类已经包含父类的属性或者字段,跳过不处理 - { - memberInfos.Add(item); - } - } - } - } - else - { - PropertyInfo[] properties = type.GetProperties(); - if (null == properties || properties.Length <= 0) - { - return null; - } - - memberInfos.AddRange(properties); - //获取父类的属性 - var parentPropertyInfo = type.BaseType?.GetProperties(); - if (parentPropertyInfo != null && parentPropertyInfo.Length > 0) - { - foreach (var item in parentPropertyInfo) - { - if (!memberInfos.Any(d => d.Name == item.Name)) //如果子类已经包含父类的属性或者字段,跳过不处理 - { - memberInfos.Add(item); - } - } - } - } - - foreach (var item in memberInfos) - { - DescriptionAttribute[] EnumAttributes = - (DescriptionAttribute[])item.GetCustomAttributes(typeof(DescriptionAttribute), false); - - dynamic infoObject = null; - if (getPropertie == false) - { - infoObject = (FieldInfo)item; - } - else - { - infoObject = (PropertyInfo)item; - } - - if (EnumAttributes.Length > 0) - { - SelectResult selectResult = new SelectResult() - { - Key = Convert.ToInt32(infoObject.GetValue(null)).ToString(), - Value = EnumAttributes[0].Description, - }; - - selectResults.Add(selectResult); - } - - DisplayAttribute[] DisplayAttributes = - (DisplayAttribute[])item.GetCustomAttributes(typeof(DisplayAttribute), false); - if (DisplayAttributes.Length > 0) - { - SelectResult selectResult = - selectResults.FirstOrDefault(e => e.Key == Convert.ToInt32(infoObject.GetValue(null)).ToString()); - if (selectResult != null) - { - selectResult.SecondValue = DisplayAttributes[0].Name; - } - } - } - - return selectResults; - } - - /// - /// 获取指定枚举的所有 Attribute 说明以及value组成的键值对 - /// - /// 对象类 - /// 第三个标签类型 - /// 第三个标签类型取值名称 - /// false获取字段、true获取属性 - /// - public static List GetEnumAttributeListWithThirdValue(Type type, Type thirdAttributeType, string thirdAttributePropertieName, bool getPropertie = false) - { - if (type == null) - { - return null; - } - - List selectResults = new List(); - List memberInfos = new List(); - - if (getPropertie == false) - { - FieldInfo[] EnumInfo = type.GetFields(); - if (null == EnumInfo || EnumInfo.Length <= 0) - { - return null; - } - memberInfos.AddRange(EnumInfo); - var parentFieldInfo = type.BaseType?.GetFields(); - if (parentFieldInfo != null && parentFieldInfo.Length > 0) - { - memberInfos.AddRange(parentFieldInfo); - } - } - else - { - PropertyInfo[] EnumInfo = type.GetProperties(); - if (null == EnumInfo || EnumInfo.Length <= 0) - { - return null; - } - memberInfos.AddRange(EnumInfo); - var parentPropertyInfo = type.BaseType?.GetProperties(); - if (parentPropertyInfo != null && parentPropertyInfo.Length > 0) - { - memberInfos.AddRange(parentPropertyInfo); - } - } - - foreach (var item in memberInfos) - { - var thirdAttributes = item. - GetCustomAttributes(thirdAttributeType, false); - if (thirdAttributes == null || thirdAttributes.Length <= 0) - { - continue; - } - - DescriptionAttribute[] descriptionAttributes = (DescriptionAttribute[])item. - GetCustomAttributes(typeof(DescriptionAttribute), false); - - dynamic infoObject = null; - if (getPropertie == false) - { - infoObject = (FieldInfo)item; - } - else - { - infoObject = (PropertyInfo)item; - } - - if (descriptionAttributes.Length > 0) - { - SelectResult selectResult = new SelectResult() - { - Key = infoObject.Name, - Value = descriptionAttributes[0].Description, - }; - - selectResults.Add(selectResult); - } - - DisplayAttribute[] displayAttributes = (DisplayAttribute[])item. - GetCustomAttributes(typeof(DisplayAttribute), false); - if (displayAttributes.Length > 0) - { - SelectResult selectResult = selectResults.FirstOrDefault(e => e.Key == infoObject.Name); - if (selectResult != null) - { - selectResult.SecondValue = displayAttributes[0].Name; - } - } - - if (thirdAttributes.Length > 0 && !string.IsNullOrWhiteSpace(thirdAttributePropertieName)) - { - foreach (var attr in thirdAttributes) - { - // 使用反射获取特性的属性值 - var properties = thirdAttributeType.GetProperties(); - foreach (var prop in properties) - { - // 假设你要获取特性的某个属性值,例如 TypeName - if (prop.Name == thirdAttributePropertieName) - { - object value = prop.GetValue(attr); - SelectResult selectResult = selectResults.FirstOrDefault(e => e.Key == infoObject.Name); - if (selectResult != null) - { - selectResult.ThirdValue = value?.ToString(); // 将属性值赋给 ThirdValue - } - break; // 如果找到了需要的属性,可以跳出循环 - } - } - } - } - } - - return selectResults; - } - - /// - /// 获取指定类、指定常量值的Description说明 - /// 包含直接继承的父级字段 - /// - /// 对象类 - /// - /// - public static List> GetTypeDescriptionListToTuple(Type t, bool getPropertie = false) - { - if (t == null) - { - return null; - } - - List memberInfos = new List(); - - if (getPropertie == false) - { - FieldInfo[] fieldInfo = t.GetFields(); - if (null == fieldInfo || fieldInfo.Length <= 0) - { - return null; - } - - memberInfos.AddRange(fieldInfo); - var parentFieldInfo = t.BaseType?.GetFields(); - if (parentFieldInfo != null && parentFieldInfo.Length > 0) - { - foreach (var item in parentFieldInfo) - { - if (!memberInfos.Any(d => d.Name == item.Name)) //如果子类已经包含父类的属性或者字段,跳过不处理 - { - memberInfos.Add(item); - } - } - } - } - else - { - PropertyInfo[] fieldInfo = t.GetProperties(); - if (null == fieldInfo || fieldInfo.Length <= 0) - { - return null; - } - - memberInfos.AddRange(fieldInfo); - var parentPropertyInfo = t.BaseType?.GetProperties(); - if (parentPropertyInfo != null && parentPropertyInfo.Length > 0) - { - foreach (var item in parentPropertyInfo) - { - if (!memberInfos.Any(d => d.Name == item.Name)) //如果子类已经包含父类的属性或者字段,跳过不处理 - { - memberInfos.Add(item); - } - } - } - } - - List> tuples = new List>(); - - foreach (var item in memberInfos) - { - DescriptionAttribute[] descriptionAttribute = - (DescriptionAttribute[])item.GetCustomAttributes(typeof(DescriptionAttribute), false); - - NumericalOrderAttribute[] indexAttributes = - (NumericalOrderAttribute[])item.GetCustomAttributes(typeof(NumericalOrderAttribute), false); - - - if (descriptionAttribute.Length > 0 && indexAttributes.Length > 0) - { - Tuple tuple = new Tuple(item.Name, - descriptionAttribute[0].Description, indexAttributes[0].Index); - tuples.Add(tuple); - } - } - - return tuples; - } - - /// - /// 获取指定类、指定常量值的常量说明 - /// - /// 常量字段名称 - ///属性还是字段 - /// - public static string GetTypeDescriptionName(string fieldName, bool getPropertie = false) - { - if (string.IsNullOrEmpty(fieldName)) - { - return ""; - } - - MemberInfo memberInfo = null; - if (getPropertie == false) - { - memberInfo = typeof(T).GetField(fieldName); - } - else - { - memberInfo = typeof(T).GetProperty(fieldName); - } - - if (null == memberInfo) - { - return ""; - } - - DescriptionAttribute[] EnumAttributes = - (DescriptionAttribute[])memberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); - if (EnumAttributes.Length <= 0) - { - return ""; - } - - return EnumAttributes[0].Description; - } - - /// - /// 获取指定命名空间下指定常量值的常量说明 - /// - /// 常量字段名称 - /// 命名空间,主要用来找到对应程序集 - ///属性还是字段 - /// - public static string GetTypeDescriptionName(string fieldName, string assemblyName, bool getPropertie = false) - { - if (string.IsNullOrWhiteSpace(fieldName) || string.IsNullOrWhiteSpace(assemblyName)) - { - return null; - } - - string desc = ""; - foreach (var item in GetEnumList(assemblyName)) - { - desc = GetTypeDescriptionName(item, fieldName, getPropertie); - if (!string.IsNullOrEmpty(desc)) - { - break; - } - } - - return desc; - } - - /// - /// 获取指定类、指定常量值的常量说明 - /// - /// 对象类 - /// 常量字段名称 - ///属性还是字段 - /// - public static string GetTypeDescriptionName(this Type t, string fieldName, bool getPropertie = false) - { - if (string.IsNullOrWhiteSpace(fieldName)) - { - return ""; - } - - MemberInfo memberInfo = null; - if (getPropertie == false) - { - memberInfo = t.GetField(fieldName); - } - else - { - memberInfo = t.GetProperty(fieldName); - } - - if (null != memberInfo) - { - DescriptionAttribute[] EnumAttributes = - (DescriptionAttribute[])memberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); - if (EnumAttributes.Length > 0) - { - return EnumAttributes[0].Description; - } - } - - return ""; - } - - /// - /// 扩展方法,获得枚举值集合 - /// - ///枚举的DisplayName - public static List GetEnumList() where T : Enum - { - List enumList = new List(); - foreach (T value in Enum.GetValues(typeof(T))) - { - enumList.Add(value); - } - - return enumList; - } - - private static List GetEnumList(string assemblyName) - { - if (!String.IsNullOrEmpty(assemblyName)) - { - Assembly assembly = Assembly.Load(assemblyName); - List ts = assembly.GetTypes().Where(x => x.GetTypeInfo().IsClass).ToList(); - return ts; - } - - return new List(); - } - - /// - /// 扩展方法,获得枚举的Display值 - /// - ///枚举值 - ///当枚举值没有定义DisplayNameAttribute,是否使用枚举名代替,默认是使用 - ///属性还是字段 - ///枚举的DisplayName - public static string GetDisplayName(this Enum value, Boolean nameInstead = true, bool getPropertie = false) - { - Type type = value.GetType(); - string name = Enum.GetName(type, value); - if (name == null) - { - return null; - } - - DisplayAttribute attribute = null; - - if (getPropertie == false) - { - attribute = Attribute.GetCustomAttribute(type.GetField(name), typeof(DisplayAttribute)) as DisplayAttribute; - } - else - { - attribute = - Attribute.GetCustomAttribute(type.GetProperty(name), typeof(DisplayAttribute)) as DisplayAttribute; - } - - - if (attribute == null && nameInstead == true) - { - return name; - } - - return attribute == null ? null : attribute.Name; - } - - /// - /// 获取枚举的描述信息 - /// - /// - /// - public static string GetEnumDescription(this Enum value) - { - var name = value.ToString(); - var field = value.GetType().GetField(name); - if (field == null) return name; - - var att = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute), false); - - return att == null ? field.Name : ((DescriptionAttribute)att).Description; - } - - - /// - /// 根据枚举类型值返回枚举定义Description属性 - /// - /// - /// - /// - public static string GetEnumDescription(this int value, Type enumType) - { - NameValueCollection nameValueCollection = new NameValueCollection(); - Type typeFromHandle = typeof(DescriptionAttribute); - FieldInfo[] fields = enumType.GetFields(); - foreach (FieldInfo fieldInfo in fields) - { - if (fieldInfo.FieldType.IsEnum) - { - string name = ((int)enumType.InvokeMember(fieldInfo.Name, BindingFlags.GetField, null, null, null)).ToString(); - object[] customAttributes = fieldInfo.GetCustomAttributes(typeFromHandle, inherit: true); - string value2 = ((customAttributes.Length == 0) ? "" : ((DescriptionAttribute)customAttributes[0]).Description); - nameValueCollection.Add(name, value2); - } - } - - return nameValueCollection[value.ToString()]; - } - - - /// - /// 将传入的字符串中间部分字符替换成特殊字符 - /// - /// 需要替换的字符串 - /// 前保留长度 - /// 尾保留长度 - /// 特殊字符 - /// 被特殊字符替换的字符串 - public static string ReplaceWithSpecialChar(this string value, int startLen = 1, int endLen = 1, - char specialChar = '*') - { - if (string.IsNullOrEmpty(value)) - { - return value; - } - - try - { - - if (value.Length <= startLen + endLen) - { - var temStartVal = value.Substring(0, startLen); - return $"{temStartVal}{"".PadLeft(endLen, specialChar)}"; - } - - if (value.Length == 10 && endLen == 1) - { - endLen = 3; - } - - var startVal = value.Substring(0, startLen); - var endVal = value.Substring(value.Length - endLen); - if (value.Length == 2) - { - endVal = ""; - } - - value = $"{startVal}{endVal.PadLeft(value.Length - startLen, specialChar)}"; - } - catch (Exception) - { - throw; - } - - return value; - } - - /// - /// Linux下字体名称转换 - /// - /// - /// - public static string GetLinuxFontFamily(this string fontValue) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - if (fontValue == "楷体") - { - fontValue = "KaiTi"; - } - else if (fontValue == "隶书") - { - fontValue = "LiSu"; - } - else if (fontValue == "宋体") - { - fontValue = "SimSun"; - } - else if (fontValue == "微软雅黑") - { - fontValue = "Microsoft YaHei"; - } - else if (fontValue == "新宋体") - { - fontValue = "NSimSun"; - } - else if (fontValue == "仿宋") - { - fontValue = "FangSong"; - } - else if (fontValue == "黑体") - { - fontValue = "SimHei"; - } - - } - - return fontValue; - } - - /// - /// 获取任务标识 - /// - /// - /// - /// - /// - /// - /// - public static string GetTaskMark(int afn, int fn, int pn, int msa, int seq) - { - var makstr = $"{afn.ToString().PadLeft(2, '0')}{fn.ToString().PadLeft(2, '0')}{pn.ToString().PadLeft(2, '0')}{msa.ToString().PadLeft(2, '0')}{seq.ToString().PadLeft(2, '0')}"; - - return makstr;// Convert.ToInt32(makstr) << 32 | msa; - } - - /// - /// 判断是生成月 - /// - /// - /// - /// - public static bool JudgeIsGenerate_Month(string eachMonthWith, DateTime curTime) - { - var arr = eachMonthWith.Split(','); - if (arr.Contains(curTime.Day.ToString())) - return true; - return false; - } - - /// - /// 判断是生成一次 - /// - /// - /// - /// - public static bool JudgeIsGenerate_Once(DateTime onceWithDate, DateTime curTime) => curTime.Date.Equals(onceWithDate);//为当天时发送 - - /// - /// 判断是生成日 - /// - /// - /// - /// - public static bool JudgeIsGenerate_Day(string eachDayWithout, DateTime curTime) - { - if (string.IsNullOrWhiteSpace(eachDayWithout)) - { - return false; - } - - var weekName = strWeeks[(int)curTime.DayOfWeek]; - var arr = eachDayWithout.Split(','); - return !arr.Contains(weekName); - } - - public static readonly List strWeeks = new List() { "周日", "周一", "周二", "周三", "周四", "周五", "周六" }; - - /// - /// 判断是生成周 - /// - /// - /// - /// - public static bool JudgeIsGenerate_Week(string eachWeekWith, DateTime curTime) - { - if (string.IsNullOrWhiteSpace(eachWeekWith)) - { - return false; - } - - var weekName = strWeeks[(int)curTime.DayOfWeek]; - var arr = eachWeekWith.Split(','); - return arr.Contains(weekName); - - } - - /// - /// 系统采集频率转换为集中器采集密度 - /// - /// - /// - public static int GetFocusDensity(this int timeDensity) => timeDensity switch - { - 0 => 0,//无 - 1 => 255,//1分钟 - 5 => 245,//5分钟 - 15 => 1,//15分钟 - 30 => 2,//30分钟 - 60 => 3,//60分钟 - _ => -1//采集项本身无密度位 - }; - - /// - /// 集中器采集密度转换为系统采集频率 - /// - /// - /// - public static int GetSystemDensity(this int density) => density switch - { - 0 => 0,//无 - 255 => 1,//1分钟 - 245 => 5,//5分钟 - 1 => 15,//15分钟 - 2 => 30,//30分钟 - 3 => 60,//60分钟 - _ => -1//采集项本身无密度位 - }; - - /// - /// 获取集中器ZSet Scores - /// - /// - /// - /// - public static long GetFocusScores(string focusScores, int point) - { - bool hasInvalidChars = focusScores.Any(c => !(c >= '0' && c <= '9')); - if (hasInvalidChars) - { - throw new Exception($"{nameof(GetFocusScores)} 集中器地址格式错误"); - } - var scoresStr = $"{focusScores}{point.ToString().PadLeft(2, '0')}"; - - return Convert.ToInt64(scoresStr); - } - - /// - /// 加载指定名称的程序集 - /// - /// - /// - public static List LoadAssemblies(string[] assemblyNames) - { - var assemblies = new List(); - - // 获取已加载的程序集 - foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) - { - if (assemblyNames.Contains(asm.GetName().Name)) - assemblies.Add(asm); - } - - // 尝试加载未加载的程序集 - foreach (var name in assemblyNames) - { - if (!assemblies.Any(a => a.GetName().Name == name)) - { - try - { - var assembly = Assembly.Load(name); - assemblies.Add(assembly); - } - catch (FileNotFoundException) - { - // 若Load失败,尝试从基目录加载 - var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{name}.dll"); - if (File.Exists(path)) - { - try - { - assemblies.Add(Assembly.LoadFrom(path)); - } - catch { /* 记录错误 */ } - } - } - } - } - - return assemblies; - } - - /// - /// 创建类型实例 - /// - /// - /// - public static List CreateInstances(List types) - { - var instances = new List(); - foreach (var type in types) - { - try - { - instances.Add(Activator.CreateInstance(type)); - } - catch (Exception) - { - throw; - } - } - return instances; - } - } -} diff --git a/shared/JiShe.CollectBus.Common/Helpers/SelectResult.cs b/shared/JiShe.CollectBus.Common/Helpers/SelectResult.cs deleted file mode 100644 index 4ccaf99..0000000 --- a/shared/JiShe.CollectBus.Common/Helpers/SelectResult.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Helpers -{ - /// - /// 下拉框选项元素 - /// - public class SelectResult - { - /// - /// 下拉框 键 - /// - public string Key { get; set; } - - /// - /// 下拉框 值 - /// - public string Value { get; set; } - - /// - /// 下拉框 值2 - /// - public string SecondValue { get; set; } - - /// - /// 下拉框 值3 - /// - public object ThirdValue { get; set; } - - } -} diff --git a/shared/JiShe.CollectBus.Common/Helpers/TimestampHelper.cs b/shared/JiShe.CollectBus.Common/Helpers/TimestampHelper.cs deleted file mode 100644 index 9edc3cd..0000000 --- a/shared/JiShe.CollectBus.Common/Helpers/TimestampHelper.cs +++ /dev/null @@ -1,68 +0,0 @@ -using JiShe.CollectBus.Common.Enums; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Helpers -{ - /// - /// 时间戳帮助类 - /// - public static class TimestampHelper - { - private static readonly long UnixEpochTicks = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).Ticks; - - /// - /// 获取当前 DateTimeOffset 距离 Unix 纪元(1970-01-01)的微秒数 - /// - public static long ToUnixTimeMicroseconds(this DateTimeOffset dateTimeOffset) - { - // Ticks 单位是 100 纳秒,转换为微秒需除以 10 - long elapsedTicks = dateTimeOffset.Ticks - UnixEpochTicks; - return elapsedTicks / 10; // 1 微秒 = 1000 纳秒 = 10 Ticks - } - - /// - /// 获取当前 DateTimeOffset 距离 Unix 纪元(1970-01-01)的纳秒数 - /// - public static long ToUnixTimeNanoseconds(this DateTimeOffset dateTimeOffset) - { - long nanoseconds = (dateTimeOffset.Ticks - UnixEpochTicks) * 100; - return nanoseconds; - } - - /// - /// 将 long 类型时间戳转换为 DateTime(UTC) - /// - /// 时间戳数值 - /// 时间戳单位 - public static DateTime ConvertToDateTime(long timestamp, TimestampUnit unit = TimestampUnit.Milliseconds) - { - long ticks = unit switch - { - TimestampUnit.Seconds => checked(timestamp * TimeSpan.TicksPerSecond), - TimestampUnit.Milliseconds => checked(timestamp * TimeSpan.TicksPerMillisecond), - TimestampUnit.Microseconds => checked(timestamp * 10), // 1微秒 = 10 Ticks(100纳秒) - TimestampUnit.Nanoseconds => checked(timestamp / 100),// 1 Tick = 100纳秒 - _ => throw new ArgumentException("无效的时间单位", nameof(unit)) - }; - - try - { - DateTime result = new DateTime(UnixEpochTicks + ticks, DateTimeKind.Utc); - // 校验结果是否在 DateTime 合法范围内(0001-01-01 至 9999-12-31) - if (result < DateTime.MinValue || result > DateTime.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(timestamp), "时间戳超出 DateTime 范围"); - } - return result; - } - catch (ArgumentOutOfRangeException ex) - { - throw new ArgumentOutOfRangeException("时间戳无效", ex); - } - } - } -} diff --git a/shared/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj b/shared/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj index 6cf0246..5a6134b 100644 --- a/shared/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj +++ b/shared/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable @@ -16,16 +16,21 @@ - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/shared/JiShe.CollectBus.Common/Models/DeviceCacheBasicModel.cs b/shared/JiShe.CollectBus.Common/Models/DeviceCacheBasicModel.cs deleted file mode 100644 index 178b625..0000000 --- a/shared/JiShe.CollectBus.Common/Models/DeviceCacheBasicModel.cs +++ /dev/null @@ -1,56 +0,0 @@ -using JiShe.CollectBus.Common.Enums; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.Common.Models -{ - /// - /// 设备缓存基础模型 - /// - public abstract class DeviceCacheBasicModel - { - /// - /// 集中器Id - /// - public int FocusId { get; set; } - - /// - /// 表Id - /// - public int MeterId { get; set; } - - /// - /// 关系映射标识,用于ZSet的Member字段和Set的Value字段,具体值可以根据不同业务场景进行定义 - /// - public virtual string MemberId => $"{FocusId}:{MeterId}"; - - /// - /// ZSet排序索引分数值,具体值可以根据不同业务场景进行定义,例如时间戳 - /// - public virtual long ScoreValue=> ((long)FocusId << 32) | (uint)MeterId; - - /// - /// 是否已处理 - /// - public virtual bool IsHandle { get; set; } = false; - - /// - /// 集中器地址 - /// - public string FocusAddress { get; set;} - - /// - /// 采集时间间隔(分钟,如15) - /// - public int TimeDensity { get; set; } - - /// - /// 表计类型 - /// 电表= 1,水表= 2,燃气表= 3,热能表= 4,水表流量计=5,燃气表流量计=6,特殊电表=7 - /// - public MeterTypeEnum MeterType { get; set; } - } -} diff --git a/shared/JiShe.CollectBus.Common/Models/ServerApplicationOptions.cs b/shared/JiShe.CollectBus.Common/Models/ServerApplicationOptions.cs deleted file mode 100644 index 525b5a2..0000000 --- a/shared/JiShe.CollectBus.Common/Models/ServerApplicationOptions.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace JiShe.CollectBus.Common -{ - /// - /// 服务器应用配置 - /// - public class ServerApplicationOptions - { - /// - /// 服务器标识 - /// - public string ServerTagName { get; set; } - - /// - /// 系统类型 - /// - public string SystemType { get; set; } - - /// - /// 首次采集时间 - /// - public DateTime? FirstCollectionTime { get; set; } - - /// - /// 自动验证时间 - /// - public string AutomaticVerificationTime { get; set; } - - /// - /// 自动获取终端版时间 - /// - public string AutomaticTerminalVersionTime { get; set; } - - /// - /// 自动获取远程通信模块(SIM)版本时间 - /// - public string AutomaticTelematicsModuleTime { get; set; } - - /// - /// 日冻结抄读时间 - /// - public string AutomaticDayFreezeTime { get; set; } - - /// - /// 月冻结抄读时间 - /// - public string AutomaticMonthFreezeTime { get; set; } - - /// - /// 默认协议插件 - /// - public string DefaultProtocolPlugin { get; set; } - } -} diff --git a/shared/JiShe.CollectBus.Domain.Shared/CollectBusDomainSharedModule.cs b/shared/JiShe.CollectBus.Domain.Shared/CollectBusDomainSharedModule.cs index ad19eb4..4530d96 100644 --- a/shared/JiShe.CollectBus.Domain.Shared/CollectBusDomainSharedModule.cs +++ b/shared/JiShe.CollectBus.Domain.Shared/CollectBusDomainSharedModule.cs @@ -8,11 +8,13 @@ using Volo.Abp.Validation.Localization; using Volo.Abp.VirtualFileSystem; using JiShe.CollectBus.Interceptors; using Microsoft.Extensions.DependencyInjection; +using JiShe.ServicePro.IoTDBManagement; namespace JiShe.CollectBus; [DependsOn( typeof(AbpValidationModule), + typeof(IoTDBManagementDomainSharedModule), typeof(AbpDddDomainSharedModule) )] public class CollectBusDomainSharedModule : AbpModule diff --git a/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/DynamicModuleManager.cs b/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/DynamicModuleManager.cs index 8f8e0eb..73d5a85 100644 --- a/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/DynamicModuleManager.cs +++ b/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/DynamicModuleManager.cs @@ -6,7 +6,9 @@ using System.Reflection; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.DependencyInjection; +using Volo.Abp.Logging; using Volo.Abp.Modularity; +using Volo.Abp.Modularity.PlugIns; namespace JiShe.CollectBus.DynamicModule { @@ -17,13 +19,22 @@ namespace JiShe.CollectBus.DynamicModule { private readonly IModuleContainer _moduleContainer; private readonly IServiceProvider _serviceProvider; + //private readonly IModuleLoader _moduleLoader; + //private readonly IAbpApplicationWithInternalServiceProvider _abpApplicationWithInternalServiceProvider; + //private readonly IServiceCollection _services; public DynamicModuleManager( IModuleContainer moduleContainer, - IServiceProvider serviceProvider) + IServiceProvider serviceProvider + //IModuleLoader moduleLoader, + //IServiceCollection services, IAbpApplicationWithInternalServiceProvider abpApplicationWithInternalServiceProvider + ) { _moduleContainer = moduleContainer; _serviceProvider = serviceProvider; + //_moduleLoader = moduleLoader; + //_services = services; + //_abpApplicationWithInternalServiceProvider= abpApplicationWithInternalServiceProvider; } public Type[] GetRegisteredModuleTypes() @@ -31,6 +42,21 @@ namespace JiShe.CollectBus.DynamicModule return _moduleContainer.Modules.Select(m => m.Type).ToArray(); } + public async Task LoadModuleAsync(Type moduleType) + { + if (!typeof(IAbpModule).IsAssignableFrom(moduleType)) + { + throw new ArgumentException($"指定的类型 {moduleType.FullName} 不是有效的ABP模块类型", nameof(moduleType)); + } + //var modules = _moduleLoader.LoadModules(_services, moduleType, new PlugInSourceList()); + using (var application = AbpApplicationFactory.Create(moduleType)) + { + await application.InitializeAsync(); + } + + //await _abpApplicationWithInternalServiceProvider.InitializeAsync(); + } + public async Task ReinitializeModuleAsync(Type moduleType) { if (!typeof(IAbpModule).IsAssignableFrom(moduleType)) diff --git a/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/IDynamicModuleManager.cs b/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/IDynamicModuleManager.cs index 3f87820..079f754 100644 --- a/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/IDynamicModuleManager.cs +++ b/shared/JiShe.CollectBus.Domain.Shared/DynamicModule/IDynamicModuleManager.cs @@ -41,5 +41,7 @@ namespace JiShe.CollectBus.DynamicModule /// 模块名称 /// 模块类型,如果找不到则为null Type GetModuleTypeByName(string moduleName); + + Task LoadModuleAsync(Type moduleType); } } \ No newline at end of file diff --git a/shared/JiShe.CollectBus.Domain.Shared/JiShe.CollectBus.Domain.Shared.csproj b/shared/JiShe.CollectBus.Domain.Shared/JiShe.CollectBus.Domain.Shared.csproj index 5453712..978122c 100644 --- a/shared/JiShe.CollectBus.Domain.Shared/JiShe.CollectBus.Domain.Shared.csproj +++ b/shared/JiShe.CollectBus.Domain.Shared/JiShe.CollectBus.Domain.Shared.csproj @@ -1,21 +1,21 @@ - + - net8.0 + net9.0 enable JiShe.CollectBus true - - + + - + @@ -31,6 +31,12 @@ + + + + + + diff --git a/web/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs b/web/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs index b00b7b6..824a892 100644 --- a/web/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs +++ b/web/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs @@ -19,39 +19,13 @@ using TouchSocket.Sockets; using JiShe.CollectBus.Plugins; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; -using JiShe.CollectBus.Cassandra; namespace JiShe.CollectBus.Host { public partial class CollectBusHostModule { - /// - /// Configures the hangfire. - /// - /// The context. - private void ConfigureHangfire(ServiceConfigurationContext context) - { - var redisStorageOptions = new RedisStorageOptions() - { - Db = context.Services.GetConfiguration().GetValue("Redis:HangfireDB") - }; - - Configure(options => { options.IsJobExecutionEnabled = false; }); - - context.Services.AddHangfire(config => - { - config.UseRedisStorage( - context.Services.GetConfiguration().GetValue("Redis:Configuration"), redisStorageOptions) - .WithJobExpirationTimeout(TimeSpan.FromDays(7)); - var delaysInSeconds = new[] { 10, 60, 60 * 3 }; // 重试时间间隔 - const int Attempts = 3; // 重试次数 - config.UseFilter(new AutomaticRetryAttribute() { Attempts = Attempts, DelaysInSeconds = delaysInSeconds }); - //config.UseFilter(new AutoDeleteAfterSuccessAttribute(TimeSpan.FromDays(7))); - config.UseFilter(new JobRetryLastFilter(Attempts)); - }); - context.Services.AddHangfireServer(); - } + /// /// Configures the JWT authentication. @@ -122,22 +96,7 @@ namespace JiShe.CollectBus.Host }; }); } - - /// - /// Configures the cache. - /// - /// The context. - private void ConfigureCache(ServiceConfigurationContext context) - { - Configure( - options => { options.KeyPrefix = "CollectBus:"; }); - var configuration = context.Services.GetConfiguration(); - var redis = ConnectionMultiplexer.Connect($"{configuration.GetValue("Redis:Configuration")},defaultdatabase={configuration.GetValue("Redis:DefaultDB")}"); - context.Services - .AddDataProtection() - .PersistKeysToStackExchangeRedis(redis, "CollectBus-Protection-Keys"); - } - + /// /// Configures the swagger services. /// @@ -194,9 +153,9 @@ namespace JiShe.CollectBus.Host options => { options.IgnoredUrls.Add("/AuditLogs/page"); - options.IgnoredUrls.Add("/hangfire/stats"); + //options.IgnoredUrls.Add("/hangfire/stats"); options.IgnoredUrls.Add("/hangfire/recurring/trigger"); - options.IgnoredUrls.Add("/cap"); + //options.IgnoredUrls.Add("/cap"); options.IgnoredUrls.Add("/"); }); } @@ -252,11 +211,9 @@ namespace JiShe.CollectBus.Host /// private void ConfigureHealthChecks(ServiceConfigurationContext context, IConfiguration configuration) { - if (!configuration.GetValue("HealthChecks:IsEnable")) return; - var cassandraConfig = new CassandraConfig(); - configuration.GetSection("Cassandra").Bind(cassandraConfig); + if (!configuration.GetValue("HealthChecks:IsEnable")) return; context.Services.AddHealthChecks() - .AddMongoDb(configuration.GetConnectionString("Default"), "MongoDB", HealthStatus.Unhealthy) + //.AddMongoDb(configuration.GetConnectionString("Default"), "MongoDB", HealthStatus.Unhealthy) .AddRedis(configuration.GetValue("Redis:Configuration") ?? string.Empty, "Redis", HealthStatus.Unhealthy) //.AddKafka(new Confluent.Kafka.ProducerConfig diff --git a/web/JiShe.CollectBus.Host/CollectBusHostModule.cs b/web/JiShe.CollectBus.Host/CollectBusHostModule.cs index 1666341..a43aabe 100644 --- a/web/JiShe.CollectBus.Host/CollectBusHostModule.cs +++ b/web/JiShe.CollectBus.Host/CollectBusHostModule.cs @@ -1,18 +1,13 @@ -using Hangfire; -using HealthChecks.UI.Client; -using JiShe.CollectBus.Common; -using JiShe.CollectBus.Host.Extensions; +using JiShe.CollectBus.Common; using JiShe.CollectBus.Host.HealthChecks; using JiShe.CollectBus.Host.Swaggers; -using JiShe.CollectBus.IoTDB.Options; -using JiShe.CollectBus.MongoDB; +using JiShe.ServicePro.ServerOptions; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Swashbuckle.AspNetCore.SwaggerUI; using Volo.Abp; using Volo.Abp.AspNetCore.Authentication.JwtBearer; using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; -using Volo.Abp.BackgroundWorkers.Hangfire; using Volo.Abp.Caching.StackExchangeRedis; using Volo.Abp.Modularity; using Volo.Abp.Swashbuckle; @@ -30,9 +25,7 @@ namespace JiShe.CollectBus.Host typeof(AbpSwashbuckleModule), typeof(AbpTimingModule), typeof(CollectBusApplicationModule), - typeof(CollectBusMongoDbModule), - typeof(AbpCachingStackExchangeRedisModule), - typeof(AbpBackgroundWorkersHangfireModule) + typeof(AbpCachingStackExchangeRedisModule) )] public partial class CollectBusHostModule : AbpModule { @@ -43,12 +36,11 @@ namespace JiShe.CollectBus.Host public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); - ConfigureCache(context); ConfigureSwaggerServices(context, configuration); ConfigureNetwork(context, configuration); ConfigureJwtAuthentication(context, configuration); - ConfigureHangfire(context); - ConfigureAuditLog(context); + //ConfigureHangfire(context); + //ConfigureAuditLog(context); ConfigureCustom(context, configuration); ConfigureHealthChecks(context, configuration); Configure(options => { options.Kind = DateTimeKind.Local; }); @@ -92,10 +84,10 @@ namespace JiShe.CollectBus.Host app.UseAuditing(); app.UseAbpSerilogEnrichers(); app.UseUnitOfWork(); - app.UseHangfireDashboard("/hangfire", new DashboardOptions - { - IgnoreAntiforgeryToken = true - }); + //app.UseHangfireDashboard("/hangfire", new DashboardOptions + //{ + // IgnoreAntiforgeryToken = true + //}); app.UseConfiguredEndpoints(endpoints => { if (!configuration.GetValue("HealthChecks:IsEnable")) return; diff --git a/web/JiShe.CollectBus.Host/HealthChecks/CassandraHealthCheck.cs b/web/JiShe.CollectBus.Host/HealthChecks/CassandraHealthCheck.cs index 1157239..edc5c37 100644 --- a/web/JiShe.CollectBus.Host/HealthChecks/CassandraHealthCheck.cs +++ b/web/JiShe.CollectBus.Host/HealthChecks/CassandraHealthCheck.cs @@ -1,5 +1,5 @@ -using Cassandra; -using JiShe.CollectBus.Cassandra; +//using Cassandra; +//using JiShe.CollectBus.Cassandra; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace JiShe.CollectBus.Host.HealthChecks @@ -31,27 +31,28 @@ namespace JiShe.CollectBus.Host.HealthChecks /// public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { - var cassandraConfig = new CassandraConfig(); - _configuration.GetSection("Cassandra").Bind(cassandraConfig); - try - { - var clusterBuilder = Cluster.Builder(); - foreach (var node in cassandraConfig.Nodes) - { - clusterBuilder.AddContactPoint(node.Host) - .WithPort(node.Port); - } - clusterBuilder.WithCredentials(cassandraConfig.Username, cassandraConfig.Password); - var cluster = clusterBuilder.Build(); - using var session = await cluster.ConnectAsync(); - var result = await Task.FromResult(session.Execute("SELECT release_version FROM system.local")); - var version = result.First().GetValue("release_version"); - return HealthCheckResult.Healthy($"Cassandra is healthy. Version: {version}"); - } - catch (Exception ex) - { - return new HealthCheckResult(context.Registration.FailureStatus, $"Cassandra is unhealthy: {ex.Message}", ex); - } + return HealthCheckResult.Healthy("Cassandra is unhealthy."); + //var cassandraConfig = new CassandraConfig(); + //_configuration.GetSection("Cassandra").Bind(cassandraConfig); + //try + //{ + // var clusterBuilder = Cluster.Builder(); + // foreach (var node in cassandraConfig.Nodes) + // { + // clusterBuilder.AddContactPoint(node.Host) + // .WithPort(node.Port); + // } + // clusterBuilder.WithCredentials(cassandraConfig.Username, cassandraConfig.Password); + // var cluster = clusterBuilder.Build(); + // using var session = await cluster.ConnectAsync(); + // var result = await Task.FromResult(session.Execute("SELECT release_version FROM system.local")); + // var version = result.First().GetValue("release_version"); + // return HealthCheckResult.Healthy($"Cassandra is healthy. Version: {version}"); + //} + //catch (Exception ex) + //{ + // return new HealthCheckResult(context.Registration.FailureStatus, $"Cassandra is unhealthy: {ex.Message}", ex); + //} } } } \ No newline at end of file diff --git a/web/JiShe.CollectBus.Host/HealthChecks/IoTDBHealthCheck.cs b/web/JiShe.CollectBus.Host/HealthChecks/IoTDBHealthCheck.cs index a1cebf2..7063e91 100644 --- a/web/JiShe.CollectBus.Host/HealthChecks/IoTDBHealthCheck.cs +++ b/web/JiShe.CollectBus.Host/HealthChecks/IoTDBHealthCheck.cs @@ -1,8 +1,3 @@ -using System.Net.Sockets; -using JiShe.CollectBus.Cassandra; -using JiShe.CollectBus.IoTDB.Interface; -using JiShe.CollectBus.IoTDB.Options; -using JiShe.CollectBus.IoTDB.Provider; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace JiShe.CollectBus.Host.HealthChecks diff --git a/web/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj b/web/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj index 96786fe..9337526 100644 --- a/web/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj +++ b/web/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable True @@ -24,44 +24,38 @@ - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + @@ -85,4 +79,4 @@ - + \ No newline at end of file diff --git a/web/JiShe.CollectBus.Host/appsettings.Production.json b/web/JiShe.CollectBus.Host/appsettings.Production.json index 3fa91ed..93b480c 100644 --- a/web/JiShe.CollectBus.Host/appsettings.Production.json +++ b/web/JiShe.CollectBus.Host/appsettings.Production.json @@ -4,83 +4,45 @@ "PrepayDB": "server=rm-m5el3d1u1k0wzk70n2o.sqlserver.rds.aliyuncs.com,3433;database=jishe.sysdb;uid=v3sa;pwd=JiShe123;Encrypt=False;Trust Server Certificate=False", "EnergyDB": "server=rm-wz9hw529i3j1e3b5fbo.sqlserver.rds.aliyuncs.com,3433;database=db_energy;uid=yjdb;pwd=Kdjdhf+9*7ad222LL;Encrypt=False;Trust Server Certificate=False" }, - "Redis": { - "Configuration": "47.110.60.222:6379,password=3JBGfyhTaD46nS,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", - "MaxPoolSize": "50", - "DefaultDB": "14", - "HangfireDB": "13" + "FreeRedisOptions": { + "ConnectionString": "47.110.53.196:6379,password=1q3J@BGf!yhTaD46nS#,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true,maxPoolSize=50,defaultdatabase=14", + "UseDistributedCache": true + }, + "FreeSqlProviderOptions": { + "UsePrepayDB": true, + "UseEnergyDB": true, + "PrintLog": true }, "Kafka": { - "BootstrapServers": "47.110.62.104:9092,47.110.53.196:9092,47.110.60.222:9092", + "BootstrapServers": "47.110.62.104:9094,47.110.53.196:9094,47.110.60.222:9094", "EnableFilter": true, - "EnableAuthorization": true, + "EnableAuthorization": false, "SaslUserName": "lixiao", - "SaslPassword": "lixiao@1980", + "SaslPassword": "lixiao1980", "KafkaReplicationFactor": 3, "NumPartitions": 30, - "TaskThreadCount": -1, - "ServerTagName": "JiSheCollectBus100", - "FirstCollectionTime": "2025-04-22 16:07:00" + "TaskThreadCount": -1 }, "IoTDBOptions": { "UserName": "root", - "Password": "root", - //"ClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ], - //"Password": "root", - "ClusterList": [ "121.42.175.177:16667" ], - "PoolSize": 2, - "DataBaseName": "energy", + "Password": "Lixiao@1980", + "TreeModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ], + "TableModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ], + "PoolSize": 32, + "TableModelDataBaseName": "energy", "OpenDebugMode": true, "UseTableSessionPoolByDefault": false }, - "Cassandra": { - "ReplicationStrategy": { - "Class": "NetworkTopologyStrategy", //策略为NetworkTopologyStrategy时才会有多个数据中心,SimpleStrategy用在只有一个数据中心的情况下 - "DataCenters": [ - { - "Name": "dc1", - "ReplicationFactor": 3 - } - ] - }, - "Nodes": [ - { - "Host": "121.42.175.177", - "Port": 19042, - "DataCenter": "dc1", - "Rack": "RAC1" - }, - { - "Host": "121.42.175.177", - "Port": 19043, - "DataCenter": "dc1", - "Rack": "RAC2" - }, - { - "Host": "121.42.175.177", - "Port": 19044, - "DataCenter": "dc1", - "Rack": "RAC2" - } - ], - "Username": "admin", - "Password": "lixiao1980", - "Keyspace": "jishecollectbus", - "ConsistencyLevel": "Quorum", - "PoolingOptions": { - "CoreConnectionsPerHost": 4, - "MaxConnectionsPerHost": 8, - "MaxRequestsPerConnection": 2000 - }, - "SocketOptions": { - "ConnectTimeoutMillis": 10000, - "ReadTimeoutMillis": 20000 - }, - "QueryOptions": { - "ConsistencyLevel": "Quorum", - "SerialConsistencyLevel": "Serial", - "DefaultIdempotence": true - } + "ServerApplicationOptions": { + "ServerTagName": "JiSheCollectBus77", + "SystemType": "Energy", + "FirstCollectionTime": "2025-04-28 15:07:00", + "AutomaticVerificationTime": "16:07:00", + "AutomaticTerminalVersionTime": "17:07:00", + "AutomaticTelematicsModuleTime": "17:30:00", + "AutomaticDayFreezeTime": "02:30:00", + "AutomaticMonthFreezeTime": "03:30:00", + "DefaultProtocolPlugin": "T37612012ProtocolPlugin" }, "HealthChecks": { "IsEnable": false diff --git a/web/JiShe.CollectBus.Host/appsettings.json b/web/JiShe.CollectBus.Host/appsettings.json index 6bfcc45..efc776d 100644 --- a/web/JiShe.CollectBus.Host/appsettings.json +++ b/web/JiShe.CollectBus.Host/appsettings.json @@ -5,81 +5,35 @@ "PrepayDB": "server=118.190.144.92;database=jishe.sysdb;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False", "EnergyDB": "server=118.190.144.92;database=db_energy;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False" }, - "Redis": { - "Configuration": "192.168.5.9:6380,password=1q2w3e!@#,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", - "MaxPoolSize": "50", - "DefaultDB": "14", - "HangfireDB": "13" + "FreeRedisOptions": { + "ConnectionString": "47.110.53.196:6379,password=1q3J@BGf!yhTaD46nS#,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true,maxPoolSize=50,defaultdatabase=14", + "UseDistributedCache": true + }, + "FreeSqlProviderOptions": { + "UsePrepayDB": true, + "UseEnergyDB": true, + "PrintLog": true }, "Kafka": { - "BootstrapServers": "192.168.5.9:29092,192.168.5.9:39092,192.168.5.9:49092", + "BootstrapServers": "47.110.62.104:9094,47.110.53.196:9094,47.110.60.222:9094", "EnableFilter": true, "EnableAuthorization": false, "SaslUserName": "lixiao", "SaslPassword": "lixiao1980", "KafkaReplicationFactor": 3, "NumPartitions": 30, - "TaskThreadCount": -1, - "FirstCollectionTime": "2025-04-22 16:07:00" + "TaskThreadCount": -1 }, "IoTDBOptions": { "UserName": "root", - "Password": "root", - "ClusterList": [ "121.42.175.177:16667" ], + "Password": "Lixiao@1980", + "TreeModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ], + "TableModelClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ], "PoolSize": 32, - "DataBaseName": "energy", + "TableModelDataBaseName": "energy", "OpenDebugMode": true, "UseTableSessionPoolByDefault": false }, - "Cassandra": { - "ReplicationStrategy": { - "Class": "NetworkTopologyStrategy", //策略为NetworkTopologyStrategy时才会有多个数据中心,SimpleStrategy用在只有一个数据中心的情况下 - "DataCenters": [ - { - "Name": "dc1", - "ReplicationFactor": 3 - } - ] - }, - "Nodes": [ - { - "Host": "192.168.5.9", - "Port": 9042, - "DataCenter": "dc1", - "Rack": "RAC1" - }, - { - "Host": "192.168.5.9", - "Port": 9043, - "DataCenter": "dc1", - "Rack": "RAC2" - }, - { - "Host": "192.168.5.9", - "Port": 9044, - "DataCenter": "dc1", - "Rack": "RAC2" - } - ], - "Username": "admin", - "Password": "lixiao1980", - "Keyspace": "jishecollectbus", - "ConsistencyLevel": "Quorum", - "PoolingOptions": { - "CoreConnectionsPerHost": 4, - "MaxConnectionsPerHost": 8, - "MaxRequestsPerConnection": 2000 - }, - "SocketOptions": { - "ConnectTimeoutMillis": 10000, - "ReadTimeoutMillis": 20000 - }, - "QueryOptions": { - "ConsistencyLevel": "Quorum", - "SerialConsistencyLevel": "Serial", - "DefaultIdempotence": true - } - }, "Serilog": { "Using": [ "Serilog.Sinks.Console", @@ -140,7 +94,7 @@ } ], "ServerApplicationOptions": { - "ServerTagName": "JiSheCollectBus99", + "ServerTagName": "JiSheCollectBus77", "SystemType": "Energy", "FirstCollectionTime": "2025-04-28 15:07:00", "AutomaticVerificationTime": "16:07:00", @@ -153,5 +107,8 @@ "PlugInFolder": "", "TCP": { "ClientPort": 10500 + }, + "BackgroundJobs": { + "IsJobExecutionEnabled": false // 关闭任务执行 } } \ No newline at end of file diff --git a/web/JiShe.CollectBus.HttpApi/JiShe.CollectBus.HttpApi.csproj b/web/JiShe.CollectBus.HttpApi/JiShe.CollectBus.HttpApi.csproj index 3ea1d32..d81e1e8 100644 --- a/web/JiShe.CollectBus.HttpApi/JiShe.CollectBus.HttpApi.csproj +++ b/web/JiShe.CollectBus.HttpApi/JiShe.CollectBus.HttpApi.csproj @@ -1,9 +1,9 @@ - + - net8.0 + net9.0 enable JiShe.CollectBus True @@ -15,7 +15,7 @@ - + diff --git a/web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostConst.cs b/web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostConst.cs deleted file mode 100644 index d710275..0000000 --- a/web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostConst.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace JiShe.CollectBus.Migration.Host -{ - /// - /// CollectBusMigrationHostConst - /// - public static class CollectBusMigrationHostConst - { - /// - /// 跨域策略名 - /// - public const string DefaultCorsPolicyName = "Default"; - - /// - /// Cookies名称 - /// - public const string DefaultCookieName = "JiShe.CollectBus.Migration.Host"; - - /// - /// SwaggerUi 端点 - /// - public const string SwaggerUiEndPoint = "/swagger"; - - /// - /// Hangfire 端点 - /// - public const string HangfireDashboardEndPoint = "/hangfire"; - - /// - /// 健康检查 端点 - /// - public const string HealthEndPoint = "/health"; - - /// - /// 健康检查 端点 - /// - public const string HealthDashboardEndPoint = "/health-ui"; - - - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostModule.Configure.cs b/web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostModule.Configure.cs deleted file mode 100644 index ebe0541..0000000 --- a/web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostModule.Configure.cs +++ /dev/null @@ -1,235 +0,0 @@ -using JiShe.CollectBus.Migration.Host.HealthChecks; -using JiShe.CollectBus.Migration.Host.Swaggers; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.DataProtection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.IdentityModel.Tokens; -using Microsoft.OpenApi.Models; -using StackExchange.Redis; -using System.Text; -using Volo.Abp.AspNetCore.Auditing; -using Volo.Abp.Auditing; -using Volo.Abp.Caching; -using Volo.Abp.Modularity; - - -namespace JiShe.CollectBus.Migration.Host -{ - public partial class CollectBusMigrationHostModule - { - /// - /// Configures the JWT authentication. - /// - /// The context. - /// The configuration. - private void ConfigureJwtAuthentication(ServiceConfigurationContext context, IConfiguration configuration) - { - context.Services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - }) - .AddJwtBearer(options => - { - options.TokenValidationParameters = - new TokenValidationParameters() - { - // 是否开启签名认证 - ValidateIssuerSigningKey = true, - ValidateIssuer = true, - ValidateAudience = true, - ValidateLifetime = true, - ClockSkew = TimeSpan.Zero, - ValidIssuer = configuration["Jwt:Issuer"], - ValidAudience = configuration["Jwt:Audience"], - IssuerSigningKey = - new SymmetricSecurityKey( - Encoding.ASCII.GetBytes(configuration["Jwt:SecurityKey"])) - }; - - options.Events = new JwtBearerEvents - { - OnMessageReceived = currentContext => - { - var path = currentContext.HttpContext.Request.Path; - if (path.StartsWithSegments("/login")) - { - return Task.CompletedTask; - } - - var accessToken = string.Empty; - if (currentContext.HttpContext.Request.Headers.ContainsKey("Authorization")) - { - accessToken = currentContext.HttpContext.Request.Headers["Authorization"]; - if (!string.IsNullOrWhiteSpace(accessToken)) - { - accessToken = accessToken.Split(" ").LastOrDefault(); - } - } - - if (string.IsNullOrWhiteSpace(accessToken)) - { - accessToken = currentContext.Request.Query["access_token"].FirstOrDefault(); - } - - if (string.IsNullOrWhiteSpace(accessToken)) - { - accessToken = currentContext.Request.Cookies[@CollectBusMigrationHostConst.DefaultCookieName]; - } - - currentContext.Token = accessToken; - currentContext.Request.Headers.Remove("Authorization"); - currentContext.Request.Headers.Add("Authorization", $"Bearer {accessToken}"); - - return Task.CompletedTask; - } - }; - }); - } - - /// - /// Configures the cache. - /// - /// The context. - private void ConfigureCache(ServiceConfigurationContext context) - { - Configure( - options => { options.KeyPrefix = "CollectBus:"; }); - var configuration = context.Services.GetConfiguration(); - var redis = ConnectionMultiplexer.Connect($"{configuration.GetValue("Redis:Configuration")},defaultdatabase={configuration.GetValue("Redis:DefaultDB")}"); - context.Services - .AddDataProtection() - .PersistKeysToStackExchangeRedis(redis, "CollectBus-Protection-Keys"); - } - - /// - /// Configures the swagger services. - /// - /// The context. - /// The configuration. - private void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration) - { - context.Services.AddSwaggerGen( - options => - { - configuration.GetSection("SwaggerConfig").Get>()?.ForEach(group => - { - options.SwaggerDoc(group.GroupName, - new OpenApiInfo { Title = group.Title, Version = group.Version }); - }); - - options.DocInclusionPredicate((docName, apiDes) => - { - if (docName == "Basic" && string.IsNullOrWhiteSpace(apiDes.GroupName)) return true; - return docName == apiDes.GroupName; - }); - - options.EnableAnnotations(); - options.DocumentFilter(); - options.SchemaFilter(); - var xmlPaths = Directory.GetFiles(AppContext.BaseDirectory, "*.xml") - .Where(a => a.EndsWith("Application.xml") || - a.EndsWith("Application.Contracts.xml") || - a.EndsWith("httpApi.xml") || - a.EndsWith("Host.xml")) - .Distinct() - .ToList(); - foreach (var xml in xmlPaths) options.IncludeXmlComments(xml, true); - }); - } - - /// - /// Configures the audit log. - /// - /// The context. - private void ConfigureAuditLog(ServiceConfigurationContext context) - { - Configure - ( - options => - { - options.IsEnabled = true; - options.EntityHistorySelectors.AddAllEntities(); - options.ApplicationName = "JiShe.CollectBus.Migration"; - } - ); - - Configure( - options => - { - options.IgnoredUrls.Add("/AuditLogs/page"); - options.IgnoredUrls.Add("/hangfire/stats"); - options.IgnoredUrls.Add("/hangfire/recurring/trigger"); - options.IgnoredUrls.Add("/cap"); - options.IgnoredUrls.Add("/"); - }); - } - - - /// - /// Configures the custom. - /// - /// The context. - /// The configuration. - private void ConfigureCustom(ServiceConfigurationContext context, IConfiguration configuration) - { - context.Services.AddSingleton(); - } - - /// - /// Configures the network. - /// - /// The context. - /// The configuration. - public void ConfigureNetwork(ServiceConfigurationContext context, IConfiguration configuration) - { - //context.Services.AddTcpService(config => - //{ - // config.SetListenIPHosts(int.Parse(configuration["TCP:ClientPort"] ?? "10500")) - // //.SetTcpDataHandlingAdapter(()=>new StandardFixedHeaderDataHandlingAdapter()) - // //.SetGetDefaultNewId(() => Guid.NewGuid().ToString())//定义ClientId的生成策略 - // .ConfigurePlugins(a => - // { - // a.Add(); - // a.Add(); - // a.Add(); - // }); - //}); - - //context.Services.AddUdpSession(config => - //{ - // config.SetBindIPHost(int.Parse(configuration["UDP:ClientPort"] ?? "10500")) - // .ConfigurePlugins(a => - // { - // a.Add(); - // a.Add(); - // }) - // .UseBroadcast() - // .SetUdpDataHandlingAdapter(() => new NormalUdpDataHandlingAdapter()); - //}); - } - - /// - /// 健康检查 - /// - /// - /// - private void ConfigureHealthChecks(ServiceConfigurationContext context, IConfiguration configuration) - { - if (!configuration.GetValue("HealthChecks:IsEnable")) return; - context.Services.AddHealthChecks() - .AddRedis(configuration.GetValue("Redis:Configuration") ?? string.Empty, "Redis", - HealthStatus.Unhealthy) - .AddCheck("IoTDB"); - - context.Services - .AddHealthChecksUI(options => - { - options.AddHealthCheckEndpoint("JiSheCollectBusMigration", "/health"); // 映射本地端点 - }) - .AddInMemoryStorage(); - - - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostModule.cs b/web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostModule.cs deleted file mode 100644 index b9a5f05..0000000 --- a/web/JiShe.CollectBus.Migration.Host/CollectBusMigrationHostModule.cs +++ /dev/null @@ -1,110 +0,0 @@ -using Hangfire; -using JiShe.CollectBus.Common; -using JiShe.CollectBus.Migration.Host.HealthChecks; -using JiShe.CollectBus.Migration.Host.Swaggers; -using JiShe.CollectBus.MongoDB; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Swashbuckle.AspNetCore.SwaggerUI; -using Volo.Abp; -using Volo.Abp.AspNetCore; -using Volo.Abp.AspNetCore.Authentication.JwtBearer; -using Volo.Abp.AspNetCore.Serilog; -using Volo.Abp.Autofac; -using Volo.Abp.BackgroundWorkers.Hangfire; -using Volo.Abp.Caching.StackExchangeRedis; -using Volo.Abp.Modularity; -using Volo.Abp.Swashbuckle; -using Volo.Abp.Timing; - -namespace JiShe.CollectBus.Migration.Host -{ - /// - /// - /// - [DependsOn(typeof(CollectBusMigrationHttpApiModule), - typeof(AbpAutofacModule), - typeof(AbpAspNetCoreAuthenticationJwtBearerModule), - typeof(AbpAspNetCoreSerilogModule), - typeof(AbpSwashbuckleModule), - typeof(AbpTimingModule), - typeof(CollectBusMongoDbModule), - typeof(CollectBusMigrationApplicationModule), - typeof(AbpCachingStackExchangeRedisModule) - )] - public partial class CollectBusMigrationHostModule : AbpModule - { - /// - /// ConfigureServices - /// - /// - public override void ConfigureServices(ServiceConfigurationContext context) - { - var configuration = context.Services.GetConfiguration(); - ConfigureCache(context); - ConfigureSwaggerServices(context, configuration); - //ConfigureNetwork(context, configuration); - ConfigureJwtAuthentication(context, configuration); - ConfigureAuditLog(context); - ConfigureCustom(context, configuration); - ConfigureHealthChecks(context, configuration); - Configure(options => { options.Kind = DateTimeKind.Local; }); - - Configure(options => - { - configuration.GetSection(nameof(ServerApplicationOptions)).Bind(options); - }); - - } - - - /// - /// OnApplicationInitialization - /// - /// - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - var app = context.GetApplicationBuilder(); - var configuration = context.GetConfiguration(); - var env = context.GetEnvironment(); - app.UseCorrelationId(); - app.UseStaticFiles(); - app.UseRouting(); - app.UseCors(CollectBusMigrationHostConst.DefaultCorsPolicyName); - app.UseAuthentication(); - app.UseAuthorization(); - if (env.IsDevelopment()) - { - app.UseSwagger(); - app.UseAbpSwaggerUI(options => - { - configuration.GetSection("SwaggerConfig").Get>()?.ForEach(group => - { - options.SwaggerEndpoint($"/swagger/{group.GroupName}/swagger.json", group.Title); //分组显示 - }); - options.DocExpansion(DocExpansion.None); - options.DefaultModelsExpandDepth(-1); - }); - } - app.UseAuditing(); - app.UseAbpSerilogEnrichers(); - app.UseUnitOfWork(); - //app.UseHangfireDashboard("/hangfire", new DashboardOptions - //{ - // IgnoreAntiforgeryToken = true - //}); - app.UseConfiguredEndpoints(endpoints => - { - if (!configuration.GetValue("HealthChecks:IsEnable")) return; - endpoints.MapHealthChecks("/health", new HealthCheckOptions - { - Predicate = _ => true, - ResponseWriter = HealthCheckResponse.Writer - }); - endpoints.MapHealthChecksUI(options => - { - options.UIPath = "/health-ui"; - }); - }); - } - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/Controllers/HomeController.cs b/web/JiShe.CollectBus.Migration.Host/Controllers/HomeController.cs deleted file mode 100644 index be4c1c6..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Controllers/HomeController.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Volo.Abp.AspNetCore.Mvc; - -namespace JiShe.CollectBus.Migration.Host.Controllers -{ - public class HomeController : AbpController - { - public ActionResult Index() - { - return Redirect("/Monitor"); - } - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/Extensions/CustomApplicationBuilderExtensions.cs b/web/JiShe.CollectBus.Migration.Host/Extensions/CustomApplicationBuilderExtensions.cs deleted file mode 100644 index 6dab344..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Extensions/CustomApplicationBuilderExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace JiShe.CollectBus.Migration.Host.Extensions -{ - public static class CustomApplicationBuilderExtensions - { - public static IApplicationBuilder UseProtocolPlugin(this IApplicationBuilder app) - { - return app; - } - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/Extensions/ServiceCollections/ServiceCollectionExtensions.cs b/web/JiShe.CollectBus.Migration.Host/Extensions/ServiceCollections/ServiceCollectionExtensions.cs deleted file mode 100644 index 9351c45..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Extensions/ServiceCollections/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Reflection; -using Volo.Abp.Modularity; - -// ReSharper disable once CheckNamespace -namespace Microsoft.Extensions.DependencyInjection -{ - public static class ServiceCollectionExtensions - { - public static void AddPluginApplications(this IServiceCollection services, string pluginPath = "") - { - if (string.IsNullOrWhiteSpace(pluginPath)) - { - pluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"); - } - var assemblies = GetAssembliesFromFolder(pluginPath); - - foreach (var assembly in assemblies) - { - var applicationServiceType = assembly.GetTypes() - .FirstOrDefault(a => a.IsClass && !a.IsAbstract && typeof(AbpModule).IsAssignableFrom(a)); - services.AddApplication(applicationServiceType); - } - } - - private static IEnumerable GetAssembliesFromFolder(string folderPath) - { - var directory = new DirectoryInfo(folderPath); - if (!directory.Exists) return []; - - var files = directory.GetFiles("*.dll"); - - var assemblies = new List(); - foreach (var file in files) - { - try - { - var assembly = Assembly.LoadFrom(file.FullName); - assemblies.Add(assembly); - } - catch (Exception ex) - { - Console.WriteLine($"Error loading assembly from {file.FullName}: {ex.Message}"); - } - } - - return assemblies; - } - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/HealthChecks/HealthCheckResponse.cs b/web/JiShe.CollectBus.Migration.Host/HealthChecks/HealthCheckResponse.cs deleted file mode 100644 index d15a5da..0000000 --- a/web/JiShe.CollectBus.Migration.Host/HealthChecks/HealthCheckResponse.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Newtonsoft.Json; - -namespace JiShe.CollectBus.Migration.Host.HealthChecks -{ - public class HealthCheckResponse - { - public static Task Writer(HttpContext context, HealthReport healthReport) - { - context.Response.ContentType = "application/json"; - - var result = JsonConvert.SerializeObject(new - { - status = healthReport.Status.ToString(), - errors = healthReport.Entries.Select(e => new - { - key = e.Key, - value = e.Value.Status.ToString() - }) - }); - return context.Response.WriteAsync(result); - - } - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/HealthChecks/IoTDBHealthCheck.cs b/web/JiShe.CollectBus.Migration.Host/HealthChecks/IoTDBHealthCheck.cs deleted file mode 100644 index 31956d9..0000000 --- a/web/JiShe.CollectBus.Migration.Host/HealthChecks/IoTDBHealthCheck.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace JiShe.CollectBus.Migration.Host.HealthChecks -{ - /// - /// IoTDBHealthCheck - /// - /// - public class IoTdbHealthCheck : IHealthCheck - { - private readonly IConfiguration _configuration; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - public IoTdbHealthCheck(IConfiguration configuration) - { - _configuration = configuration; - } - - /// - /// Runs the health check, returning the status of the component being checked. - /// - /// A context object associated with the current execution. - /// A that can be used to cancel the health check. - /// - /// A that completes when the health check has finished, yielding the status of the component being checked. - /// - public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) - { - try - { - // todo 此处需要单独创建连接,并需要在连接打开以后立即关闭,否则会影响整个连接的使用。 - //var ioTDbOptions = new IoTDbOptions(); - //_configuration.GetSection("IoTDBOptions").Bind(ioTDbOptions); - //var pool = new SessionPoolAdapter(ioTDbOptions); - //await pool.OpenAsync(); - return HealthCheckResult.Healthy($"IoTDB is healthy."); - } - catch (Exception ex) - { - return new HealthCheckResult(context.Registration.FailureStatus, $"IoTDB不健康: {ex.Message}", ex); - } - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.Host/JiShe.CollectBus.Migration.Host.csproj b/web/JiShe.CollectBus.Migration.Host/JiShe.CollectBus.Migration.Host.csproj deleted file mode 100644 index 5a66ca8..0000000 --- a/web/JiShe.CollectBus.Migration.Host/JiShe.CollectBus.Migration.Host.csproj +++ /dev/null @@ -1,70 +0,0 @@ - - - - net8.0 - enable - enable - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - - diff --git a/web/JiShe.CollectBus.Migration.Host/Pages/Monitor.cshtml b/web/JiShe.CollectBus.Migration.Host/Pages/Monitor.cshtml deleted file mode 100644 index 9bd1e80..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Pages/Monitor.cshtml +++ /dev/null @@ -1,188 +0,0 @@ -@page -@using JiShe.CollectBus.Migration.Host -@model JiShe.CollectBus.Migration.Host.Pages.Monitor - - -@{ - Layout = null; -} - - - - - - - - - - 后端服务 - - - - -
- -
-
-
- - - -
-

- SwaggerUI -

-
-
-
- -
-
- - - -
-

- Hangfire面板 -

-
-
-
-
-
- - - -
-

- 健康检查API | - UI -

-
-
-
- @*
-
- - - -
-

- 了解更多... -

-
-
-
*@ -
-
- - - \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.Host/Pages/Monitor.cshtml.cs b/web/JiShe.CollectBus.Migration.Host/Pages/Monitor.cshtml.cs deleted file mode 100644 index feab3b1..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Pages/Monitor.cshtml.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace JiShe.CollectBus.Migration.Host.Pages -{ - public class Monitor : PageModel - { - - public void OnGet() - { - - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.Host/Program.cs b/web/JiShe.CollectBus.Migration.Host/Program.cs deleted file mode 100644 index a9bd5b9..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Program.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Serilog; -using Volo.Abp.Modularity.PlugIns; - -namespace JiShe.CollectBus.Migration.Host; - -/// -/// Program -/// -public class Program -{ - /// - /// Main - /// - /// - /// - public static async Task Main(string[] args) - { - //var builder = WebApplication.CreateBuilder(args); - //builder.Host - // .UseContentRoot(Directory.GetCurrentDirectory()) - // .UseSerilog((context, loggerConfiguration) => - // { - // loggerConfiguration.ReadFrom.Configuration(context.Configuration); - // }) - // .UseAutofac(); - //var app = builder.Build(); - //await app.InitializeApplicationAsync(); - //await app.RunAsync(); - - var builder = WebApplication.CreateBuilder(args); - builder.Host.UseContentRoot(Directory.GetCurrentDirectory()) - .UseSerilog((context, loggerConfiguration) => - { - loggerConfiguration.ReadFrom.Configuration(context.Configuration); - }) - .UseAutofac(); - var configuration = builder.Configuration; - await builder.AddApplicationAsync(options => - { - //options.PlugInSources.AddFolder((configuration["PlugInFolder"].IsNullOrWhiteSpace() ? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins") : configuration["PlugInFolder"]) ?? string.Empty); - }); - var app = builder.Build(); - await app.InitializeApplicationAsync(); - await app.RunAsync(); - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.Host/Properties/launchSettings.json b/web/JiShe.CollectBus.Migration.Host/Properties/launchSettings.json deleted file mode 100644 index 642958f..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Properties/launchSettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "profiles": { - "JiShe.CollectBus.Migration.Host": { - "commandName": "Project", - "launchBrowser": true, - "applicationUrl": "http://localhost:44316", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.Host/Swaggers/EnumSchemaFilter.cs b/web/JiShe.CollectBus.Migration.Host/Swaggers/EnumSchemaFilter.cs deleted file mode 100644 index f06c6ab..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Swaggers/EnumSchemaFilter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Interfaces; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace JiShe.CollectBus.Migration.Host.Swaggers -{ - /// - /// swagger 枚举映射, - /// 原因:前端代理生成枚举是数字 - /// - public class EnumSchemaFilter : ISchemaFilter - { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) - { - if (!context.Type.IsEnum) - return; - OpenApiArray openApiArray = new OpenApiArray(); - openApiArray.AddRange((IEnumerable)Enum.GetNames(context.Type).Select(n => new OpenApiString(n))); - schema.Extensions.Add("x-enumNames", openApiArray); - schema.Extensions.Add("x-enum-varnames", openApiArray); - } - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/Swaggers/HiddenAbpDefaultApiFilter.cs b/web/JiShe.CollectBus.Migration.Host/Swaggers/HiddenAbpDefaultApiFilter.cs deleted file mode 100644 index a5ae689..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Swaggers/HiddenAbpDefaultApiFilter.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace JiShe.CollectBus.Migration.Host.Swaggers -{ - /// - /// 在使用nswag的时候,原生默认的api导致生产的代理类存在问题 - /// 所有隐藏原生的api,重写路由 - /// - public class HiddenAbpDefaultApiFilter : IDocumentFilter - { - public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) - { - foreach (ApiDescription apiDescription in context.ApiDescriptions) - { - if (apiDescription.TryGetMethodInfo(out MethodInfo _)) - { - string key = "/" + apiDescription.RelativePath; - if (IsHidden(key)) - swaggerDoc.Paths.Remove(key); - } - } - } - - private bool IsHidden(string key) - { - foreach (string hiddenAbpDefaultApi in GetHiddenAbpDefaultApiList()) - { - if (key.Contains(hiddenAbpDefaultApi)) - return true; - } - return false; - } - - private List GetHiddenAbpDefaultApiList() - { - return new List() - { - "/api/abp/multi-tenancy/tenants", - "/api/account", - "/api/feature-management/features", - "/api/permission-management/permissions", - "/api/identity/my-profile", - "/api/identity", - "/api/multi-tenancy/tenants", - "/api/setting-management/emailing", - "/configuration", - "/outputcache" - }; - } - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/Swaggers/SwaggerConfig.cs b/web/JiShe.CollectBus.Migration.Host/Swaggers/SwaggerConfig.cs deleted file mode 100644 index a363ebb..0000000 --- a/web/JiShe.CollectBus.Migration.Host/Swaggers/SwaggerConfig.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace JiShe.CollectBus.Migration.Host.Swaggers -{ - public class SwaggerConfig - { - public string GroupName { get; set; } - - public string Title { get; set; } - - public string Version { get; set; } - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/appsettings.Development.json b/web/JiShe.CollectBus.Migration.Host/appsettings.Development.json deleted file mode 100644 index bcdd3fc..0000000 --- a/web/JiShe.CollectBus.Migration.Host/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" - } - } -} diff --git a/web/JiShe.CollectBus.Migration.Host/appsettings.Production.json b/web/JiShe.CollectBus.Migration.Host/appsettings.Production.json deleted file mode 100644 index 9c7d2e0..0000000 --- a/web/JiShe.CollectBus.Migration.Host/appsettings.Production.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "ConnectionStrings": { - "Default": "mongodb://admin:4mFmPTTB8tn6aI@47.110.62.104:27017,47.110.53.196:27017,47.110.60.222:27017/JiSheCollectBus?authSource=admin&maxPoolSize=400&minPoolSize=10&waitQueueTimeoutMS=5000", - "PrepayDB": "server=118.190.144.92;database=jishe.sysdb;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False", - "EnergyDB": "server=118.190.144.92;database=db_energy;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False" - }, - "Redis": { - "Configuration": "47.110.60.222:6379,password=3JBGfyhTaD46nS,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", - "MaxPoolSize": "50", - "DefaultDB": "14", - "HangfireDB": "13" - }, - "Kafka": { - "BootstrapServers": "47.110.62.104:9092,47.110.53.196:9092,47.110.60.222:9092", - "EnableFilter": true, - "EnableAuthorization": false, - "SecurityProtocol": "SaslPlaintext", - "SaslMechanism": "Plain", - "SaslUserName": "lixiao", - "SaslPassword": "lixiao1980", - "KafkaReplicationFactor": 3, - "NumPartitions": 30, - "ServerTagName": "JiSheCollectBus100", - "FirstCollectionTime": "2025-04-22 16:07:00" - }, - "IoTDBOptions": { - "UserName": "root", - "Password": "Yp2eU6MVdIjXCL", - "ClusterList": [ "47.110.53.196:6667", "47.110.60.222:6667", "47.110.62.104:6667" ], - "PoolSize": 2, - "DataBaseName": "energy", - "OpenDebugMode": true, - "UseTableSessionPoolByDefault": false - }, - "Cassandra": { - "ReplicationStrategy": { - "Class": "NetworkTopologyStrategy", //策略为NetworkTopologyStrategy时才会有多个数据中心,SimpleStrategy用在只有一个数据中心的情况下 - "DataCenters": [ - { - "Name": "dc1", - "ReplicationFactor": 3 - } - ] - }, - "Nodes": [ - { - "Host": "121.42.175.177", - "Port": 19042, - "DataCenter": "dc1", - "Rack": "RAC1" - }, - { - "Host": "121.42.175.177", - "Port": 19043, - "DataCenter": "dc1", - "Rack": "RAC2" - }, - { - "Host": "121.42.175.177", - "Port": 19044, - "DataCenter": "dc1", - "Rack": "RAC2" - } - ], - "Username": "admin", - "Password": "lixiao1980", - "Keyspace": "jishecollectbus", - "ConsistencyLevel": "Quorum", - "PoolingOptions": { - "CoreConnectionsPerHost": 4, - "MaxConnectionsPerHost": 8, - "MaxRequestsPerConnection": 2000 - }, - "SocketOptions": { - "ConnectTimeoutMillis": 10000, - "ReadTimeoutMillis": 20000 - }, - "QueryOptions": { - "ConsistencyLevel": "Quorum", - "SerialConsistencyLevel": "Serial", - "DefaultIdempotence": true - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.Host/appsettings.json b/web/JiShe.CollectBus.Migration.Host/appsettings.json deleted file mode 100644 index d7ebf50..0000000 --- a/web/JiShe.CollectBus.Migration.Host/appsettings.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "Serilog": { - "Using": [ - "Serilog.Sinks.Console", - "Serilog.Sinks.File" - ], - "MinimumLevel": { - "Default": "Warning", - "Override": { - "Microsoft": "Warning", - "Volo.Abp": "Warning", - "Hangfire": "Warning", - "DotNetCore.CAP": "Warning", - "Serilog.AspNetCore": "Information", - "Microsoft.EntityFrameworkCore": "Warning", - "Microsoft.AspNetCore": "Warning", - "Microsoft.AspNetCore.Diagnostics.HealthChecks": "Warning" - } - }, - "WriteTo": [ - { - "Name": "Console" - }, - { - "Name": "File", - "Args": { - "path": "logs/logs-.txt", - "rollingInterval": "Day" - } - } - ] - }, - "App": { - "SelfUrl": "http://localhost:44315", - "CorsOrigins": "http://localhost:4200,http://localhost:3100" - }, - "ConnectionStrings": { - "Default": "mongodb://mongo_PmEeF3:lixiao1980@192.168.5.9:27017/JiSheCollectBus?authSource=admin&maxPoolSize=400&minPoolSize=10&waitQueueTimeoutMS=5000", - "Kafka": "192.168.5.9:29092,192.168.5.9:39092,192.168.5.9:49092", - "PrepayDB": "server=118.190.144.92;database=jishe.sysdb;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False", - "EnergyDB": "server=118.190.144.92;database=db_energy;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False" - }, - "Redis": { - "Configuration": "192.168.5.9:6380,password=1q2w3e!@#,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", - "MaxPoolSize": "50", - "DefaultDB": "14", - "HangfireDB": "13" - }, - "Jwt": { - "Audience": "JiShe.CollectBus", - "SecurityKey": "dzehzRz9a8asdfasfdadfasdfasdfafsdadfasbasdf=", - "Issuer": "JiShe.CollectBus", - "ExpirationTime": 2 - }, - "HealthChecks": { - "IsEnable": true, - "HealthCheckDatabaseName": "HealthChecks", - "EvaluationTimeInSeconds": 10, - "MinimumSecondsBetweenFailureNotifications": 60 - }, - "SwaggerConfig": [ - { - "GroupName": "Basic", - "Title": "【后台管理】基础模块", - "Version": "V1" - }, - { - "GroupName": "Business", - "Title": "【后台管理】业务模块", - "Version": "V1" - } - ], - "Kafka": { - "BootstrapServers": "192.168.5.9:29092,192.168.5.9:39092,192.168.5.9:49092", - "EnableFilter": true, - "EnableAuthorization": false, - "SecurityProtocol": "SaslPlaintext", - "SaslMechanism": "Plain", - "SaslUserName": "lixiao", - "SaslPassword": "lixiao1980", - "KafkaReplicationFactor": 3, - "NumPartitions": 30, - "FirstCollectionTime": "2025-04-22 16:07:00" - }, - "IoTDBOptions": { - "UserName": "root", - "Password": "root", - "ClusterList": [ "192.168.5.9:6667" ], - "PoolSize": 32, - "DataBaseName": "energy", - "OpenDebugMode": false, - "UseTableSessionPoolByDefault": false - }, - "Cassandra": { - "ReplicationStrategy": { - "Class": "NetworkTopologyStrategy", //策略为NetworkTopologyStrategy时才会有多个数据中心,SimpleStrategy用在只有一个数据中心的情况下 - "DataCenters": [ - { - "Name": "dc1", - "ReplicationFactor": 3 - } - ] - }, - "Nodes": [ - { - "Host": "192.168.5.9", - "Port": 9042, - "DataCenter": "dc1", - "Rack": "RAC1" - }, - { - "Host": "192.168.5.9", - "Port": 9043, - "DataCenter": "dc1", - "Rack": "RAC2" - }, - { - "Host": "192.168.5.9", - "Port": 9044, - "DataCenter": "dc1", - "Rack": "RAC2" - } - ], - "Username": "admin", - "Password": "lixiao1980", - "Keyspace": "jishecollectbus", - "ConsistencyLevel": "Quorum", - "PoolingOptions": { - "CoreConnectionsPerHost": 4, - "MaxConnectionsPerHost": 8, - "MaxRequestsPerConnection": 2000 - }, - "SocketOptions": { - "ConnectTimeoutMillis": 10000, - "ReadTimeoutMillis": 20000 - }, - "QueryOptions": { - "ConsistencyLevel": "Quorum", - "SerialConsistencyLevel": "Serial", - "DefaultIdempotence": true - } - }, - "ServerApplicationOptions": { - "ServerTagName": "JiSheCollectBus8", - "SystemType": "Energy", - "FirstCollectionTime": "2025-04-28 15:07:00", - "AutomaticVerificationTime": "16:07:00", - "AutomaticTerminalVersionTime": "17:07:00", - "AutomaticTelematicsModuleTime": "17:30:00", - "AutomaticDayFreezeTime": "02:30:00", - "AutomaticMonthFreezeTime": "03:30:00", - "DefaultProtocolPlugin": "T37612012ProtocolPlugin" - }, - "PlugInFolder": "", - "TCP": { - "ClientPort": 10500 - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.Host/wwwroot/images/cap.png b/web/JiShe.CollectBus.Migration.Host/wwwroot/images/cap.png deleted file mode 100644 index 76c667e0525b3368071af5abc103685afcb1894d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11113 zcmdUV=R2HV)UJde`slq3gQ!uWmxR$GTJ+u`dM7#&Z4iVg(PK!2=)Lz&FnaWEM2$A0 zo@ajV^`3v=d^#T(<9eQ5*1Ffd?zQ%g)>Ky_#G}Q-z`!6>QHH+8z<9_D{5^n+4g6-i zM>qf<4?JHh$zha@((eLauxwwby}-bzOu)afd<1-d?51q!iGe}XjsAJi@A~5n1_sxi z3iO4Jui3#;l#kAAEB`U-z9rMc)7T|oZ@J`pnKz!nM4w(WPO{5qSY)%FO&F8(rj=BiXIUfi}|(CB*4H269s z6b6G0U{M>PKgjJIJX@y5J9Ss)Cy5UbZ<7uAT z$S?+F#Br>(Q{K1kdOa@j$?04Yf=kE2INuTQLb2MHFl}c^uDItB?z_9`Dk4lM{&qa4zE^YzUjO)`&qnr_QeLWR@UnwJQBsW09y?`nS}eX~7wbEYizp$hL- z#eAqP6Z1GY)*qELHW3RE1k-J6J|8+C16Eyi~6TJ$4IPu&j>S7QEgZGci z35SM%U}+42^4%uaba=Ef#&O_nImK|j>Z$ILN&Eg;VV(m5j$uR($=nqqORtw@rMG3q zN*lkdgt#HM@ueDfKcTWgxFrFGy@GC1U-DS>eC_~AmBPron)~*NgN-h*!b(>r`Awqgba9Y4+t+tJT2ib+eadu38ng>3jQsz^Y+?}4YJBHq6rj%tf?<}~~Ao&Hiycm0ia zLrNbjLIp{|NM^q}8At+FImzr|rQ)mxnADMbW$8@j!*6PuvDA!k23 z74C@hLm>j$z0lTP>@*u{#>`sZV-`SzaP5S3pPMZ@|4+-uQCOEWqfCTO<34YW1r5qKkz5AKSA z(ChRJLO|cCU&A;P1!_ooqh06X;#)X;&+O|2m3#7`nnkx4O(s{M>(|(hO(bVHHGuz; z=Kz?kKX-aXp~}DE9rei|rg&NReVr$*U^5?hsT!0>1^5}t-jHO`67H|iy53T0PKQ*eSTT?x9(R0YDN96y8paB`!hargd{)O=+;5hH zmJ41qa&!hw6In1}YyCXr6cy;~Y9Gn10Cb`hB7c-S)C)XtOG>Tu#Md>vq)*M~KPVX4z zJgWt=Jm@iT+7*trYmsJxJ184d5vn>hM~j7sH9y5Wo*nst0k)L57pRGFE%squeJ)5{d21ZWvbp6UtVV`DWKMg;zRZ*-G z8gJiGHAov(m`z@xd7Wy=^wsrRk)SY#04M$WSVV+bOpIBcv>PAPiU(l$qr|D>@$u(V zz#LHN--!tzkU^jCE0niX)a`4Lt9LF6sP*5-j0=&b@9M836TZBZ(v}LilB@{->Do`( z5Oc(etZ>W?hbhMa;e*}ALIvcYkA^>k-Q}wrSR^)?njtJZrH;t^ZB+w(J}9cizG5Ws zUg3x4qj&+%;Stn$s~fhHu}r7(49%s)Lal}(^sFDf$#L&mqOG6(+py}nsq34-M}YvJ zh-_eGt2eyFL$te!mT*&c2-*rZeedJ{@ncAE!_kvw)xck47e&P4waECJ6KxfSd%3ObT<_l+##TL@BEm@fLyVRlYmq6T>?hzn^~R#h+C~ zK=4?1?J1;dxZ|#TwIzToST&8n6nb_AB%lcvCgC3y;e!b4go$+PL z%+�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 diff --git a/web/JiShe.CollectBus.Migration.Host/wwwroot/images/hangfire.png b/web/JiShe.CollectBus.Migration.Host/wwwroot/images/hangfire.png deleted file mode 100644 index 5cdeb40f0157e93361de3ca004454f7e840639f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36278 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 diff --git a/web/JiShe.CollectBus.Migration.Host/wwwroot/images/miniprofiler.png b/web/JiShe.CollectBus.Migration.Host/wwwroot/images/miniprofiler.png deleted file mode 100644 index 244c7029ac13d24d9f11101d0d4ff550cf233dfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22147 zcmdSBRa9GF^fekN#jQBSDH2?ZTY=*4?!gHZm!L(86pFhSC|Y5QXoEjs1keUMxLJC5f=o%r-~a$E zKvDLCj&IH(+{e>oCy@DZ#}Axq7V_Z)1wE`I*(woOp_v34RIN`IiYH|D78nG3kAuCF za{`ln#s~8@a|E-U>kZ3llYI$q4Eh{(3NZs*0h6Mr87p?+fS?naSJJc?N`NnPmZvv$ z48_>T9UgYuK_}DYVB`B@uh$0a9iU` zqo-opVoReJYW2h+4$xt>Qf%SGV1$cFqgNY5detUauviGA(uHf3Tl{aHAcN<>abNTO z-w)DSyc?QujxL|cOy{D=`TzjmF*2fnA2+N!pJi#fn$(y3TxbA*lohb~#p)t~L^=gE z_ra;e*$n)cY`aYqzmt+`j^EA%P-qjMbebFE#u>$nr9oYVO<$W`JUxa_?&HUj4Pbh z@b4TLem+{x*M&2-a&oxSHeu&8od_9GG|ov_-7abo3-~OU8-Dk)UOnIuCGS2hK-$v8 z=tl=Ej7zKaafSSR#kXDd)^-H`D#yvsba*d6hX);glhKVm+Ko4?F6x}{KU4d^o|*rL z`|3QxxY{)oXcz9vjX~ zK}@;20*~5!66qThaWDLmpV}0DEdKqVqnSTzZVldP!w$C zXN~vLG(~+h#FD2x&FgN6Iedk*!QT8GLM-EsqZBmnr_nN{*VKJ`IP&Tb|)jy&JGGgBfT zW+})Z$73uM|2G*iN~oervodbWsOdT`|J{LT#lf7OK7U#{=SO@LCJp^e>)u|cEJ2u0 zKAG5EuScMrJZ2`cUT)z7BEGaHXeh~kbcQc9z9@?>`LxV0JJuZ{Q(lI>$yT_`rQR18 z7J_)uzfYYz7cuZaLuUTNKr)yNsrZ(of)>y#&WuXbc362mQ(Nf}Tu=g1?x^i&V<7;| z=x#5&+nfHo&r0Sp6S}G;EwaX7pD1`hLyTw6Etk7BrkYSCe3}YR9Ytc|Rbp|QIQO^V z_uhgZ$40d=J^c3X33=^}@e06Am(NKwwP+K3J7sLzbfjyXZ`NX~?qpc4uf_PmVhiV4 zKKPUQ(_&W3%k%DOA5VvcMJOs9@4uN0<=wBnlDPE{z3Kefdt6T}N@vWJ@O6NT@>uL* zs3h8nPFwKAo<=GJknv5O_+AYU$DG`C(bo%RCs+$_StNAhNi0_b_4XchI^L{zJ)fS7 z!Wwhpc|Pljjbx-oVx;iM0T}_|W;8tLum@>k*KvnWIcT{T`Z*T~(8RJ768SDJYCu%m zVsUyj`@~xC{wkG8OYP{k76xN?;kVgVi9^x*-05C@rt|XONicKr*Vb#x_V(QFd!tTx zfqwc2@l1+|8QqRcm3enJ3BHwL%t5{a6yyKM-Zs`-_E!PJfHO6fj=Mid7!r+1N*aA% zTO)7fV1-#^k`QJ%7=_Wgjgeaeq%aCo+yVVR91NldZC1-IIm%XB1C^p%hK?>pLjWa2 zfL%|MH^?dYv~dKu;Zq67H5p!jaLqL-;CoCtPkykYkDh&oq*8?u^7vzvJYwB*Dj?IL z2z@Wk{3j`Pvm%+N5Xo2KNQGoYuE(1L_5W_y9=so2d$M|d>mLQ<{32aYcsN;mq}r!t zXWokVj^cIv#CZsLLlHW`lCQ;RPwnv=kmV<10BtVKb-WqQSSC;S6g(eZ|8GGxPLoy& zBNvKM77l>$+k7_=%i`qJM9$HW3n8TCo~3#2p=l?fid~H;!X`A>s^}){x?jr@uZ%(J zRh4k}dVC6&@Y*^$R-s30|AU6~r{33@;cSSei*J%@@#c5@{v4*X0-Y)MIg^|7qdXg> z?MRF?3$g46Jx0qqavAhDjG=YVgWtfCJSF{)9+|Z40=+^ZeT<;9s1{nauBT|%!_`~+ zsC^A|x-^Hre!|+vy`>K3DD2U8rz@RqI^w~c+nzgL?yHfmfIoW;G|vykFp0yR5;dXl zF~r* z6nTRcQn&2MOx;P|Z2h9VBt!nPQGSLJG4JA0ka9$(Ey)+OWLW&Q!<}K1V}9eSwQn)s zFUo&2Q@>Cp>3sTE?Bp%hpjbB?P5?eTt68I1g#>R7{Sl|kv$B_ip>?QO^qV@xOYi3W zjx($ak%7>EI&K=yGyppYic-Ck`u%Pa_I#5#qqBXVV%9MHar3@K&<@uPOkUD4#22bp zTjTq9@bL8BYzWxm>r8*PpWm?*2wBTZ2yRpXuU~?8J44AolURx0m0W>Y?3JE$;rR6E(ids>qY1Ud zvU2D=st3Oha(B$z95=rP1lps+eST7YCP5YYwzaII8r?P5uoK~;mj4;wDpT)du*X_b zC|h;&;fx!Smh8li}y#Bo*hCohS)13GMqJh{6N<>)s&26bCjCySID_HSM z(nMWXQ{YX76&(}RVEfHcvi9f1q=e6gL?V?5x2p0AEV`8SqHFHAN8tqGm1V^XD*U3g z*9=MwAKiUjI*n?l?OKFE()LVws`Bp9esCpWjYjCh5 zmZS956ze*lnz~geEgqC^cUhE*!iUPCau$ZbnpWVH$=3M&ew?O0j8wj}!#h~*b~57$ za|e1#Qa$RbjAWZ}^Dv4Cb`RQHPuQET6j$S9OF;$IC459H3Kd;*< zA!ZL7?2P-Y~ls9sX2dac@l$*T6!@MOHl**JXeEz*Xq_&CA{9?$#PSdj<`j z{@>(>Kp{)kI%V~?=9RrWwBZ@8vv+1E_I9UvK6;%vLny4}NmWYW_?jf-jA0Co&Q8A9 zk`i-2YvfWsx_9725Gau|rtn%GFDSfD)NMw&?)VwF#7B`6WcMix74Xv&OkN6dXXVzk z31i?6W$`w2I1Q*=o*fN^b^2{CMm>3qQ-n`wb;o_c^2~OyJr-7SP?i6<6wma*0!2Y2 zOZaN4uY~+?5SWNkq0nmjS`LFw%gnlU@O`quoYpLoMA8%6PFrYTi#H!aYn+{Ol@fj>dpv$wvAM$S6~mbDj|MTD7FuP}~kZY3+0Y z+3R^Rp4B{6UW-waghFsGa9@zdlD_TSJE73BOjL&;gU3Gfw#qVfxLmAPcCNW$!TcbT zdUloXy&HJbTb1-e8iN~&LGD%FZF94gE58CSeQgKIFlzX(8pfCVmiOf2Qb*`!ga?92xNNvKlAd8qcQ4ahlAii?iB^^LVw(&qK^N z^Ow@1DCl$%{g_rpXEIC6%&n$nty`1m?@3mF(tVL0o{Rj7S4$TjXF?Tx#1(HMx>&m- znKhv@?1%^mm{PX-Pnt>{y2oX~|JGWg7G(`lLOl6Bu9?$UU#`nf6`Vy(YZ};Nv*#uv z>S$iCqT}>kLo6#>rg~BImdv0xcSq4{qJ7k8=o}m)WQ|hOumw%-=H1 zcj?D#{W%0bPXFK@DIA+w`yIJmFigYpJG(El>gTmZd9$-BJ8we${k~+x9(I=sZN>*y zudw>qbKg(B2rc_K8gx1Q0pcH|mAzB=9r@WwKzxplg#drT{eJQ!FYts${d-ZSaK0!2 zDV8~`{N0P2bV|5<&?hd!Oj=B*>t-{tx_BNVFc2$#(s`!hAr5DE=QmAd{@QJ;FzSI8P zUOJ~24b=?Rg*{c_AM+jf^(AWCfE=(+CFsch1#wA=t4tQQ04b(C@wnc`>e4$3Tj6rVN8=<&0Lwrtt zL1GqF6J%e+T9$rXbAZc|=n?R%ip}@=D_fewDDxjiLzCd29bgw?LNd26adNAVl{f!- zHX(E0k#G|yZwG12f#2Utf1$;25zTsR%Il*Nlcsrvh+QK%*4*iXLcvNXionVnQvDZx ze!h7^qvg#z+Mkb$k@NqIIm%iK7+!0RR6G0yJ0gJ+OOQG&asrMVoyCM}<+V3heU2fv z#iQi}(ugeY`^%#!!10-?>1)_zAcUJ-2?<}xp`fnHGLS0Qyp*0lID?0clu^YdiFL5q z@1QOel1K+8(GR(jORTWOMlY7 zhEenE^wuH|)zjr~ffwRfEu#gk=Xu!m9@Loirnc5kxM5Sdu7PZa8v;7q59pxrXuAJF zDHBbLk%uXmJ!{1?z2j`FtBOoxT7x7tOtEmQRbwX;BeM*TfjhAU8UJ-CSMY!1BG>)H z>>n4uK8YxhD@<>Tuk!I+*16C$lVdvT5UYEcmT@lCcMjM!L1Xy@$A#uw;#7U4OA zYPF~53+z`_#wRB@sOKs)Zh3ocom7@)I>7(VSZ--*~aKUGTp>6_+#=CsmBY)1kup40REOxH?jWLTO4oH>l&T9iu3?p~5e7y*(ALNdA7 zljWHSQl?6BP4cv?e9HN>0~`!{Gv3$RJhLZm>*-$f3H-p9mLfWD+A<-UM;-{;_v7Ow z5WKq6^IqtvX#1@@60nA!ucA~TyAT#@-ta&Q^j7p@5v&EpOyu^&84jwzM@smAnMLq= zc;L1`Oxr8ml6R&e61r0Jo|Kh<_ysm|3`Ts6A;Lk@EF5ZtKR16&9{|<1)kDa#95@`~ zd6|?-r@iHqw=}GD9;ArS4d-Xr{d@lYX}IY6)i#_XP^R^0gdd@r;639r!(QaMTYI52 zbontPI_$d=8IQdKqOLGl-gu|av~o9BC*7){f=nyL@>**o+{zfEu)+G3g0tA!nRq?0 zZDkveA${oe!@D%_C}v(}4DYYHR&4ab<$CGQlZ=6B(*cB_y7@JAAdgq_s!5YEP#bbu8tVg3ujxBT|`jS(^xT5fNb^Oxu2*zySo#XM4oaXS$q-L&CH z7673BM`%%syJom3#nY=KU9DT2Ipax{;exlbZhM6g4o?ltoXq&B>b^=mQHOCus}zSZZp;&1qG)T5b>3h%jirA<>MpBnnrFccYEX@ruKzosvyi84N?d=;e@@ z{*4EK>I69g{pGR9X?5NTjnTkEw>0DEq_ox>KLt7l&Xxn!{CKh_v5mRqzJ46bRYA9m zbf8GkXlSe?eHH4=YPUOW3ImoL$Luw6y)gnKVmLBKLj5ofp#H2qEz^wKpm|U&-};TV zD-Tvm;kR$u!KBf~&EHsnpS3;fIM^;^F-HHmLk~VPvb@{6l4zcXVju_+h!A(Tkn7OR>%VH6=A)u*%vsAO z;28NzWs^SqmCpd5I))^Qhqr&|;PE)bLz9A@KaC^!E?k6?{(?pyrQljsryZh&&3vM^ zY7Rbo=Ifn2lRYzTGd6wMb?hr=PY7fDA2JQg!#ZHZon-*fJ!8R;K0+vs&RD}M4{zQ7 zXyzuIE=LcN6mvYGw)T4>9C8Fmm#&FPYMjAu`g+P=|CypfPKvf2Z-*!j?;|!>{j8|o z5d@sPZ{W7~RjWd1bK@M0&LNBdF5EW|g_k2)Ijg#8**bEQy5Z6615)^GOjg>{xkcZL zU&BVj} z#P6mRZOL}~R}P*gfg?;u;`FZi`K{|7E-`%E4VwwEBZg3iS-|dtknz1x-KDogZS+c3 z!`qSdMy@vOxN}zVN#toFMD?%#)$nx;a#XrgU6l?D>PCNw%gWh$&y!bo6`!8lj=D|+ z{Y>{h78Ot!`WLESizpqImRUiK?JF<0g48ouwlA6M(sKW3rbXikGzpHVy+%J*Qa|6J zb%zyR6#JabPX_*1(M%i)WPqlrWzg42JZY%r&o2A3W_43EgR6buy+;F`0dnJ9RaZd* z6WEpvCIZxFogR~(KhjJKV`RNPmt07jSsUUAbwzz}+TD)mHT{~sFCZ~R$3jnNU@CvL z6SOk{h1Hn&wJ`IOvDmx^_guySQZ$q@yNTL1tIubuE05=wTJJ+i)Vqa)TpTk!-^jAz z{a5k*Dm*#oW&I0~`YAdS2>Dr2BK-|zw*2`1vb5&Lzx3GcJraOBw8Y)rF*^N=8L|}p zySnt}TaN7EW`F%;V6=)wDo5x*kl)K(nZRW}`M!h%K2>y?75SRf$8EQT3K*)9zaqJev0VNIljddzOZS`p z9Ay+)NC?J;QCH_M1{fPRX|;+|Y{5{fr5}HZcvimi#Basrv)!_q(fkE#UWcAc~tp}z@egzZ$9EJpxbj_*hAoU z$L^DtXt!mR$A8?k*}NPQxsthHagF;Gj_;jpuZ-cEs& zJ%Hbvq>ZrfKC%=eYHNWG?DuXzRd7iLOg{J!BKS9}%=U}VD=NG}LVvAbs6MU!$!oKjec zB~@o|Tk8{bkY_%EMPN$?8OV?h#6R{%|FNl`M_#Wy7rT!Qpt*h21AUr)Ugc4W-uTuc z-m$(0yB2D12K`#3kXFV$7@CLykw5-kSF?jYt#KV*>9w*Hce0L8zg!nMlO`bU1yf#I zwkZHu%-LoAP$o$IxKG_zgN__SL5)S_&L>z)J`!yUrpFB7Vy94-$+JJvG&wD4+6j)~ zX_Sb_H5eizkSTf3WoMK9>m(uDR6zufKUVPmXfE=I8PZe`*>T0W(bo331|H!@3R z)%S;sU4wHvuw;4jKh8;oqq1`bJrx;os=bi!#S4Nj`@PQeT_+J#sj5gy<<7}^Cyr;j zQ1uSavyJt!35}7d^iDClAuGQQpMTa)Ey0e~UQQoBQE|)g$=AenTnWktsN-6$S~YH1 z_7|k$0T2ZHL?;*f$Rk!Fbc=(bhh&rB7TAX?2l)RL`LT1lO z$Qa7Ae`g4jlwGQO)8|I8|i5@@}a%aH9(cgcN4hMgJ*jX^jthQSs%D>?8H;LP&-;?}mRO zWEZrD-s&ZaW=f+j$AIGL9el=6UM2FTHs`Mt`}?ptvaIr*D@f}%Wzx$kv%h;%LhIq& z6~Oxa6x}kbn|TC+7z(4soPJFTShKLVbrzevvRp;nkbyR4EOMMzsPHJnPYWF4t=0hs zu3wGS;5}pu%(@`(??9ez9%}+^{YLAJibWCo;vZ460;_*UYI7>FCbrfqB9cDR)@qBa)fKVE9^USJQz!LT!$znW)Gk8PKRcTp ztW90yOIoH}Jjzw@OYMG^A|%(^GEA0j90Y7a!_6VfN>twNm6@EZSv&1JJ3AgkB-TyR z1s^~mF@m#z--H-MrbdGr&X#>)GthjKIw-OE*Or=&iq?+$il*f)A65ayPe2umJ}oSO z0TZk~LWMUyLnQ*W*joRyH9UF2pyu1`WqWAx%^Ow(*tLHPAj`6}mt)HN8rP3DhRcTg z@zYjFB8lLf3 z@Z}Up%XnsCOqp1^vX(=KWAdRE`t|yLun%>6`hT-6{QpTN|KD{|Y;;J3JYa!Rc$L=7 zXwamGsX-}PO6VOhw6J4zgxBER(pKwx-8)aDwPcydq_z~2^3C5B_2NV*ymZcS@e&b2A>c`Y4lWWxO#y7{*XG zbpkoqb-OXp4Y*70@|>VJP4Q3YJK)Xq!o!qmY(G1v>4FEAc6rOJrN4CLb5x#jov8`i z`-xMl^nV_77GwS!Cc1X1AF#Z|srkIC`Wg^vYfA`~1*XQ^G*)r4B4T*}GS~;I{ONC; zX>IlEq;aC(?=-aqJ9MdCBeF4W%s+Cfyqq*UbQF9=Lf9={PV<8@;+O@+r=J^Mwk~3x zwi6<2$mKh|HvYu!*mRvL2Va&nyKwk)t|kbWaTi&C>L>^%qDKhBDQ}`+W_m}(ac{HU zP=BR(9q2u#A&nB93QH>=TM9};>IP zNASXFfmd8B9rup;=OHoD_Z+;NVt2xdR)5# zWmZ0mJ#yP)Lt%QKo>;TL~98+KcJRN^0p-}dKp#jmbDyBHhC zKFT3+SHy8BG$TfBuv+s%8-dcJ+L*Smaf1P57FU+;TSZ}|zKTHnT6&9x&$3*r>6}FSVDk5)9RNnGfkY-BFXOUm|rC$W{5_dhV zDkqH-UHx~+ffgXWde@O&r2T0P;(Zpz+q!;sy%T&eT~GhjH2ZPvq=P752>_)7IS1Xv zc0C+snO1wOI0B>uGP_RiXB%qG-kxMmVZH^RvtG>pY7uJ&zwCGiuqg+*Ms4qvNmwJq z<-zH7XLr_W!z8nb6H8-s#+mgdSal_nw^>u`@|H)ce?qzaz4^=Ts73G(TkVC0F7Mqr zg*F%6J}QI*Wgyu1`gE0Y)RwPa9<%YT`}U;4XTXd#7|u+mURmk_YfhmMw`t89kz{RYyN zf=FnRqix>3JJS*S_>o>Ajz_PG;a`#ETPEn}s1<9D-@{2l9^?$dnzj8W1)lVMNDQlI z%uNi^-rD52R7_C^Y*48Rk3&Mv@yMv*Yb^tw!R{qqmejZFg`<|%tATf;DTUdD@Z@4R zVPp!dN%&ZX)I?G|Av|7{p{5g-tO#nysj&-sn$*qC3Q3+fC_3;=;9BDdYW^Db>3Vkf zs4@Yo-`bKl{VV^)?ncR!9x^VE}hYJWMkUL&QtFum$4+dMd(y8qn()p(wc zYhAldd0T{FjEI<0k}>-<|UXy*@oIPcIh zv6>jln&fr<&J%qghfDqQiG89xwuG$MlKz(%DL+Fg&E&G+1 z06>dS#-FJ&ryuOSN-8D1(N&^r=P7MT>z?+&tZ|}4>XLflwR2%R$5$aT5~g3``lb@u z(ZC+%^{j8CS>7FErVsCA1pZjw*t55-m#o_I9D|xoeD0v*s1|K+UO?277UX&e)ss4Y zyTPwzRd-Tn`&7hsb?XzB`=E(0KSFM1pLX}B1T~rYL>Q-pL)91+Bc|#s!tk}=B=fgx zMm|u4urY14&|H}bu#7{Ng7AJio@L9l(esX0ha43?na{iUEU+*FCK}7lnuocb2G=1X z!G~MZOZm$>U8URi7(Yoarzo;AbCmkRFgPlQ5eSME**ctHfirdX79jxwwE6xem(R%Qj9pcGOk*zSFThY~DNgVfAfuCFnD+b^G^r5m{ke}<*-;#^O zNnh5SI`--9`c&ARSZG340Yc!l5mRGfsXQ}ciZjRBk6iiLJZHMS!Gus9qAgg7hcik` zD(cG?P0bF{T?k4Vzp;cz7Dbn*fZVaFP8B>GJ$`T3-Lb-x=-)`QPb)=EPfq$Wr4nbX zVf-4}hv3!*dN*t*L!1c)IPiZH!vnuVIrTQGMv+SoZbcXoBOX zj?_Gkgeo6@%5aE@gxlD=;<^i0Gt~jUBxT>z2vhav^ABxHa){AfSEqU1f`ldvQe#vZuolEXVGSdCW(Pj^ISB7xZ4hFy#1CZt9dd#|bu5c~lu{z@o8I^NAPDX~ zdjVdOVJ44oe0?aN#!1&+QK-a<(%%eq_HMUW`GF1QU`XMEO|YjJ*d8_Y%kY8oyvap( zSxb{Mn1B&UXmk2*n#;V^?H8XI$@}-p2#7_8g-6!BvU_T)pPBz~`2{Yd!z^=*L9Y{9TFJmOYJu z3NO`xAYbtA20}$*p)Lp6JrzsdSituk5oR@XoR9w%GdF|JHViT$KX5;4$&AZ&s2W}VqZ!a?{Gi$-iv*2cP%>X7# zSGEx7cyL@=*6BTAyu~39Zh#OM^2F|+8UpRmrlYY1p`(^;##;+FnWhKVE#3g{rtM9k*;s3uB{k!4EdZU{tt_p`kZPwAJ3s-MSo+o3$cNqJpY}GmpyG_ zoKO`ppX1qK($!;3Jo)pxFrdP*yZk`t9Ad|9)t>W)eu_00pYqsT8mQDmqrRUyj&=3+ zraDrN)KakxC0Xq?UugiJyfsXWi4t59_j;7KL#Ud4(im&9O)R0homAt_i`Tnyim5D& z2v2D6`Ob+!QQgZ$8|rwR=~Dr->t^25=zyTauj`@2Jhl@vEZp#hv#LHQL-fj9{fEx9 zsJzvGIKKvJR6FlUPvT560k}EIEnQxhzy3jvB4gA8|4m8-`XO9Wf%881saS*^+L@ku zv_))d_VZeSYsvxpMwPKQz~v2u9{p}rV#UK9m_9NnP!6^R4VU&Wz|NKs#>?M`qHlkx zL%a-)_pIs%=KBLy8b}aq5pD6R>iM`O-Y5UK<-s}sp;`vV=^!RRWuU?J`RYO34vkgs zA!yr2;()WQ&^Cj>sA|th!7ps=amSk-zAwo~NgZ!w@xD?21mYjt%l9;Ah3w%^MS<%{ zv~REIc*uU&p}5T?`wFRht!;H+xA3%w8&Q*av+#l^;YC zE=`_(!t_cZ>ppqlrA28W=y}li9w}Fx7h1y}Wy#E*7eBaq;RZ^jdO(5lsYaCBGZq0_ zY4Ae6TQ9&~ZUUIigR!iqw%s+(G z`ncN+86P_=t=q& zK6TvJ8s&9-SfT!sbPR;U&ztHiowsIXH55sz6JC2mqJuTb9sXc5RpUFI_3Ew% z?(`un|ms?ulKKQ z4~Z{(v;_0ydHU?8E08{a7>m zMjQL;m}Scwz?s|N>9l)3KOF4t+b;RT_fO26JDXy0aa~>L*{Q+h&dVwn%Kp&LK|2Y( zpnkVlbN|YN-|Ll;<+UqIm#5=L9JQ><5%n}dH=A4U;M(Ge$z1u>rn;%hTL@Ig^G1;$ zTzNO;8eCdb2)Uh_zj;|>eivwMEZOwDcJZ(b8dG5p*vqZ)WmssgJyY&_$`M@K6MlbM zEa@*UBmn)_K!GdKwYuK4le~DDk$8~rjObh`wft&^>(6Q$(An5jR~)qa_$QN;i-B-& zh41s8K|h6?I!**v$83q=9dpoLp?qg!KCky>o88K7_Pdb}h``D$aBR#5pPy8y8y3x5 zI4t+>d=eb6E(~w$2|A4p5D)Uvb&B}@#eW4+a!X(346+W&n9u7jXz;l6_?{2!U>Y9n zKZxT6d8?+FFwjy zGY3H=$kl87e%vjXOX@7?QBfsyoJ+1kZ@qWwXz~}X=S$O6zw%MUePyaYgFJ+8f5r3K z#W=Xd8;id}DV7(lywEUt=f4HRxICrIO%#`KRBEGlSo5d z=~U}|S9%auKM6U>>_M2;7NZ3I3P6Lrs`oXrnqf7;6<$}(x?8iLQm~ou)Wx=W7Y!bB zWKG<$SR>S1bvC){)9H-zj>ziCs~&#@x@w|IeP+#XnM%ei8}IaI;eAtW@Gsz5GPo6F zU7R+X@HicP^L%&PeRozV-*z4RjD+lVWDpFC4yrDAt@l6!vDEiV?RvS~&4h7ojS^)J z;0OxRmVsI%+Jc^ro1!%~Zf`i_u2is1$=WW1c4Wp+_U7C64=d9NCUs603OmNP9t9S7 zt3o}Bqsto)Qv{ArzUl6j;soimbnh9!He-YDORJPpG>~UtyAro09k>a(8o$5qRUCVY zc^nyZIqIV`h7+{OR~u^=&u?Z6IN94m#6X!$sb9KQ0uF-8ZXC{P@B$KE)|>^hD_2eK z=DEalmb7w}HxmM14(|kO|DX~Mn2SvRYVN$v+@L&Htlk;X*m4Yhs39qXY0esCs&217 z3=sB&vY017c)YBazZYFnf&0Iht94%a(8QT!{qnC0K-?fj#<$+h^;M4^De7AY-hRB) zBi?=KpSk!%T|g5pw>=tw`Va>#sx-1T-Y!{~qild_BRmP`LDbQx(&l4@v9UH>kB5g_ zSHUSXlJ^4<0?9+f6oFMs(`K_eJt%XHg`Gu(V*CVezUY|gGogC~X6;@61Z3-rUw(Zh zitFP2>uB_Vf+;|z+*-U`fhqUzuct9zZbSrNX!{j(^42v14c%d3(ZXl3J|eDdd8^uU z)pMFbLHcRYake_={<%a}Xzy}SfX2jJQ(tWXM=)f}{UN!_-EE1G0+c<|DolNtxi2pI zO>z4?Qw%8yy#zrzZ@b?`Hq9%ImSc-0zOM7|uKr<;8d7WkJ__s7mT>@@rt~3aDh4vX6%o7B zdPk)yTuH42L(0X;3juulYG#`CQw47aTA91)XRA|Fey(WCtQP`xZ`0&quqNHu7_uK+ z0c#${4{CV*@1hZe!`Fj-+k08=M6kc}Xj$+RMsU;k%!sp?Q<)hP=4%o};I_{(|CDJVEd0uPh~#4Ic8+t^#1_NmArg zCpnaz{A%P5@Wq+>a>b^>3e$yCw}-yo+@8t7y3PWJjODdgJMx$d?H1N}9X}rmxx{E$ zruL62m$z#4pAR`krzFlGO5T)3ii|;tQw8zP@!%1Hl(gtl%IdM+>yb&giECeM& zy4vyf5GO*ucm>mkDUm`*vdGE*s-ipVs9L2x@B!u9t?d5efqvP6ZBNDCkNKxfJw!bf z!p(#)%~DvL9=3Yt%EdE;PA5ydRVi+M_q0=)#R^``02xJ8p-LhAwCpS3l(0O(8mc38 zIqeGlphr?%XFt!+4AB7EFU|;i;_l)uA|%f1v=>QeK*X91{3>twUY<1$(n#boY3mDz zt)F(}v8U&6Ee5i3T`EkGD5~H~-uDBfGChA!Q|nCCUHalO6HfE z>!MqI>c=)R+qu#64||n7=q5s}T6bGhOK~{C2O^?u zY$>ZkUkTH1L@ZQ;->u0DOg{ZpvGCAmRKH25N63u*XfTluTqf?a)%DtIEjf;9ew<|d zih0Id!#La-0RTMh3I4q75s(EHCv@vKCEwtz5;~fpOBO2F^VkSZu_chaP8_?Wg65x`~j4typkQ*=*B?$5=D>( zivE2mFV#FqaqW`=OT(ZiMA0+bAq0KkYl1Wb5Y{jWF|zln%1M0|>OiV7EoraOpTAz` ze3w~(h?rxO0W9cUR+pwa8+5&Ze54=x5ARg|n{CF6QgCSzxwaq%{%~W+s}h}WC}7q6 z9~;VpD5!vF4OVi+gMtIU;+svWGYKZ1)D4o5kBHg@WFWWlMV&zyO+BL0%cH?jAA?b? ztN1B&yEmBaU{C!onNqZKy5Ql-X-XSQam;4{=c=Z*Srrk&w^&Yoqenh1&Q6D7a|j9# zjSH{E*%=-0W3P;+MZ34EF36+LQ($Ce`|6rZI=W?Sq0XzR3P?g0hk@w$=(Q_LL4K{| zaHj5XCRZh7jE0ZdQXR=kGNJnW?x<4Fqa5PNj!SZbV$yf6bf$e$dv!+v>4o+44mz`@ z;m)M0yK2)OTsKxP&QvWDOQQ?=IkP7!Z$c3!y@deBpT3LMJ>d=p;WA8lrhmRpJ~@=w zVSkgZDImpGDu-Jq>o{!laaAVme+$KLu*izsI-Q@Bm>1yT_S@gWDh1!adPc~qGU@Uk zNsV7DqP{DnXz4>eX2&9X#u5>vK|SxX@iBSFTkLCf774%E?NVrf%t^287=RzD9e zG&JR6J2GOtR{JE}Bb-A4h|@prNIt%K*O;N^-*4e<hl}AIDyB`y7ODIb1 zNkO`$g6%@#7|z>bu`z$i81>Hh2{>wd43DIY4eq1)*D+L-dEy< zYgLHMEch%KnRR(Lp8U?vweCpHlto2vVP_-d#HU5sl`md%rE*1Q$e+a?Wgvl`Ue7u) zKahjM0WDI)`$;n2bfdK#Gfjrc!$U^PS^M@&H%9PU+D@S4AY>39qlJ+izl0)4G zg>MGarAF#dQGV)UVZNUye4_F-Y7D;>+lJ>_OBA!5egRER!VzJ8_ea37B(?h;@X-)A+ni;5WRvG1_;yfdM;T zK*s;3qu3mHxt**txvc|aOtW+lcDWu+u+p|QN{#U$Ufuv~&Zv)>`qw6F{6=qJS03IC z%T=uKt+lJZFRz>8izLC?2*)5ctdiH2r;_eR#Y~z)U?lr1O zc;czX+N|&Od8Juk(uUB#Qv0<_~+8rJH3$%dRbN zqLz`_!40Zd3;aD}5yNeTSdF|$yKbHhP}~#5Pr)I}Ou#{rJ>&>UVj%b96CfAl|5xho zAzqtclJ!$XjX`TchqH1jY(PMB1NRq$p%U{HDV~pMDU5whrKC-w!*93;hp#C@D%##! zRI;hA73z3x9-B1ZTy3bs@JqF&GCR+sE}suH16x~oIK++Gi8RAjtL><-%OMKTgo~W^ zZ3LD@ncXM1FrhLXA^E{OucZQ^iF{*S%fiAU?;oOjOm@B^1dMYz9KRttG9Y}2L-_Bux>I?>gd*NC7Vp~`ubSi zV9)7kNu9LXMT_rSk$rHRrpS{y6SrY1xZ0EPvY5#~eH`i!tH@NdYH_+CCFYqp-1^sZ z0sxTw&;J4ts$P4vK3&Ry`hVcoWeT)*62;!giz%xuSdL>X@}%d)DmgRA~>)R_9Xuxi&W_ z9RhJ~0V_GHWsjC?{ay~*sVQp~5xZWvvWlA&{;Und{wY;A*f(B5ivem{sW&=t+aW8F z#YeyeM5FU;`DXoHs7G&MqJp(NTqU&wWB_MIMU*gEY3ntfo`)u*D7J=J6y(XOTZocH zIC~fz=pJ~`nx@0+aI=^Nd(ZDZL(E+GMz%+Lod5+1=(IBb>uFN;@ZN!4ZwMW^IBv|Y z*0XiSsW|cbcLihW1%uf~O;IEcv!5B;#YXpkzlTW+))kknYgo9UJ;r z`19h;{T=T+PVj8XJaW6l_!bGLRmF9*O-%O8Up&&y=UjgB(lLeGUs2lQ=+Nqp67QIG zuO!JP0vHG=N4Mr}L5W{dNP}!rVL`60f;!Gl=yT@ZFPT zu#wOZea8@+Ieyg--zSgWgkdzjVd4dJYh`C6336xhds%N7nT&0Iq_|4?rQG`=oGv+I z7hh#sma@7fqZ=IwT^NuFJv3oozlH)(h8=Sf+A&OmqH1R-4)XKJvuHYb^}0IJqEkF9 z-r5H7;L({7Z=oSv01}=-p7p^Z_2P$I7X)&ix-@(LM*vL{vhALj8k{w`DHVy{kOvbS ztOHgVYedjlapV?Pd6Cw;NvO0Qc^4RxJF3hKigm-*;>ngu)A#GQ3DkZ1$ z5JAp4Vc6w!uCjLN*-U(7)-((z(&{bcHb*TMuwWA)My6?Egb5>+4W)v_@+rCR%wYYF zTDQ+xh}2URJ(rf<#V&$wZM<(BI06o@BNX+=CuWip>9O7kf4P?m1cJgQm11!2aNCx4 zx^5Q0znGFu-LyNTlB$4UV}@a-=dyA(w{oEg0U!c}02rz9Cf7Ax&gMnCDB1*Oqy8!{!YG%NyN>mgMuK*iG=6RB-5;Kk7xNr_ zWF8qVr`30Ua>5((R5g|_#4{Iu)gx!}h{DQfSs{}qCb)KIwZGgO8<;xtS{IZtqAArd9SZwZ%y6Kh_9juVYCp>TzKnJpxh_e1w%Ck&W=nE&O!pq>O+At?|R=35e$UA zHjhn?=ljkNZ2weiu(}vwWO8n3hO(#%5@iyz%*;l(t89$AsJ-#;@7P`E0Ot@vOi{-d zw{JPn+;pI}@6=#&F8RiH-kutq@fH^)Cl;=~KafdhJ(Z5OLoJk1cd;u_%P z3$I*d0;M7*cI*%NTJNimZm(ei^^~}atNg>4b8r4f=b0my8DUhy)It1Z-qu5nwOgy# z=CuVI+pa(u1tWgRDWzhmpMUE$m)}V-C5%W;v8=gx>;0R{YJxXiQZR`X1zi)@C)4rV ztN(U1SXJz+cHjT(y*K`UlU(O;MPWK;z|pk7cBpG?VJds%Uw*yy;AC;7UsLo{JTuup z72R9C|H<8}Y$xYttatj&?;e+abf&ba*j?n3Gx?EAv6-DLMGm-kXkoVQ}3+Ct)}+dPd38~fQ~C36T%L{1ue9F?BVUDw z2vp5#@BR4nRPRht$hG&09o}+p&9-oSWM=%*)X%>CN^?h@-DS&X)tQm``KkGPzPPh- zca({=ZfEuBBNvnL)cOCqoQ`K)Zg;G2dU9mS;j?EKvbQ))000MEu+ez${ExqRq`IXt zJ((LgH=^lUAmVM=*H8#Sg$Q~}J<(m&@zGf&r2wG&BCgsko7Pj5eQ@EKVD#$5Prmso zVHjcn3r&&3k+vE=C4$sdg$DcR(=+Z{?)iI2=@ z7c;@Ap9-Y)aKp9E{`lC;J1?9{&t^+1i*xhxrMG+2b7@D=w(WsdyW6g>v&e%G`pdmd z`=V!Fxj1%y@~8js%I1y+r^k^?=VvDp3)AU6pWD{3yT&Rcuo3PC8-odEHp=Xjb22-c zm;)(*IDh~$-W-o244d{v=VQtDe|m0VE_3o{7Z60814Kcv!FTwXd+N4sf(W{N_Qrc^ zrw3*fHJ{gViU!SDE@$$6`Jt=l`abvEXPWoc6@}dOdm^#xQ;M2b@`9o(X3EITE4|9l z^>c$yJoix3o;rvi5eN}66-Z&4gAgQ)P?1o9EZTQ-Fv18CRMlKs(Nvo5%qXgw z%jJLpwVW<5IT?rQWskCc(y9A83pXOeZFjBj+c^uS_-^+EmzSAq*1{ zV}?GshY`RO69G{P7c3SN>6QnYX2+A~UhkT_o_urQIHnK)$T?>&5^gVBtK5MI`6|2) zuYG28Zgy;*BhC$gFc3U+^P%SZp4sp8I3R?S(arZY=(_RVi>IdtXXeLJbEC<^fg=Km zh;>8HCGrCohP}Zer_X^9B$NmaYCDi3~5?aWOY@NFUcuIcU1SC7|i7t zWkpt1)ilg}zuKQ0?dj}$aAC3Iu^pab_r1^V(em2lK>Ym6JzcL~g9ri+00KqT&Korm z0H&BS0!6UWiX(zB!xRxr2>^f!q-kGtav*-{7Z>ISQ%6TS5yFM4IMhK)qFw+1-~bbZ zDIyY~j4XGSFkHVk(syET`0`lS>sNb^4?+$lzYuOKAGtV51YF3`?xRku~%^M&ol ze|$DQpSg0Z9}#2=hrJ{b#Z)Ab-4$h3rRQF`bmpaQZg38PtJJpt zsa+KfWy^15BCX$3)Ai;RZCW#d5!o6Jh6C%amLVXNFdL%^Q4(6;=>Z5JfE+-`p{XJD z!0+$I1Y-<|fT={5@0X7RBG`IgqhXlGUpzBCG?SgpWoAYpga831(NJBWqP`Rp9BHpS z{FRPl-+w3W1>)27ihh=Iow=<3Fan`U5|Gky{d3!=ObJ8e0DpgB%e|) zAM5S>+1bQMy8E4;NNd%2&s5i2S9PfEeSF)_Pj7=1E(qs`vqdUT?u1Id?_PI1!yYzPNaM!rx zV-NrNec`5Zlk-$8-TC7AYbX1APYexQ9PyTT@S43HLP`6BEu8afo&AdoIa@&7`A{1b z2se1kp*nZi5v=x0H;Qmzf;VleR1DcxBzc2Hg`RjyTo3-qflX}{y(b3d;t55|3nFs| zTvbhF%^gc(hm{}%8#`)jPVs7I|MbYrVm1c}@Rt_Vw1=DbHn@DwWlyi<5O+P&R?%46 z+u0xMpH8PTnyv{pRuT>ff^g=guDMv^<>%iV?jGOu@YcqTx=^I}z?XJq(pheD1ePS` z9Gece-BILNIpyb^&yFX$-?}ENa{d0u{m<+d986u*`%VrWf8k6jmO1tM`K^ar+yPh1 z!Fpf0=kl?>@xE9#n*|s+U5?^vU)}Z^Pry}BK~%MrZhx$m*ilJ1u%yUBFc=B!dSr`Y z(YQQ5cchRdPgdm;5y{cgeHQRLxce0Wwjy9 z2!ybx%(Lw?E$P%kbVq~$tmu9w5_V8?ArSS!-~QPaMO7kgRVybMn4r>V z@UgFb>hjxt!xzU=$&}7@n@uVX`>I;XBU`G_I(y?5tAMJ02hH(1YTn;5oO4~(bJRX_@ACBkCs9N4bA9z=lYMnek7ev?|P!` z^MCb-U=uV&H*^CN#6$)WhB(|+h6_6 zUq9{%xH$l-tiJog=~tgWssZ(>Z#=y3i9LkfJV~XhXlhQ?HG@zrID|q*TSZn4-M|Dh zkuDc>(N$ejbcm23(GPNQa&BmbDr>5unkL5>5k{FvFeNwDOc(NgYKpGrHC@vogay?Q zSzGs$1Jg8hb*UvcIl_oZjGMgw?C`PgpBU;I(+w@u82HNHf1#u@psD(b1PK6uf;$~8 zNv?08t9s{8&%N}IZP0=vLOjsJALb!~QQ#Dls5QsLR za0vkbxXCqDS8}SZY5)RE2o(qw2qqt#^Fl>9C8y}BP8bpGBBt0-bzL(s#Z41hvvr6v6W zTrM9_2sJ}YU;qNsFjZMI4U>w5FnU|w8Piz4v2%2&xpcrhHA(e#Wc)hEHyJa zM;QeW&J538KQW}Mnyo}Ai-gvlH$lKey4tG&!CFn{dM64(NEjiE*uaL$g26((C&3?G znzx`^mDnoXD(AfAKz%4u{N4*^&b{6(FUq-$3^CkbtzT~P+=8s-HIwKgSH>ePVTaq6 zOUsvz_RUWw5dr1(q2&^12qVER2`x$s6#yTz@!8*I#P z523iRBiesvI6gM_%JWB0|MH?Bh;l}rn@B>Nx%u9P_QS0?V$2^~|VrR?2Xd<4N8A$x#>%Z_7dm#V|bJ=7(>8^0> z`~0qo#gvY$&M6;AN zQyaB8B+)MX_D?j}Aj zbfd0gi`zn1_4Is3Q*_B8+C4VQir;B08}re8vTRI&8$Rb-)mK`PbFz^8?|0jS8$VbN z$_;QMCD82#^I3dctUAX3Q`{Xk#`4FkW+OW`w_V@w_IhxqKKQQWZrwuszl(LgEp7*^ zDz$}$g@whP!m=?I78Vv3cM8kKSXfwCSllTr8)IQ%VPSEnuxyNlg@uL1ox-v)78Vv3 u7IzBE##mTbSXkUCEE{8CVPRo$r}%G&lCJ<-=Pttl0000)at;XuUr|-GDcGam< zRktJLWyKNUaNz&|0HUOXh#~+0j{Wt%1P1);`LLe*@aqNEUP8kO06-e}?*W$b0}1cz zLsVx`b!R0zQ)f2=M-zaOt%bdTle38q_T&ZtKn#!+`K9chectJjOfc-T^;&WIe))PH z1jd8|fCMw#V@UYfr2Z3ABawbg(U_;+$n2WOS0i`I3lxXnos`Wv!o|qwYRD6DV5FgI ztWZCoUP7T*BoRq25f7*X;f(XU9bRgIHdO9EaQq=9=~y@1E;}I}j<%5rPh_`FyB6-N zUaG>Gp~3zqxjh?%g8xYmG^Kok|CazjkhEwhU$UJeC<^|cbfEv0`0o&ZH*ic?m@m1K z{X_b{(VIK(Z_Gye=%=moj(>%ZGe5AvBuJ^Q)wh}B67me3(fZQ&Sk{VDW&YtR*>DDv zC!l-KU4;wd9i~Ej{j6;OHl_oOla3{4qsd(8<)U(hHit^TPCo?-F=ShCVx&|=Es9cW zcsNDC9?I#&LJEs1mvV!}94X>NS($5{>GI@iuo8wnLm4@lAt||t2?M#d%JgQ(!GG7V zP-}{5fN(fA^c-@yl~={@(>!S{L{o@xoXLI&6X4I)#{|H-mXQ50du1isNFk6KVcBvL zV^6Ek-05;Hxr;$BZZT!EL>a}=Swn>Q(pEI87);dDhfD$^NM@^(hCdaNhO~uY59tdE zb-(G;AAi&ta%k}6x|{BXqJi^{H^7F!tF_G+PcHh9cc*G$?!39l^#<3sk41i_Zl0Bl z=l2B{rRp5WGK-&26NY65G8|RqJD%MKZi4ZuPcjP3435arqPgp7YaCd}uzXZ`|3Tei z(qxuUveU&p=lx=Qvfz|b2DDKGAc2W>t>6B*M1khcFCB%(xJy03VSn!H^dZZTXI%Dx zbGZ#^Na^;jxH@-62LN8X4y=^v6lYwBt($FSD=w$zFR0uolML#xFS&_{iS_R*r9|=j zHZ!e!-zN@AW3dfH1y;?2egW{o0o&7B9uo$1MfTk{SGA z@#H@ERucv|=%& zJt7^UEl$13sfu}GHDtsyIH;zB4(EFLrEHha>MUVu9`F1P?RkIa0!RJ^^9ubA?|ONq z3nXuy|M=V98WY)?tm6F0IH|(eTzStI(g9zAbNR`ukx`kMy4JKwg=FIA)8(q*;tvWB zfE7-e7T{ND({7^U;EaWYgtW%7EhI%UI7s!e)UC)Q;0gx-grM_>59i0EHI+bWo&_=^Y3xk;!)y#+#U+DgcS{Dr`wt z30`I&3ve$ZPJo;C+-^$%!S4$aWtPo%wwX8oS>NJ-jdGC*u+$6>i|N>SA4Q0G_jYX6 zapN>na?JVIaKn-7_)SNSp<+q@0Pxn!JEw&{^Y0Cd5Rk1$FxH~H~M%lP04h|WUs%7D9AjxK}Sg!RPxZ_ODeWvrJ zS6c3w^B%@8pHOS?g&@kPas@moIG~4EkKGhbGpxJ0dgZx(5+BZrA^CvFv~~Wn))VRr zuBSg#^qN1ejtv!f__j{3mauRa4+5ciLy<}+SpnF@^6_7GiYO{q@h|)E_aZmJdHZ@T zBRw9$$LxOwj3|X%0VAkEmGW@V#lVF^%dB zf?IhywV05^&Djk>q5mLL6_%@j0nGn=T{j#nCV61`S@x`e=sf1;`eJZE`{&S zO3h`KuUJl-F@5P+pWazQlswvz<;Wo|4&}-{-%>gAOhrkWZDY)%$rdfHl%P)$JT?*s z>L~pSSLE+G@~E(dy-eK#r4l$E&N@!VRUwPUCY_Pws%%GB${RPXCbl>M2w8?BZl-2b0?!BXO(WV5lhkT(< z#PWpXgBX^mK!c4wio{$-b-gPu;}(Tzb!4A4tZK39K}3k1AZ&zc;G~+t9PykVeMOp_ z!2ki6a~+v*QAXnu-5%g6o-IC@9dgO2#TSbl7y}>b0>y!)BeBu#CO(c+N3)Lm$MP&EYOeH|; zCOxkw;gRytkj8{Q26l(*1zppPy-LO9WKc%-s_(<&QP3L*L)#L)PdnCQMr`gwJ#Ebr_E)l4 zM}PWaLPILY!2}ulK=?3r1mXLMj+B%peL@mF_+ebpo~p9cMkT`7cn|qb2ShnF_?b(G z&e-_S2>wB}!7XCJ?>?h@?JSSBGS;Kik*(!4y*RDO8Autlr$>icrH7fr*b7T%v6jH- zS~nufxLW^2xKDYZ^QbExXtKTco!&;48KP9kai?@)7?+b7X6Rs+F!_yqiis+=D(R?+ zB&Vv80x*EbM9xfhq-1X=`wy5PH&P8q1RSu?6l{Bm9kKpC{a~VSI4?0HvhNg0@vO`= zc)K1*PWY$0Hqh(Qv43I0{J%xZv*-UK={0iX4ErlO4%}ecXdQ2{sX^oWtQ2{oS1{1at2d*rcOnOx5U&mqwR9$DEsh=i_=va2-a=t$q-0LHI&mVRpe~casW6l3NA5w(?VV-_@SMPZz^?p!x zi086arrA7ZJ*2t{-j=Krqvc_y3Insu!@AEW;QEUUGbWpX1S!ZFI7yZq35V`;^TBWO zDB=8SZ%th#0(8g%Yx%=!h6(`3DV&K$gS{nGfdpfcXFHPWYBE#O-d{5!(4{*`0mAZ= zhKnM_J%$Nathw6~TcGsENOH<-^ zmwqS!n?Zb$?|_hJT$_n3$T*QEcI^xS&WoH_KqelR_wt}5{cZz zqp&UfQki=X+n@(P7C!oB{(D?XdxTu~4F!jd?(tmP=}5j&hgR`#7Td-(wKc|BFC`sc_Xp&FA<5ftB&gd*j~YPcbqt> ztCford4bCHOvw|y#JH5pqvCrhbTJW#1ZeRq2YL9z0Px|WLfe-e85XJ%YFj99B&d@)fHV(_&AsI4U;$yHS@Ry zG0XTi6QnU&Yj|>VNFh+K$+QZw{CVn%HXusARXVt@tT zqYq913_=gn2wvvLP-QcXDi0@Gm!FgMl)Iyr*hw55NtspDp!JCRrS^Ss*`nn{E=(qa zkBO|;wIMwLX3L=v==GC6O&0Uct^@dV{=Axf=;_ydd|JL?eExfdTdBVF$Ha5-{*h}b zXd@9vVw8~43*9-2Zn0|5<9>PYG(Y3;xjWJZyY>uYt*%?$0!;wVwg?o>wMIIw0K@ zM;6`7I#<;qRWiJR9uyG#^zQ+(yH_O`3}qA-=;W*L-k^q++r6A_#YLR^$zw9teVSbv4=v#Ti-D6}bfO|>exY2UVr*E6S z_mia2D$n@5JQ-uuwoTPuW%n2Y00t=i9>jL~tk2X4&tDgYIhxKQ6o#zRbaWNFeJZco zYv=;R{Kbih<+*K@G`R-h*2kh=#^z4l3!VmjP8j*Pfix6hBI_Z~sM8*pl41kq%8)hC zGKcb|mP?tbImzDTFea4-XcY!ZLGJ64s3rQ@F=Kc>mzG#up2Z(6;%T1`)}5E~DT?ve z6Ld=ifx`Z%;nCdtQ@V`!dDgz3axZFx1QBESOTBzl`IQEVW{m7psZ~w3Y&bpMgSwW> zK$*U!iwuVS1pz;kj}YkzT&5|;zN+}^5#alMA%)NiqX0MVz0H*aY}Su6Jumvt-H=t~ z1#n1|(}%mSyh1?q5#7zOomwt%`mHML^`n<>rB1tbtnfvC)&eskH29-69o|WFt?);$ z!TGGA6=~^{Hy?hQ%z;+}~r>izeLHsI13 z)ENL=Fr4{s>%RFf#;yZGooRs!( zu=r33&VH{*K^PFQyhVSCeb&5F!CM0Ja`Se}5MIXV}yX1{W zPVQsJDkOA|<`k$!y}lFp{P|A)3Cha=c2s8QUKCUmolJu!&P0j6ZJ|rQTz803SDapg zIvTGEM?CSu-0*PXLvt8$Q&OE?k@ddiSC+SZK4g?_`I%h*-UyYSlK#3bi(~>lcHif< zw2woPvU$y#5?3%#As#JS+HhDQv8T(l0x?^uN(%xJsM!+8Dw^|`;VHZdS2m12nhytJ#0 z<_VT#9~Eh4X=%<<88yz4O-gE2sa>J>^OWa~3ZF|`U01sYViWlUgjIS>fVjW1A7f+i ze$Le0>TK?!LrBsY&jv#uX}*{S4==NnPxxa20w737Ua4fe z^pdY}>4tQa$F0lVTvj{V0vVW~vx^SU^MNaycjqYFCPP`ac)b6l1KB$9#4 zM5pp}w?~2EY|f+cf(w9f$+^V@*z&p0HhHih;O_7-oQ4av;gVlEk<9@OCR{7u@P*kk zGV;MK+uX)Kg(ti~hQHx-j_=GTm(lyB0hcL>2{j&Ss0;R`QQTl4M#`gTmA#K9kw6KLap~*SqCP9Ka+qK;vef0b6zK) zcY(F#Kn1QxsBnq2+7_H=bCG}_@U7FRxL}97mLIKN@$E3j@2sxR2mpyKJ=7wb+;#2h zpwTqq`BM_HRSX;JSAwvQfv;SiqxFh1-}wy+AW+)GsM!>Klvl)1e#kP^tIZIY3n7aW zU0Sr0-Wrg}XO6|iHTRa3c7!}-1r4hCJxJfWvj!y8Yf8*JuJ7)a@8{jjPIf~UUA4+C zL|CnTA?8$@zL80CH7SwbX4W*(r~)4H`T_6WjRp@==VaYp?nSr3L?B7~c)_9d(M=)Z z&jc2$83$nirl*U3?j8nSVjk97qz7c{T-%GzW~dlYyz%kRO#?9<-w1Q6*UYR`$CMy+ zHu8^tEPiI%W^9hbBlH-=Sx3f}QXp?_(oc@F=XyGwoH!v0YjePY0Y-Z~Kmu-jEdjg* zNInt|ob!LIWwn+{?Va!wouNUib5GVFkGHR}--Me`s)GR8OWhe}qOe74CRngsiaMR=MhOYAood&E>w2gg8kaEauf|1J#*7?(DBGOoBY9?cId6q1mCp{JfQ(3fB_X!V0g$QBpWV z`q9GN5uyV`($~A#e;#rI-Pw0bSW$7 z=B#0f0vGD>C`WwbM`^{m0p1?jVR0Pv#a;d`F1+9+^Mnxx!BQvD@OH5}JnD0LN`rEQ zm%SI>lGL!{7*7z>nhioxpaE-hH&nxWTF`}SbGwVy)M@d?0ZGAl-7c}xXAt>b;$&XU z{_4)bM)UIQbSSkw|mAFSYr~1_z)TeV_fi8FE*eVlkG{UHC za-|&+<6z+~8T#&!RjpJ_^IYpGsO?37#eLW~YHLp8ft?8ja39vH=lRFa%kOwmu5i8) zNvE98r9=5zVR$D81eInJ3!l}HpSPO&^z1c@EEE^^RMfAjC<(Nso(Kv;XK`_y%1O&;i^XKAG3OYTu&*3<;BWY-+MQAN76Eq=( zfVJ#+-@V>-anNY2X|hTvP7$dPE$KO7jL&sIuaTOipI7-?U&zZ~pn^&CvtK+;KwvJn z4#62b2tOQwl09}T63mb;*j}XPi?#{qq)VGpSu9a#L)0c%5J`$jOawUcu0e$IAi<3A zX+aTpcj-GLulkiQ-M0tl=qcy%19_L6|4tQ>Km`1X9M0RweR_L`@ZuOI%Wc+ZYrWgg zT}N+ku#;Q#1r5Usv?|xU3LxJc&l{d;)f|243o`*e@?T3@^@=e{ptd%G0fMeup-PtQ z;ze0tS21JbONk17RCbw$8E*3t{a3R7y)XfBrnzC6Nsmv@*P?!5z?U9Ejrqh~?ByAv zt%jd{78?@^$-|2Tw36Lw_48w;v&xQO^7CV-0Llh_lFPI0!BX{Qx4-|QZQWBChx51T zRRaAe6-a7>4gb4T7`w^ViFhA=pCwSsY6TlBlgczp9J@&k4my45JOIX4{$;#CD{=Pd zvE=h)csQ};3|qrmvu3KK0w=bZ6cezqhMC?Uo!X#1C(at#dit8K zwJ1dql&<^AjUtb-+RlZV;H4vq5SBY`wZ*Nl_`T7fVG01_W$m;-_IU4SDCf$=@LY%M zfa8E;LlpgXT1dttzV~@T1&Tx=8C5=p@4Da>go=G2 zz(@GGemPFYqxfF8w$#kWy2Q)2X8(4ShvxcezF+apN$zsuU{_IVRml_w04WEvjrjb=>9nCsQ12)^RL*rFoR9}=X!kn zK+I#XT7b3REKhcrfwpt{sPe;e6z$a|Z33q>O8 zPH1KrN;p0m7XzI`TWe!RFGCn^vzEo`W6B;8JuX`k#%QPsdctr*EsCTBGE&GRlBlW@ zZ})7yUXCT9&O9#`dFQ1rCE9LjGgV#(6;H>5d72iA!jn!ADq(&|{q=a87iJ@mx34!8i~PA`bW`_gOa^L;<5Zl8QpYi@8`mwO_6b|(CqoQ zYz?NKIZ+6^EG-mNrGJapw{(EywU-3Y4_I z7Jjj4rtRnRiHA!olld#5_wVS%algsMSxL&&j2uzKhzR2RPBy#ZK)=IOH`*Y{U?Mol zBTvFP7qNZK1HpR?C0Xgz(Aq@Qql1IP2dXG1;#nRZocHcC7JtuK4IiiA<;N#I$Hh_c z*RgVCd-&Pue^Qfj%n*G{h(p7x+}?MnJ|=vEKE!^P0pJS;rrC*NX^osO{NCC}N+Fb4oI!)S6yn{%$?;%p*JB zFH)Qis2(35-OBS8GPk+r(~|w(>vo1)D0$Na)VT@)g5KoKi`_b9%AfChgJVz=OGrWB z;yXJ#S7r|f54O5B*3t0;1k0IkeILIUD8p`nZWF@cuumE2#X7z?N(%dYK zR;JArp{Lk3zoTSLrbMUKt$I2QmmgN{(9j|Pw6S*W87LoGI|%@?lk?bVY_;B{;OOkA z*nYNhhd<(?tCNB&<6|SgRP!+++!hb-3r7zDu>ZsSu)bm`sWa@@y_wHM4Da`rF@YQ_ za=<{4qCH9rd==aL=f$6R7e)}GUCy+T)XD%4=B3nTJkdL+cO&=h^`l{6(X{A>N%R7I@r&Z6lnJ|GaBaXU6Gpf@hSCqFRQT2 z@|3t1bzC{Rt$z4Vu0Qgu3FgQ^k+Wi74-Y7^kzIv9=48E0s;KmZ& z$lMhU+)h8b%a&XOF((zHX8cA%3p^dwg!De-(u4fuV+T+~LeNf#^nJ*SjW)qookKEq zqJn<6=^81>S_DT{@_HI**Z!v8?%Z6IRo+j1n;`-n>~nZ|=VMeCd!HI7xh~-M=QBS+ zLUGrd5zF4YnrlOewvV{VzCDZ-*;=lu!sBXjnL(#gOQ2)fe0BcSHT;+M08jm>FpuJy zkE~DF&TsQ2dt**ocjPlj0D=|k1G!2&mU z)A7-=t@01I9V6b?Bx^#)9w-OCIXX7B+#-Hu<*# zekQ8&H_+Tdn=Xe4RP*Q-I%hmZozCBX{ad?s-s}$>{m& z$hv6IPse{j0x-ZpqoZ=M;vR6DHo5pf#fG>kD|k$!LM+KMW?&8JN_wjiF*rLIk)2n* zL26bDcdR-R4O`_%W);t7Hw7vd8sAniaSiw9lH5&0i#Kjdm{9ll=fk@Xx`l?Oi;lmm z1IXRcuoe;V0$atc-k7eYZ*_$PA85q{wA(PuP>rw!rj&rpc5A!BIhm(J^DJZ#ic^MN zP2R)QzNc#0rk~A`p9zaUs*sc3`oOL%)zJJ|sZXfc9CbC&EK=sMNn)DPJgCnPUf(P# zZ#F_#RmwS_=%<`lGvVta#ql#H{jT&hnFSJK&G!k~A%I2I)~jigUJ+WzW-!0N%Ve}q zmn$kVZXDA|ox+YbW1JVfTOso|F`PY&t&ot|b)xwW|Dm0PUyge@*nyiZx+?TFjdGZG zixn?ZeDI#*@p{)f_aB%vE$2zM^m?d8_(kZ7+>3K2ADW>_jA!%Y0RB-?d3nyCIM{AT z!^tXC15F9>0~fe31UPU(aY1`%IpiF}KoZ56tjIxZSMpvlDd85?rEG!8_O!EgaD+^}-? z3_DJjeiOc0O=F|oDm*b4f5U!Suj-9ja0q>SeQIyDYMw=%jA5@ZvaUdF6mtfx`E1}X z+va$*+gP99;$<0&L60v2a1Od8gy#l*Q@Z*f8yASume zRA=bkQosU@ga?W%0$yNu(yIh%SFeo6)qw+Wq=SQJT>e(Jcrpd1kH?gnbhmK@XM}2% zXX(T~4u(fvpA@aTe4X1ZD-|>}f`Ep~yjuKn${Y5gSbsdc80jxxl4UF0q%*tmKA0mY z_cwfCPiCdICR`DLmHsv6EvC>xmhQ~%fKA{r(tUZBNcM$hvHs(4iI&Pm>M!s2atucU z15OgL_6-%?T?akw-SqCoBbc%hFB6T6L&IJ{A#-WPaZ%B>HAJY?Q`I_?*NbR)6!@qF zh6xm;LTha-%*`nsCb;>Mk`3V%5xlhAD@=G^LW$7~7{;Z#5#BZi6zysa#;I~71K!8n ziwfzLI+MNQ`lSLz1QDdi!KZ+t`8s?ZJ7rDRHiKt+6N%*qFbk`a*L?HD+5%DpYIi&T z-d`IerC9TR+qZR30d*+b5U|XpS2}jo_Qj?X>^9w)-kkE}vfEsxcycLB@sLPA@L~A- z8kb%8E+F1!&}qQ|{@dFU?j^Rm*NYea!s8{X8`ZeB+yi=g`F}0Y3JsJdpzIca)Y8)H zX_SU#w%^P*Q$tNT8rd<3@TJEJ%Pz5%(;ykrlsTh)ifAD8m7R{S3oP7Rrbe8YP%sB< zrC|Wt^XorK1fsCO3xl5I~DIY zQFS;rZdsdx_R9XbEFP7ZX89g+hrkTIatsU5P0Zlqrai@CP6D9<{gap@qU;5|=~cK* zZjtVlzAXxXhkZ*-TyDbc0tv}T(btIjSMSN`X}Mn-*Ab9uvPLrtEvJ~|ywoWg?k{m+ z*vDKlVu6?vVF$?yh_p&CWrlf7*b|hrXbY%a!9xXnk>T+J!H@En?sa21X$xf5 zI5*W;$12{O(7-@});%;d$d58%2H|*0h`uB_{v>tRgJiZub#>PDIcDI%Lfb5Byq*1h zE7gDFTL>eGCXk8v*cGXc)TDj56RCa<%adgeZ?M_drBpg0gYu+=PX*<*7;f;lo+^jjGQR>F<1 zN%c@6XZnbdt?MbCH9j6Q8V8Vn{(Q1{5c>EBL$asrFVj4PZ=h_?S0I@BRQ;_891tci zu`Ncy`|V%B=Gij@#86LhG_zkwp?8lH=hA%x41|nZw(DTI!8{&VZmu8-_H}w6EDjXu zZr)}l%-Y`WWco1|-?aU4N}!3(tCNt1irWRS|2HV~euvtx7SvBd!WSy6G3NY=v8g64 zIO@+dq~7m*aS8Q$gD*8m-X(ot48DnMpb@dQR@%XcM%9DY$yW8X0enOV?QAjl_9t5A zAj@R|8qiOAWO^EU(Ah?!?o3C^$4mI=8-k>`l;g>RI}nP=KM%j@5}gPqfO6o2Y1L90Sx#)Fco5ZoS08W7kRvZz3Q zif~=HVr%M9Lzq_EyuWyj|FG5FD*zMFLy|DuH53Ryk%!K_1nNkB&j%nFfQb(>2&fGG zsa|Qm&7VpnMu6qaNo_$K1ou+aI;Q`sehmf`;lq0%W(kJhJY6NSuhruBX|j7*$l9Rk zAsvO2-chlKijFy!Eo}HN>V`0zF59NfCV$uO0aqQklx8*ZtsvimZM7CxcOh}adIMuGQZqEfNZp*Wb9|3iK*`?qh&H3O! zfb>y>vb)J*+?c@gsT9#s0*ytI`N=KhID*0C7fY}7mNVYgV~wBs->6rm?8;yO#=`5D z(b3*N+sF{VGXCY|8`JoTO&M_)9(vmU#JoC1FL?$`7-nu^A9TP-Bt%5(q*dNg#k6bn zT?j^dO4Lg~PwN8oJk9mBhTDH`_BwAxyzsLqTvzwP@T*Y4@It%YxOZ7*=a!|hr9H1? z;_cVvjD)efH3+_BN(4W<^Q`t+g;r!$*L)z_1Ts@wRiCW=4g;R9iEWduQ1l?5Yu>x3 zA%;GG8!nXFO!6`IGNh((J2UqbpLL=|O9;u24ty)ZfHiNnB+RnKVSv~ic0s`%3l>x4 z+c1CkghYC!P4hualb;=24Ot>iW}~nF%N?4hDT(3FL|#zal6>1iciE9& z!O>vxg~jzi{R$}|Yig3ZR z$G@@OPoS$N#qS%dK8hv!$qDVO06GXrw!-5jDvZ6_@P$rI(B6{svnGc>LNVhPi61dy}ku8;#NUbedQ1V z5f*5s#{iB}EABS$+2g+){QR@#8b;_sox8I)pU@*0=9l)NXvQrMA!PPHJ`YgOk!$hl z&qnquTN-po-XR>}_@fnOPumm`00}IJD2mUi?4W`V^=UbUb(@J7NY9+TjKkdZgBa50 zUxCo{&-K(5bTd?a05nuz2ctKJ5I!#Kt&Y=gl*1M~^)Fy+gX@CJ@<^EfKqsKUN%OUM zLOO>K)?0T4(b>DjQS`xW4gSN5o_6=+taBLG0K`X2jkr38PAydx55@2-yP?fFrqOa= z>jbeg?C4lXo~!;wf`|}SX!8ooe`LYfeI-YFBiCoq$GR{re)lIV#8wg6zSieXj*cWf zDkLjGc1K`_HAAoO$nSgmYCl51^>G(s(qJ$Bp~@q+4?jUVH&))yi|Ou*otoU%39-Y_ zT#m_wagiC`nP=l*;3RUjMwiO8RBTI34hERc`dZ6z@@H`J(AuiiAutGSWFlA6sa>z?2~>oAF1@^z_Da ztCKId;s~j zu`-}+rQSI)qo!r4IER01RY(8AKd~KAl9Jb zR5s&tq#?*S-41WUPE%@BK(~#=vn&c$ytmujYYyVj{3J%rAd+}0H-QE8e5I(~Us6hB z)LSC8=~QE+;b}R?)vzdn^8eQIk%bKiid3T^!=8krpw-p<lwjoZjZj{I#zr9X-I+iJoz z>Ja{<;ltHf#3g>%q&PNNEi&qYHM(4E@?O=36~4SA8C;w5{h%x1kLQ_}gy>UUqjR-& zz;@FpejLO6g_B8xLF&23$Mo&{|>UlI?`-@jNy%oWOxs1iFC7( z9TE}>izA~dIm5mw|CK&M7rvUaHwgnlV%?ptPO<_j-)ciqH zuQ~&z4c>}fW7ilVB@MH&OgB$jixQ&K&> zgTqgfP_I7uvX>69ukpNul;cxluo@gQzC> zs47|HyUF5G6%kbXpQPcis4kZ25U?cZ7dk(-EKxi4=P`S=PJx>hIbGvb_w-VQ*+v;lT1VXJwlh<8#269zOi|{=LLBlzw%EH&YGjt!2b( zh{IX7S!)SpOGtK3(=AfvZ6$5yz&BBt-A)&u%1x@h=^ZuGDc^Mq)E6z;KyRNB3z0<5 zV7m?=M~fxy^u&2Wpcz~ndA$}!c3;sVE$F=LWHSM2xPB3`?%xPR&5A-P5W9^&+!nCPHa z4FaO}WU-MY6Np}JvfjeR>{=IaTdt>s5jL4R0m`d3ChMcR9I4t$(kRDe%&zi&xBul7 zu~nj&90GM|=4uw2r`84Wi%WtcFzzapviR8wz(06lZmd)8QZdYZ? zmQ=_quL?6zN-EJyDM#5&gds+FNQz1sNw!+JJgxIxyk+lGY=ZI)=a!DNSmIvLW(V}0 z`LY*e`hA`q-^=z6jG!Py&ZUKLMBF>*DKxD*sA|?Yfc|ZwOayXrU9~Ma33j7VkmzVbczcbUY0GzMX(i$w+M| zYReR@Q9~8&dpEG9qrKFe=C5b_=ux*;V&C71^3)M6h^#FW=##{+^rNyL*wdDh9J)lo z`!~!noSNEn%SszZp?~3`q>8o;f5$$cwq%5`CoIY*4%_00iyuN0$}9P zG@!mW`01J;A|blIPKw11CwOMq&1^GWjWKnCGM`Y*05Vz_Du~yqJQ?L$U+6Td#*Pyr zztW=h2>vCntN!ZkU?}^r(qobd29}!C0C^=WxZZG%pGC#*bb1%rtPv<$-E4d~^I9-F zhUH-+%lEU!gz{{`XN3whVv7gkR#}5>BtLhKJ(c=-qkjGT9aGztZC6lm2}FZ9RHAk1 zyx~Dplc#RJaGEiNe-4VO;o1Q%>9HmVM=&vxi#Bfl0Ef;-nTD>+abaz~6G!^5y;~xk zse5J?XEgOyPgnP)ljt{5R(Xq=HvsTwZ9fM7V9WPq^l?gM&7^KU6aK623uXa@(;#tmz}b?1+Ix}7BlT;KHANs zRK7;zCF_@@+mtOY*TWjQ#Zv|iSmLgeQqgfp<#LVBLy&3r!|Yqz^Q349>S!*H?h{C0 zM8L1kLxtCteLSTfJ#eW+^Yi&RM8Ly9Ovfv4PFElbAQ&v3hX?DU=a%Zcls0#d#T5VD z+mfk~jD*&08rpTr{;q`(@8bd_d$LL2vm_WE(n>U8u?m07s&d=#h%`X1x2U_3&nkLB zbwumu+{(02yVj8Bu=0`pHn&$NqhUgpN_foc#XjhE)O3XH(v7`4T?~AWF){%>?TUvk zorpMk13xeB#uhJaU*>)(?b< zURKYzaJ-~sF5Xf_ecl(ptz^RPmJ=oTAJRWE#-=h4>3O=FpJ@i-arj66%yN7BjY9GeB zliN!9DpjY(ND{KFm8LEbY1?*iYvg6BO{vL%`?6iqA|Tnu3N*9Q!{hOfREu$)#XTyX=vv{4lC}E#k3>Dd%V~d%U=lIxaK4yMM zmsXT?QRWZ+E3j5|>vpO9nXN#|vXmCD_FRf5C@K=r?xn;_iy82;DZhmP&D%4Xusmeh z=&MBz7FUv@*U@cvn&Mq$&g7TU1gPd-`w_L8VdB$fd!D;=NeE*z@vrP}dU$xfzg>PW zQ1mpjVZL@UIYzpnib$4Cmf>Wc)aELrM=FzqhY7>iSj*AS9)c>yHNAG62tUSmCwIIq zLp3Mh>q)PUJ z$55l9S_+a&TmA)8#>*TgKMxY zZ>j{10q1fi^~=Yp)!ob?aQ9m z*3WWVbj)B7ac>eZOoG9D%&1#)*ojLll5}bG^W&i0vI$H6(F@0wm0#W-0@bUsAp&6hHr}@S@bPbtXKwTqoaM^^>|rP{a*@9{G)?~t{7}iTV9(?m zPtWMwD@PpCb>?df8I*Zi*^y<(H@AA*JdcLx4Ky)-k!WFWhEV~OZrmf)mXYjJ*)pW# z;I?Q(5hxL6|JlV(oDe3lI9phOdNRJ`2ek4h5;J0kcY(akn*{+LJ;rvMaEEPFKnGh5 zVV=bf%qNJD$k*P#U96tDRb9O{bKiL24Pb*pXq&2la_?CC!IL!SQSq2S}K2rhCfic$HF)hWiSrC9ZL z2k{Rnz#l+}2S-n5byTkU>V!;#7ym4W9Mbne`|p7cMqi2YF6GrRIKG=#6}l}4Dv<_! zw10sJin&%hE1swcpBRD3F+d~udoWmNMQm5IfKeZc*t<`@-~9=F;r9ci8%q}?jh?R? zb3R|L^J_Z0yv=oO#=sKn!-)Z5AH8xnejlnd=;+PG#W@xy0TJ_vUWh283`Yx5V=pK5 z4rR7@+-2piBla>#J$h*w6`a;T>D~^>-rHYqR7-Jqu3OUewo1`?ClxJqs+I#Eatq&ha8XUiyPxhj$ZrsO8&} z^n?4*9R#PN$s8_GoL$ zUl@+*&aWqm>YLCEv>+7g@L|ldxNX@#9U-N3j~Qh8|GKiAD!44Q&5%>uJ$$kkIbVYzJ9jtr+g! zDw_6MEd&@sIA0aI&-H-yRA0sfY<2_838)dbP+lR(eD4j-Qd+H4VVl|5N8J9z9o_1x zX1;BkMS94d*GC8gLEb)?=Qep|R6{sMrwfL$fByZ{sHj8V3In?-$VLkBL51_|;M*v} zReLvU*lD@TOOGjD4QqP^9rfSVTbTqgUeI>cK%mzH2p^j?6aWe*hK2YxhgkR;@kjGl zZ~N5c)cbm_F*I3jy=#}%j};qU@b7x`n_V|Rhou)k>h-2r;)_>BfIcx(x`cGzM%w#e z7&M=5FpIr8sGAXS<2s(zT{vC(q`df&y*r(JyM5LR7XUyW`|p1N0z~knR$=j_XuDnr z#3?ST8yk7(1J)KtWYnd0#O4(Hwyprg436I;Zuxnkx9g+(YAQ4+X>YqfuIp;}ms_aF z0#-&o^lU;{L;~V&x?fq1^j7o62EBmhG7`IvXZl_0RL#iZpDlTtZz&KbzFlA>IEYA>ebE5IF)Ie-?{a-KV z{g;H}{BblbEK3}j?>A}T%9T53p}E5qj<+V7v$ULP?t$e>anHok%s~z`H8mx&rMOX4 z3}@~{1ZQXtK7Rj+@6Y$R`{DJtdpz!O_q z&qM=D@cvf?1v{cY?DcnQb>EL;$8oy8_>KtdW$kwl5n3^gO2dq)c23zjj~@g#nEea= z!gn9x#Y!2&x>m=S5}`iHMMmu?!N*!P1XBuH8GiO`NGHN$FKfNWpgU=|SpUu5Z#Ys| z=(fBhY%S+v5%Ao4^^+uxcspV4=%RW7#grrtGq#&W!_poP{6>kEVQF)z%1W2nN`JGg z7{8~wqa;_CLB-H3vrvC4maHmk|Hz5c?l`NTWZMk*o7w4^95AJwn<~sp4I3PSgkh`qs8*ie|K)H-IAFTk`IAuWre1 z%f!|@Zp#09U#iF&9vD^-p+$O~FeAWE8SC$VlsUev7y99!#o48Cw(X^OeFpyaVnZx1 zXt^d;9dK7DeIr!DF7ijmz`Q(a%1dprhFRUGW*m>Fj8)#AIT?^oseve(AhcZJh4(E! z=QCq-xT@-f$9`G6#$1(ThvNw*ifU-C3V(q!l#tUoTPc}R1X=(7)|Bf>*ycL@Yp#G) z*Ru;xRvUabiL^dwyNd8V)GLMTW(!2>Em}y#jBk}d)*g9J;c|cUW9Jf9jpT#l6aHtrph#wynXdRth#Qj^EJH6! zd7*S~V{Go}3HO+Eg)?3viq+jUY5*_H2AQxL~>BqgLxHhSz zXmZyw=!ZZ|loYkKZ_)gx65phn5O4S_#6({B1&sexO`0cefZ7uz#lL(-dEmrjs@R@zEIGJv!^ky32j@R)*+;i&=G3!fjE)+$>Z-&pe}=JL?Qn_)dc+#SkZjCwWzZtJvr zTn-tgB^5shkO^*9Y`VR*}2?a8OA8Uyt zh`&;cT#^=fI~_-JSsfo0q1yShah(?K2N9|j=)l8-HNY2xPOJMR=I6DkBA7U6N(F30 z|8G>!)PbjJwu&Wfj~&MNd_Z?UHUj`-*An`?>5o;Wb_@QR9NzBPo9US*H*iTcG$@v*e09o#kqp9%dfF{Cf_!fMv(ErzU0D zKwLnLvGQ&H!4jz5-_^FQZ(+)w-jR;S!8)*HnVgV5=bHUsb-yP5MSQypYUpY09jMS$_Skb* zHwfnv{0A2k4|W82*{cnbu2)Q7P)B^t2iJr-iYuRuuFKw|pv0QW(#J3{q7nv`29+DA zyb$$Y@3*hcW#+C%4M(kXW(9WrHl3p9*O_P{UJEJr-od^iH73eEf%_%`4rc5X*|JPb zJZ@GMW9?@(3L|~xnQ03YN+VYKsik*%Nd0&spJ?6qA+fa`XTr-Ck9uMn9#k3-qqV&h zfjhpNFJF*7QThs+Pj0M4+dS$HE9>*ic(RUs8xU*qGuO=XLy-GmLQdPb#d-Wwx#+j^ zFVUm3q_q=at13uE&GZPBRFyGN0c+{?rNaK2bDXBL+o@y5Ber+)dZ>vA-=wd87z3_M zPP3m|mV^19L*#JVblL^0_{L$`ddrtUx@_$QNBITmYbc5 zt>o@uQXHoJ_wbtq!m~fjPTp?VE6+x#A?IvLGm1LcW9wjrJ%7NF;ZsHrPX~AQjz+ZazVjAY!Yv@os1zSu#4CC%@*HrekqLZ zMjDnhph{V zJxSoA>2^ZY=Y@MF?5gZ}@mte5h2w%K+(ZSnN1CLPtpavDjA$Ofr83j0aDpTND3zP4 zHQ^kMKb-P-4ANI!N`*fa5NYJ859Nrxs*T;j7zg-?Z;}fSTLqJ@o6{L*WZ?5&B)wtB z`$2Y8sOS8XGm-#+zy&VT7+0;Mri!^*EIr8ipw!I#U$F8eNh_kIgi52A;_yzwe2^ZS z#)}!Ll^HM!_sl?d`Hk`rR}Z2WOtA8Y<{W&bkYq8aYetuAW{-M>Y?3dm@?AV^b^ahG zS^V=smXLtQUsCS2r$QPr7yE3`a0ZzRBG{zouWuWxD41y(riHO>-|P~D!1Xvc4lF|% zn3xr47Ep>^)q-1(_Tdkf>tMa9v(@>Xqek8pu=1JdBT)|3LXqAgt^_~7@EQa(Cpqkp zvMck9O{ z9${W>1ewTL+ZxB(k2+Xkg&Y+)KXt%M_wd{l!7KGX%r^Vy%=8LQ!xx+Z@w_aTid~+L&%2fk z|JqUZyQ}(Ct$^6Oy4}QdwEy)yh5qF((nPG0=>jjiUY9nsCgj^=75nJ`*1|#>YHem^ z;Qg}lhNcw0+88GnK8m{oxXO2xV_5t#jHuCD^+^dS)fOg>cp{*mXOkd9vi`8@3)+-j zdyV{Tf3)Uo3r^GB=~9hhJw$N;U-mq%A1p_19Cq9Pr+%g>=f}vK(4>!0g!k#OM5Hf% zdozt}?ERXl)}9u^Q3_yh4Gqc+*-u}mo3jJV0Q_%A4du!?V-Hcwo4f}bUe)tb()#3! zk973W=YCS-`%czk$F938SA1*{kS}6%mdQ??u6)_?QX8o+XnzwDG<$Mzq>BOLnfKNi zGqZby3~wK8j)do1?{Bz<%I~6*XjiT;IzydIt_g$}h!M2Sc>yktKDgnnG_nE|d$jDZ z_WeBbv39%q6I85d&RfY_*2*iAR-TnsrtaxjW?K^rptHD^M(-RO+=8u@<&issnbrz! zkPaO+UC~E*>5_|Ln(i^8yWeaA+jC8L!s5#pnk>h)8BTtOTO$+xWGz|0#l-m;A2@6) z@u_kR(XMuTq_7Q+c}dE}G!y=PGG{!sZsgre&%fkUe^Ap*Cxz5mD-{ktVV#Z1)Gk5ea#MPK?kWeXOxvhFLC-{XO8N@uXz;4 zSABaY2SX8zllX=^Kb7C)mUCD6)Nop#j*W*U`Bc0l30o@# zQ*0_Qn5Lz~sD0G(HTB7H@Z8F7olEE-3Y6*c*~Q|)?k2?jlS^UmvtUU*N$Tqj*0_T@ z^|%7vF~J?h8nn`eStY6#l8uCe+kI>sjcDOUIEyUY;fvPxS_&8a*eL6fX4w@NhV1;? zu?Obr9?Xl-M*iwD(=)hGljgZuXKxi(sTN3)^KrQLEeQYhk{jA=NE+$p$lX`ADv=r6IBfCG30qOxEI~P}lMB_nPpqHXRNc{YFf!jccz?I4qQ|X?fAxIC{ znI!!jq$dS6BGQcJ2rz1!;5?Vw5*;-k^Vxkx+T_<}DERxgQx3_WR?@xHaq#oQsOI z*qgFZ(mmK01Pf7l(Kl}@V^ANB-@(4Wca+g~dGiJX`uYRc=TP|V&6|YJUnE4;JPnSX zb`0?bu6xl%Uj9Bco~#hzXw&w&jD8gDNR_ha9pGTVey1$jlj<@WoBEL&gp4dR%4heJ zpM#~$LVwCf)|zYw8CjwA)}Pzh!_Cdj<4=jkADye01>8Nfk#1wte{u+){7yvxahJkD z_U+-BAs!W!$3TR>VU;brNM|-yz6`0HaXIT;2h@lF5?%ZD7~llvGnp993e!Y=6lvUP87Q$HK>F~BTS`|FJkZER9Bs~C#pj)X{lUCzGf8Ge zrZVRNIuMEv!~w6)*30$uS&Ay6^+e76H?pcq`+k&wfca#|V+F60I+*uY6(Q0EgN ziNsw>MD+MaUIi(FPdn3=oXKBg$j8zpjh^{v;VCdOSo` z$^5^O1JTF?dn>$0;%C_2DJ>2iD>y5XGK;$NQ1*!NY_p)wJra&AHtYVO=7`S5|E)dq z)>}W@f*!7#?^^!u<9Rhc<1ca;7ue_7%4&&`(aKaZQpEhHayjFcoWB6De_)ZAeKY#R zDTUKJoS3Hj`ZzfP0%HZaY@ZE>I48n@FZcD?K;-}RY;e-V**+SkV9QVZIyG`1xIaRU z2D37|Zz`1gsx3KTdi$9zIV2(4jZi(43=`c%{84}(S@S=d_%NEt41ONF%}Lo8rqiATOkMJ2rN(z56>o8TuK%l?lQFpQNqk?#=) z{=+yjACfHMi?Fwt|o$o^)Vv5QjTTzm@JSmBl!M%{EDPx<)D%C?(mRKk~Nx3Bcaz*^gRx$fOu z=l`bY|IOaw3K~jAQluZJhE9DkUzz%kR8`jhM29(9AU^PQD9lLj{wIM{P7OZpS&)!}%)BnJ?FC!Pi!J+hF9@ASAIov>oj*a^n%^kV!6-lU zpoU{Q-MM!kN8$FUf!bf_6Df^?cx-#Q@NoziZQ8#Zuhe?d6Q53^=iho2&&A5m9e1!5 zrE-WqN8yifWO;l%ZK?+0qTosch#|LkKj1Zv>iPy*eV9<4I@Y5xv~V{;p@ERZ_GP$> zw})&qD+-RJU4mCu1<&mt{=%VdB}$TzR=QSGx6SW=cW|HGcxc7A-2w%Jh@@`~T)XaV zMrR$NU#6g?{;wqOEMN-TrcZ5}bDVtdPaw&gTG?0LcX*nH98q4sU^u|n^7lMaXJfOJ>tW5Jp z;a&$v5^}Q8ccEA@0mi`wB zgppX6Qw#O5F*E@CTBk9x~iJn5blh zk-oTxfr1Fyi!6s^A@4?tK17Rr|NA?tH-dhDOe7-WER6B+rdQ&BOT5PyjX)E(BvciK z#&EGd+cWmLgXJ^9AuALZFUP$vQE$#K>!CqL0!%CW-wiREI}ZLaZE>+T{|88N{xz0f zOA*der}6{<#!;B}1*2r@V|CM$q6BAUo3KMatVnipR9M%ip%7v;tOxzD6!Lq;w{jev znwDNxqp6)#4*$gy5Z=Nen}{R-;*9(QYRurb&`EXGtqXLfMOJvmF4D6FHUR_!oV9Ft z4_J)YuKgU9FoWA3rEt4d0pB)1Xx8g~TA={f=))Ij=|pUPN-MHI&+FFx*#KaMhGl@o ziN12|igmW0nP8Vv`|H2`i3uZaje*b@<%w=b_1{;Vfr^bAAA zSy&-_rqF#0u7Ca;_P))3km514a{=1wWcY<&SSRg79?H)VzH?;>Wrrjk+rWO-8fCifAuYy&3*N+ILa- zz->a&ep|+PJ}`3etaDnID!;tg`x>Eosx1&I+Tuz%MIx z=7NV#PXWGrPFV#;xop!dbQ59A9J%^w2&T~fP|?YFX&Wf|h0lL6O*0r4(+E-US5P_5 z)C+|c{qj3TY>M41%uIiOo2i{#EjG~}23KxNuA0Xb1J!-z6N4I`C(oAb2};|GZ&)T? z?H>eN0r!*0h44WLO$t#xyFOZpvEGhnJDkS}{rP|uWm?RZnQ^o87*)O7M47Pa+MaZI zR87h6;77T&_3s@v+cz?=@p%Jg5S&yEwE@D*N&2ww9l);Iqiz&QAbFYW9TJFZxgL#}Mu zCR&C=OG&ikxJ+^n4oox|Wi2c#wjMz*W~PIqhiz7`);@fV%>dG4Fc)oVC2wNHdVcX6 zWnu->e`8i?X~AZPY}jK(>XI4`sFE9c0Y1L9Q*izSwSAJWrpAMr8jBEGbquCXu&w#6 zEL;#|DM;4i=se$*Se5C>##56#-aQMP#{ycr#dZiXFS0!x9ARh$sP8zv?jB88u*5+? zTw+G`gSS7(k92bF`rR(C*(ko)0ya{Hg7!NW&c46x`J{)eXOx zHQ&bT-Xm1tLeO*Tj{LLOe}c~TL$%w8zrUbSbP6nj3mJ|PAB!rPc`-KVz^yH*Gf;oU zoL5zsCw_;al=+OS=RL>nwV}y}f|6Ka^t>cE*t9yIwY3qeCVFeH%a$wE(@Fnc$~{5m z*ZpK*Iew1vzGXXv;#$DSKNINQmH3v5-`^~|^MRdrxd$#Pn5^PDH%H;F{FO`yj0~~@ z>XUbf0GgsQ{bqElgDv4(^TP3eEnm?pP4Jjg8FY4-+7z)($!qv@4M zrqP-34rog^-xM;#NuxywH9=UyM!_d@6^K>xa^9RAi881NeCW0k>aE|!lYE_aZ8^?9 zY724JXcX44uMZC|Pdl%2#LQZgQsEe9%Lg@l^b=i0GY#%Z*}l<$IlHKWk$@0RiYb_^rmj-< zLR8U);ku^8w!@S;j4OK^X8mt2UAhO!Fiu-7{ZIazaSoed-=I|xnyTKb%Y$cWgRrR5 zg2o0i4df5cA=F*}utQrKe&hggjK42LhyNqmaU2{I5MOxMQ9AQ^Ny||8X^QJst2}Du z%0vOLfYR(QN%k|(t^-SWYRr{!zR8q5*rBrT<{=f}3O#)O4@Asho}7x@wiiT!IcvJ? zO^qLfI9n%X$tf8Xd%eQ?_fh+)EJ(`AD2ddL-yGF`nPc^9_i9sFN}1zL{6<8*kYFB! ze2_II!=feA(b;h5{!}Md!C!@zq7g&4bxWt;qGUxWz{PZ7@J4^;)Z30KRF@hJk1Z#h zvJiQ}?yQ&Sl>bk#GfZ%ECK&LN8l4_BRYSB|tcbTgId^@hn!?6c<&R3ki&x{8vsM-ofrzuEk4`;(2K#LAQGUX> zfo^Z3fm;=>wS;c?P5ioB{K?~sY7ys=dLJze`#MnrZ-q}~hi@|IgQ5dRO%Ke7I!1{F zIN@cv4{}{z_t!j3Z@ssM$q{%9yh3bC#IyX<**#y-3VyB8oYnP>Ei5{1p5uSpqRIS% z=A)=7kbZf&9@8fLIKC1t^6+p*8-VYBav&mktJFfq}wVt3|q&3WcgWvn%G`Cr|tQ_wu8qI!*0DTLECm@ zjDuK-?tFOy${}LJ@6W%R`#;~qg>3<*2*=)iQ;td_A;}OBJ^-*t@5v+?^_7JM@&01v zIW!c`VZHkgYn-O=XAkKV!aD;R70XMCsK9`UNmJxEfp1rxJZO`3_hF{S#DCY&F=9a_ zhsjs$UAlNmxjtE**7~nPlN{@ODX8V-1+JF3aA5o+Yfxy zo82+}j6aWYtsuUC|Wh*EVl|m>-d`D5=K*fI~I4?jf2}T3qr5ID#-bT0I ztVMuQrl%3Nth5O~z=GzteYXpDT(t3@Kb)rat%wTim2dpFxT>CPahPM8eg4jC1fz~W zte&L=>y6>kyD*vR!hjzK2#a*aP9UzY5o#btg`ClXw_sHLUOZb-_ZtKdL0>mlSj@;fz+jo3M z*9fOI4Jc^H&kUzR-MI!57Cxx={LQ&-`qq&{4a_KIS=pfh4>|R^W$maQ7xo(|t1N5# z0(DeiRq^0=45oU5@T8|3lt`^VUvm;EIG|4NoBRVqpLqZ(4;nu0qt{_~Wb_bkJz=TS&X@q%=rIC^&soL$J;@3ho3jP8B2x=F^-P zJl>NOm!^m4&4Z_Og(|ym?3m62P>&5xjM7({t&?gZJI}~gno>GL?8q+_B0Q50o<41R zwMTg!BpMwH`I20+J-RcAN}Tg?@~98gYM)wo6r5KH=Ff!AELw>E0wtT)*O@x4GEKUG zX|@DX(6BKMIoH$)_W^ z^vDS10+%FZoP6MTC4n@c=@6MYufyXu(;y35M&@OBOby%k8Eg17GdDQ!WA zMB$U@*iSv5j69sZ#_?OXloC+o8BQ#GipjjG=ntWmQQjgT*>F0^e~}JWgaWfOywQ}J zI=0@52Kd^T|Lu)@=kH*AaAv5a;e;8W<=yk$B{EmMTb9TFWUb~4i!%z&=~BYj>OPmc zmuf@bUqOJtWtHbG;Z{SLnbKbWc^^sqj~F*?>UGKeFN#sL2LtUK#*6$GzVt6Ej;qrD zYVhe}CMcZKa2eZgZdS*h?FH+!PBVGo10NC~C$3}#wJj1xf40@K$l4Z$w#N8n%Uk(^b9cAI9JHFfQZ7VWaleBrf!Bl&%YYI0{a)v3M5=NNZ>o;G08ekt2K`vr(hpn$f0c5-7T z_do|FhEKbs3Mi2h3`TpfWCHy9i?p8o*w(vo~xkY=)ZpTki z-_Y{%;((lCrcS=Cp8A-YRK^v$mg~ivP=#DDp|L8q&C=j&fzG^ihfX`Gxmx)6Bs1~$ zu=$CvuFs$DX6vY3{iK~rO=1G0(ZL7%x#aj_{NEjK?BGK)THhu)g}go(N{_k~K<*s# z5_P0v;g)vp+rsNClGi#i>HvxQcR&_0aCNM3i;MJgag#443P&Yl(uy(9U!^{-hxcd8 zn}=U|l>%p~Q{1+yb?shcVHfE&d4fpJPb?hLFjm zm36gyo(T<9C%OR~ADp(hdtaup&IRYgm3Gan@#@l(0iOv(_^|}^=M%4!*@Rt>3hHMZ zFP1I;PeTGF$2%%k)if$6UgzyG@gOz<|7=(xm#&g+Q9C-DK-;BSsbV%C5s{>-()=zA z({Xd&{NKL>`p#_Za&&xPs}QgBIQFe~M8ZkEe^~aukEdY&WVVE?Vg2Bmin=Q3sKB2@ z6IL02PjDqD2dx5fth$VMUJ@lIHWXs`Ikb>4n|o5`-M(=y9?h=dBX&!(vOFG?l%0XE z(ODat0v35H`Jg75(E{|b!eq7EW;1pf(ypbUA>pWXeZF8+4}VdKV{?g3TdB=fbj1kl zY?n>dXa9?3Z@?GSQbaSyr*!nt?khcqkR1d{c8eTVCHKu%BYF{J>82x*N@FC=4?=2O1snD~@4GkcI`4)lDtXRi zX$-hqyVWp-@NpRnCfmeK#uXMz_(Lc1WjI~U`&(OCZmubln^2TjyePQrJd_s+gTd$G zAePy%)8+#wjv)_f6Gv`TqXoh>e>-Y;)5Nj-zYe?yq{RjqOdtk?m1qvwFm2CWE!>L#qng`)`t zS{04;>DFi&pm5OoJ+E4=dG`2EmYdxyCGe-^lCowx2L%cHEwgSs+rCCHMJ4UCi*3YO zD?3ZTM`kKtJ*T5%ey>sP58Kb)UTvF6Z3LDyGV^|Eg0d9Rya@(PLTA-Z$_NfB1LU~V z(%EhD>5Nxx_*s=e3Ow}p4Y51GzIKlF+Xz@`E~h&zW%TwAgtPtbuM%Gu^mjTw+m8*P zPLObSY)Oi;zG~!(x*XDm4xLU@&v#vk4v^*hfG3F|%mrcTN)SFG2_^{tZjM2x*e0s- zVwgC-bhH#)`K@EaB<3ASzVQgXK>YW=hkVJrAgu^r3M7ZIcnBX>;}(z7j3_q_n zlY?3J4b8BqMh{<&dt7F9t5l)iSIzLpW#v$eJKIBF5V&fCH*vR23bO4+K6p3GRe018 z?w&B&%O0MvGN&938D+Q&048rehQ=%Nrac0=ljFEZi5IB9pen0TKt{`)U2D`afGIma z+udY*)VVA^=yfY=4TtZ~0!?Jxhjt%BkmEs%Sl{CHKWm$!B+q{xUz!SO8+DJ*N=_=y zxOC1m7KAP;MjZ{+`Tbjhw54;`cua#ThsE4`v=M!kp8&&zC2{%C1b0! zXfSpazeA^+K1ZyzxqLcDCa15;42jw$=N)~wkS&76)`DV{N2|1y3Lljxx<8kN|LN3g zz4fuRS@YNw^=A|&1IzU4g@PI^1*4fZbnA??W2Q!%_N?}Wmy`v864(|WE=o!YJXS4?vqaLrM)wpzu<#dwlYqpd0n z$DA7wvx-dHxE$liy+M*oKrW`PkAL5!8%}n!KARulh2ehmq(j&o+{g(F(zDJ zMZPnP`-P28`?Fo_>$oWA(C)(*!|=F18zL_jsow_OJ13H$ff}NP1XZroUYP`Mu9}@S zWy9w2bl>vu_^ZB(l^2UmzTN#&>3Jvz)$?L5Gb{~4e5zTj2TzDor8a!6)Q40GRx41i z39)z6?%`*}vfM-(^pZKf#M=1Rd7Aokz1LRhlX))8h>tt=GTQ&L$cV|uSc-gsMd4JXpnwa`>S4D zn7dI7Clq@8(D&clYfM7-_Ph%-uA7!!j&2*}|F{|6YINtY;XbZ#dCgt25r0dhQb(zi z$O2Lo>idZ=yU-T8QN$Rl>7y#4lT!m9y&Od-=wh4QgSc{Ye9S=qLOg2z{3+}Jx>JU> z?m98Pz^J5z6LDRJ0tgG+5nHmEyf`~S91grR>Aly!6jZHIs!09vM9!B-Fg~tqcEwFJ zV`+f@Vj)P&LQ@ArUE&`G&iD`5J2Lu-npXMAC{1y#`0r}#C333_xe%iEbzj0Pjos~Q zvsLw@1ReZpVNpZO^Vl44YCm?#Pn2zz0?M<9!uRezPSEciZNS807~4*|RHw5BOK3W@ zA`4rI2vhHqkxmOckLc7q!W(;E*V3j{vhGfiyzE5c6aA@z7jbq=0VUKMFpi%uCA?6z z3#yjIvTSwZ0bQo$`_6&xRL(Cqv%!@i%%>?H!Ac7%-}^X=UE6O!oj=DZZ`_F|)}GM8 zdtdI<%&xM_m?`=u+thai;|>ES=5u5Fkb6;h8w<|o#+4z7Eh7xv=ANzL>mlOxJ6SDk zl4+{)C1{Kb-+RNhs{N_J-U3bqn>DVFFB)cvzvWXa6v1--b_nTz87ccxh?(hsWq&HT zf9u}s%V78L36jSw>95Xv|7X%BY@Eb>5tUu9hHmre_@}amfjpXmMX8ufZlh+T$@aH- zl+A>t?~-lh+@yGwUA_tdmxK=<=t2}j7Jf)s=O56YM^`!*C$j}biiUm|dg17kxv3CX zX%c%|(RxRsS8~>nqI|d#d6%L5%jb-$-%l+);U|ZpCD>W0-aBATX#>a#N{W2y73R=T$+**p=}_`EZV9nVO5Ez8D3|fHo?9f zqdlw7?JdJM}Kf~95uGm-KcQ+R5rY?>hu7Z_eHXjntqR)voGXvi{N#jq*T&&Wjp zR7{V=FV|$z$t^<-HaA*IiXZ^nbSDl=8{5SR3ZxaQaM`KuJ+Hu@4w@%pkkB)35%=erQn}KN$F6%m$Kgum0vWot^ z2~hbsP#F_fK^QTfMB>PqVF-wLGO0-4$(kkHE7T~3ofKur_|_Auz!c$ zFr`+_v@WGVBMVe-<(3rzNICR6o&j;jxX}42^_bys+6s~MNm6Ke=X+yzsxdl|=yZ${ zN{t&$sm-S}s_uE|MO1ZRNv6<};_B!uH*Y8JSfvWZ_l<$xjYbxa1u~36LFvbcd=%vo zGv>$$!Ny}BURd1Ldke!ro_ymI^w~7Ew>2n&m=M>FMsSAG_>UlIDrUMm`du|$<>8X1 z+3u(>UgE56V{NmRx^<10eG~bLo7Gud6tOovCen+v!Sm2nNeR?e<>--68J0Q5D-k9U zk+}0eJ8DN3$oR{BlzrCY=VP$;0uGHM=a6QultWMP9J>cE$x~@wFH_Y0gTZJdbpwW8 zYQi)kbq`Doy~R?AsH27%(VEi5ONVa+Gz=Sq1Pi+zb6ISj6-c#in3X4~?YGytVccz=fan=;~&%8NV_ zope4c7jf2kG)4i-yEfnY@_HODyGGvMbvmO~$ia5wC)C_LXfgLleIBR0>A`02amf=f zaHl2LwDvWvi_JWr>{x$r=jl~d$#74fE5 zJ>F&0b$yXqX|t^U_oW#GFn&TLdE4dE|Ddsu8XPs(Zj56pvpI`Zbdyx0?y1$+TD9pN z-)Tx@t7=ff7Wz5MuEwT-IGW>D;~ihg^{R(e$Db>Ilohu|!}AuAy}HaYN=%+(1QXeR ztMRM~)?SD4rEZp%0pC}E6NtD-`_KIX)V*HAGy27-km??t=QPDnZ? zrH`dMVF(AS@c4h#wj;|cHt>q)`N8PMSPGKzlTA-8kY6(7YL!v8at{D86iCn4!>1)! zbJi?L8>sV^eM2&${mn)@DycAscd4VJMnYtO54f062vb^XH4>o?_;qnQcXDtn7USEl zE1{{VSiNY9qMDv@lXw!g$Wzoz*|DkW|J<7L(B+od!YrB|?J#vY+VSMgHs0mp>cN$* zT$ZJd;FQtAD3#`=fWZ$I9>XBQi<61>IvxYBOilA5+?TBQ5q_s<8Q zn@3JjV6#p*7z;=)yuqTANPnjbXgnxS5>YIVe5w~xQo=JO6y_o(ooAa!R8A$NzF^jB zDpp?jNliWO8aGvtoy`VcYd1GxC9IaiqmCi))%~0kq<1wj8PTk+S-}+tHSGo8>Bn3Vbi4^%JEBG$Jyzr8xri%bp-uYFHf4C#dqda6wq484#K zve}(8UnFMjs2-W)QP|cmk7@wUH7j^VkBW9c84nK4)Xkee2~DYQlX`6|`FsQDzI*?j zPzf6Txxt%Ug}ZH@5Ut~cc*Uab(jK5q-xFSIdDn-v zh6d|Z(ZZJ7xUq&olCuQ0VMs{-BzfQ_~OX| zLT6FcC6D#{T|h`f>4g+&SSU5S0$^Z>DGf#17B~{m>t{-9UY=GafzcB@GRc|lxX1Ck zt9*7F`DF-)v206XTSbj$MZI@&_)4*yESgCZuC@l3vSgF4Y*GJ@39UlDUoZ#J%EtPV zp(CS1D`B9JNrgjZKBYA9L47C(sBd}V4Jz@N!O%V*iO1=)XAxJIZHiOO5{KG=es`LD z?``-~Kw9t26S3>%j?^sh?83>32daz%@WkX%(x`0bEW2~o;$_!XixG8n&PdPC4+1g> z!t~F;Hf$^bx|iYoW&9-}m_u9t4o;nAJS^bt2@WM^D;|l!x-jTO!?ZS$Sgb9^lK>u> z(OJnzO6|Rqz_;s-Uv!&DUx6X9Gbs+#mtAt6=Ym86A2G9nN^CHHwE}XDR2w_OdS*d9 zST+AGRZgF6l`qnP_ zZ-dAt5P7O*vMTvB)1kMZLoU_la1`kS4`BoOWMlX$vjPO0b? zGVYdsn!2-J_|}|$c0#7Apz4RelTI`Tn*X>g25Kw8oro**Z7SRuv>1^2rmsp$WWfhi z?wGWV#)|6}vw)X(?zAJlMKyUc!z&TiUYPfD%Yp{?AHBVHv)0-i_sNS_nold3mH*ru zck${jw16$wst30A8Mr~|1a9QeUj^NN^u*gi371`_R65TL8nz~|YN{+Y>1&T5DeTHH zdZL6i=n|iIpkR&d)@s34q99#dQ}>t3iLWENV$_+7v=99B$rFl;Uz&6rSCX;OS#5~; z?tyh*#?raTAl-;o{t%>$;OOY;{AJ@@-yUIrlt0Xp$YIT7LB{1w3h6dALngcDJwcVf zl&l3&x@$EK7h88R^`Wx;Zff{%2}>rG5=-u)tztm2vvS;mBnD73IG}z_QTbs=7FxInLMu5 zzGwQgk@rVwVlm>He28zV?1M5H0U_5r5{Xg-)Ku~XTs9me96_h@Ln`m)l&Loneqiep zV>4x=CFWTR{V<^#GF#gSFVtAfw-UCvU(;qa&yvF;(3i!%7m_(7)k-@xV0Ew%0j=K- zB=i?Nq0L4Pm!yj&O(uBnQ|CDWs}tP%M{Y8{6^asDe|di{Ib~4&xvdb~LEq=&MCkb7 z8GVT`@IqJ}r{PUVtCC=p;{9wPqrWV%`sW6V^r;TS=2d6<9ISius5~AQ+192(Lm1~F z`QDX!cP=sgKwR2FXo7+qsaj!S|Drt-KcO#`O%HFHaqO$K1r721xtY<==*TcRHST3m zdDXfa3kH*OvCZcekzBG*46vTOk8%Ug8*o0JIyWTj9BQT!*g#w+jg!vgB^M`w2mU4j zo%kk=Y)J$56iX}X;_iLmaWC7aEqRRwlFKAr<6V0PHL}5zCo*f?Zb&GLV|^Z8rAL`j zS^I@**15GHaB^!>A!52BLKn+%*(~SeFAxv|IzqWhty1NY7*c{t z!pG@)qd%yfmuHz_86a__xVzU4xvV^>B~}PRY*Pe^fBa}Cxr-;r6{p~T`b@~9szMU+ zYcEQ^N6PZjhnIG}EGsPnEzkZO;xs7d<%%fkpzdV{IsZ_p0CmZCRL6w(A6wSWn^<=s zfEIzvxURRnSP^%>j#_(7+idBP7ODF7dm+6iNLkz8FO)ib!m1?;aypcj0SbLM5l=&5 z%Ck5Cv;v^rQtf)GR;Gg2Jq?r+AzuqYfj|JpdG)UkZ@EnHyQoCOT_P~AivZhR$g*MN}}q9o;Y-b9%eXxy3kEk zc#(4AcEMvde*y)6IkxU#YxqAtC(4srY0bm2>Wm=Y>$N$m=pfMGrKp;123=Ecc=;eN zRSI_IAzv<_?t&P^C2XxO$Y->j`?BXmQJee7i}#Nk{+*i33_%fiLoDKscz8&N>BPhG z-q7JbCRxGx*TdE}v?%WkVwF`LMqbMN9I=+r7>+ow$>X>|Pyjm#Z(lx8P1no=c zLnUKEj%bQy7MnMuGa4I;+r#<1-AG3$?o4CDwNHFPEcWY0>D?PlJm6)b8h37qpV#o< z91|i}(dh^BFFR!#6myUrpAT*<@v&Q>0wQbI!DPFYH6`ts$Gb_lCHeG7JJtTIe;Cn~ z9-(Oyh+v|egS4PPD(!tKD2th6kBXs36yg5GED4V;#FQl3HU$en&xtFJe&;gphy^S2 zsjC`&tx)2uWQIyav7b{BszhO!hJ&xYNQKlq7)LY9!x7VthBw0M*o=D@9+MeoiEbj! z%R)d{$j0pajR{yq)Ca?IO$6-zqIRZbPVH&}V5WmFinPVL9Nnyue=}K0+=1VG7Q-71 zUGQ7&$clw1O03edwg_h?gTNENRLaNV`@*|Ei=I&h#UO+Wta?N02i4@L$7tHc2bC*g zet6d|Dr=h}?VrI>9hXb{$!7fvB(GUp<`t%;B9*AAsf(lHcfhHWXk#3!kNPKFCXb+I z*?=RKlRJ{T#J%*>LP|nTrW=-dw4jAv0d_SzTf!`r$D;IRCM}enmlu^os`o@>1xPx# zS(tQYcdm5f{+eX0*cIVB`QvG_Ynm!P{$_YV&#+n!E&#D=`q(ESUq--Q-C%jIiScJ~ zG+vxkH0H!vheoUAaBZLKU74_hQCkM;wIQ;En_H#IVI6^}m-eRFuFa8{wFWQ#p8!Mf zRsZ=X4qQr+&{0YKN%0(<9~en2RoKA&&|`6JZSrXfEy^SF@VN;CLGzZL6gv)iMUngq z4?^sPGi#@=&|$;zshQ0A{g~reRa!8-QTWId(-u1YU{0kz5wsnPNztoLGc1jsBU88# z%o+0iJVDu$@aD{lY;(EJ9e-_psW$)1Y9e8ICWCLt&UaRxulDjEG4h7{rViaKPfAs# zHi|kwI3L-iZ8ap3Wd1BiX9*bk6BE1OC`sgJ_93^7DJ4ks9ZU7m=G;M*8dc>-H7}*G z-)zLJ3i(ySy7~p!x-L+hkl2X(K@0Ak^*>) z&nP~~pq)w)uNu2$e&&roM`zeSF0A8V3}?k^>rnwwTbEOEZ0u%V3o_WZK%}X3W(|d~ zMLX{?>1F*-syU;O1sGOd)PP?!RMnw_8*$@#!x`g?8b*TBW5p?tt&LZ3%!3|wedIFk zJ!z2}yN9LmHtVfq%fh}2O?iIa%34X^e-#Z?@JWSy9o8MA`RJVxneqk5_eQq9#kBNS z@k~Y2z)B+a2P#?wIp`RFYeRwLa>_F^K(|%pg@2K;ITDB7P?m3OS=x#n*4Hds^<2!6 z;J_BdXeV$FAqcZf!kBS;2}-$9^Cf$vSdNiUKgT)j#j{64I)Ui?OUljZ zEccGAC7e4cX>@K|gk)<{6(8WDN4&n)I3`{GaJKEgs;?31*eW%ga>7+BVAotVHf8c| z*4^~A&4`_maGGbaOV{>QI9hJuGuX!h?)YPnNZ#5J#EwLB>dyaxXH>tgINv&1{7Tul z`{U}4yG4hEN(XETi25jRD%W_zy4PMAoV^5K~b_EN-q-mll~o8X=l@ak8Eu|%T6 z$}ZjvA=r^E^3LCZYIE0tj;<|ddor4!>z;LJ%=JK1ZO3@w3yWuQyj1ybOXW#Z1NQT% z`Owkh`vR7mRrlQ^Tk-8B%hHwVx8yeToE9CXEiMO+@bWBH0em+eKBNFwzjaW8_<)N# zzV;SAg*_`mNtK5Jx>GHx}G-LoM$h(qA2ATRYVW0 zMaL8WJ+nieInEQ4?Q+ZAVS>O8nsU+z62x&!766kF_-I!`RG$~)BOCMD$)aKU5UWZm z@oEh;q41`X>s~wr0AL6aP^M@&3Y_28gGPNGr*dN(x*z&Hb~tgND2XZr}3ez2~{9NtJuPFDlqd%Fb&h`LlFc!{psty_T)AB~sWN`8@63 zm`^VF)5qwO&OMX74836X&%gAswhTI?PHY>roVECLaL;;*QJ{n<4}ax8C+W?hn---N z?aYK8m;2#aP3ujk)&z}@TjbrN{bN|f7iFO9dMx5I!R2m9A*K@$is>eQlh+z{HO@`Q zGd%+oy9m;`AGTQQk%$uXq0-@1x7;i4Jj!krpFn9*fc11^E7dr@L*__r`jfW<)p8e1 zcTDb^bijWL@sD0;tpB^b%-Ih$aqTBnzI3boz$ZW|5E2tk^?`5?1QZ>+EC2dZUQgCPjO-apaK=50P-hS*{HN1$?WafIv-Jh~*sMB8jlU zX7|Xd&-CDO-Y8v(6$DUg)Uia{{MA|7%5Lp&NY~jq&)A^73PjwcW~1BQ@x&j@q3k_# zo#!cZ_z7UwUoy#+iln8^_#!Eb56PP|`3UCTJRYczs>^II@tS4p_4zxY6NZ{3Qe=7^ zA=*zbaB~?Tz9(g+&ZO`Os3B&Vna&Ifz@o}-&_T<>XsTeH?vt6dMsESvun|fW3|#ftoXzhz*_dT zTP}DZxk!uXku4~uv@1~dd6wmt^d8^P?zui$>g>`D+nAf(aVpJ;%ckIq_N1i5oN?^S z!s$E*<^oG8^q5g})_I%YvF6BS_$DP{?jV2gF2AS&yS`R@VNiU2)bz-;p)4uP>)B!b z0a%7C^vr>wkI{AP(ssIBjSK7a3_IZOJ(UX#XBlG*QZDOriT=fZ6NK)tVLd_V>cPoK zJrs*1fZPOn$TNM?pD(Cv#EspXh&4Qg&bP{*j`S*44xji9S4T!vwuINwOVm9LvPvW9 zaRt=%l0+gRhNjCJ2H#(kLNhC&3EUhSE`-z?wa#Kh1#sXOx02OKe5-}W;uO%}()W!b z)Jmae_^Gqxv`Mh|mvp+a2;A{*&)C>B4tO*eZTkaeXwRXc8ov5Mr5;17zLT~WUN}YH zxR0AGWF9Ul$hrWzf{?$f_hxaVQtk6ebM8;m z=kMbCqy`UmOGVZEDECS`Jq$;Z>J)ScaV?W z5G|MhH?SrDezwol4DzZQFDl7mXS~pkJ_!`&F%yL&&Tqtsc*MjASV-aSoGk0-Xi3Kco zX7mQ4ahofZ-|bVH6EPRbLdSE>rukdK6I+1R5!_>A>cZ3^zcLX_43WH>tjFiCvHl$O zSXwdO%jztAjjKOV#`Kslf3bBQ+f3WQb}cjDr#tD2^46Ih)wBJKsbfFImDEcgkGCP9SEW5&o#ECQ_n6+?>iS zO7iJN=v22jtc{dbP@QSG-`lHyWwAw4uz#njBgX008;d;7JUgqFe8f(^(1uys-B?ge z@hQa1-=S+tHzWc?O3u?jO6(d*8{@`|Echst&B2mgy59ySEmoeTSr9mm8!lk;jlM3qON>a_ zU#WXxGzR_bT>NxIYj)*U3oDPayBD>HGJg%}*$K~TFX?Ic9aLl)+fHJ@?G4WtZ{5w^ ziX7%)#tI$$eW6@~J3|Bdp7BsGf!f1;qwv+=5!OUGa-Hfe^y0P2;V1qUd3q0iMwLp&Ix72ejSjxv75bliN2m zZ~>v0_ou~fkaw9&*S_7wj|8HKG6-VM#^w+++4%p*(pk7Q{r!JmMM@fp(E`#rx{(;& zEgh25-5mqQ5T(021V)#1cXxwyNawxJ?|c6TyRMz{dY^Ni@z}1khjD39%8lJ0DJeIm zDl)0sf^iH<&ncnnA0*7}SRVwJ3(yZ>-(VxwDeYqxcv=M}+tn1C&EZs*`O+a&HkDau zzBd(;rgJP_vLrL$G`7dQQ+uKu*PNV9O1KjqToodsxoF1_xaSGv0aXqky>1-vBBHfg zn7$$!X=o8d;>0DTdr9ZVKyZG#vUAy#lNj_*g~u`*(3cJLw?~WVV6)3t#xZpw8LZQD z=)joQZLnTZX?5WnIpqQW(#AfwrAiCZ2;Aa^>~Apel7uI33U8sL*k6k#uNf|-63Obd zFwSB&@jClCeafWn-|ufx;aY7a1Q$Y08Kj5EEx2@$Y4jfif2et)2%5$y5;B05)?Req z|3SDHJ>vZ2e&-UvtBq;k&FE}}GL|1e-IYE#Vx`YepFie%y6bgP%|1AqD2&vv9rnYUfj5~2Hlt*XRqPg#%2;?qyvB%%|OjLjNr<*<3!{;=|2l1(0cS!CxTmE{fv4i}={1p(a@lhZO3_q>--#9+IF%mp>#0pzJsi?Xb zLGx7NYVt|VQ1d1Yuf#9BDbQxQo&Az{%w#9@)J_*z42l|^wM|FKhwB$QUuyEL zt?VzK8n)goHKR?AYiv&$+GtMxhN?yCnfJs%R{~RHh|(q)y24KH$z5;a2dy6QpgnAw zyJ;Co>|qZjSDK6kZN!wZfP*L;(|xx6QHB=q^W4c(ir{Ce@50SJ*hIJP*_CpkuCPyS zSJ~j2p!d28R9aut7}twBciZLgSTD)?Nv>0as55{qjbPosWfBd_4 z<<8D#6sM6CN`<|T*xuW+-I{M?xwf$xS4Z6?AAB4Y4jx&GH7vaBQ%IN93o z_)K_~0GEKN5ybjn$qK5e-8YX+I<2j>$tS37<0aNUif87y8ldiIrvNtSrZRem;%T?-$MWO6#{Jr|nqjM$SGCK#Cu@e+^ zi239XIh1l~T#=q=!B&eSjbUOPF4sem$q56vIg<;QU)*-|MFe1Z`6i}xHYkM4^gq4i zG(I?4&?MgMa-K&NXiK_spK6eQ%-N)+{^qhNe%908!%vlL&sY_fVrBA!J1YcPGtMUE zCNa68T2X4)8qsG~%@*G$PcqsBk6uA==@9$z(>HK3pX>5RET-K2F}1?@{tYt`!ak*T z0u3JXQro&G)})udLHgF=u7mu|lP^17C%9%d)?apn1I(>#_cv#{*On_817R)Q3m=b_ zy(l1!NcCjGkz6%Y<%@+wdN!U4_he?0Z1bP@cj;I9{=lFncnrAV^TXIIgyCQUX9dak zyvE0N`*lm`Id!Du{qB1k1&T|(U7Ds%^u@8g&!u|BHcB9E?HKRanM?^hozg7!)8RlX zV5sq7NFL+%#m2uhF}9VU_sTdQ4?JfW#FgG>R47TcK^P3{DYAmvGYPm{g;fNl)~i~> zQ)%tv2Ohh5xF5(xLi+F~|NQ%-+~@z9FzbE8JCt_wqA>?>;`5Ypb?lGkVa`XvvtI%h zfkE!%jau9O!X&%92{)Ta)j(6x1J7IdBcjk>%%-0EA2YPyTw zkUw-J|J@exSPYBEVh(bguvzpkYB}yc_7ob$kAdIy?sRHnIj~)#ao{++vg{$ZM=rKc zZwws(>hgd^w1sA@X1YBa0h|8|=eUin4m; za1=w1By-LS=J7l|`M0k|Ppllu;Z3JkQz3E-rRP`2B_oCd~yPdnk>8HBX?<^|x&z1rte)T#s`!FmzmM^@jz$>9}|vX+2>p_n+AR6jNM zm9db7eOGO$2hf^2=)0{1=hyDWcbcKY+)l%2-PBi-5HxK`r_|!k%QyQRF(dgcTQ4Ck z=7bZ|tA&uw^CR_n+Ho6oX>us7v-Me^)gsX=>dzhTMo>^nj+!ApF{ib}UjHVHtxYyy z=u@sbL`6^f?d8yRoE~xH@z&7*V{32=R#k2-yl~|Eedf}@)I%mZgWl2xYtne>NNdRQ zA|m0GztmUF4-bM~6uXZE)@(}JgHF^*siG8a4tMQpjgE}l7#Z zQ|EO3MrBl2CHzt!;~VB-H)rDo2kT!i)fmqP$3>p6Mws>dzvh4wPbLPqn9r*RlqK3e zX`9wwYvEbH$l!HP!Ke(n@1qjkiN4bAu1Nj~s;d5cPKNW|{ew5aez?n%jY*w$ zQoVYx`<#$Cr&nKELFZlXQ-G|~nj>RBuPIFWz1Vi}`b%}-zxqX6U!!~wE+59JyP&#} zdGr=HluAn61vP8k3C#N9T2_W{PMyESXK-lwC#T0w6DFON6U4<=wTlAW zPZ_b|R)+TyaeD|IdZBl*zXki?enTQPEVC%-NCGeDLHHSAr-43M>^9MG!P8q{ry|)l zT76DSi&Da^PW$(NeHWxFssdDWIw#jIeDJcDxjxyZj6EcSOm!uGJl=j|pP7KH%#eM- z^ct1-vvxqot8bX1bDk0R>7qI3ao=}fHWKPMr}62%1?5>zLeuK#$%2?G?wTYAz*)dY1k7;R{m~fKL zWTTBI5ilq))g@mr)s=*ue3&TgN=58-Zl z@LB<1i_MMMFRpbiI~I-i33}@O7IZf<{%mEV-B7d_=chF4qdl4}%M-4rW;4o~Xtl}J zwGzW)%08*X8EWdHGKPNk`A35U>fY;yoN0E(3BJE(>Cvkpg>#HF{yc3(ua%3wPwK%i&3y^2^2l|DVXjozgrk6+jaM>2o% zsFaTk!zyZN^nD@fN+Frl40nX@IcV#ePiYXobfLgih13; zor@!e-adc9S<7!6X%t<2;=C;ZjbnZ`@m7SuiRt<5LY_v4@f4VY>U<*g_=l?4v(J}FGj%uYid>yjLK?vZU-5({aU-1~a0oT+2u5KmKHfwgV9PihNR~T9XZ&<0x z!9e2xX&8F=nWeS|#--#&#>dUU-TuNw*VlKa5iT6hOgrqAQG#mGC>({lQuWV{o){AT zbOsrh3b1Vx2@Y2CG>5mH`5{jCdXw|L3{uffRnlOB#L-!&U2t0qS;RJhv2whi`&w!Y z8Lv^MaGX6KHm_0Wf$i?@x3>Skl!9)!<=B(B3C;w5iCPExRy~Uc7`SY{CcC$E z1W-Uq9Sq~1W^lp#n3ws|y;I)^czATYxk;BRc6i(=5FPjGKTIey*hs}DRS+*bUrg$- zNlrq*#htiqu?2kkJ*o&L@{VC_%6N)_d-xF6x;Br}W(y(DiAoIp#R$VkM>f)`_hj!$ zUx;hlEQ~9C*Ue%8XJY1)A@Cvx+9ynA>8^~jzgFK>rZMz0zI0wT7U8?iA})B@up}b- z=WCGwQFL}aE}Rp;r_h?WQs!C_xa%9nKG?Xsb6gUbm1^5uD)|nbrmpN>dc%tPrs#OOAGRb)1n!6WeVEq3>LnD-Cp2J4iQuOKmDwwuet@suz{g4CKjbk$U7N*=Q$)NhE1qug$$l;mEpDE;9` zV)|gXT5*Z*a~~#29vQadcLJW0f;Q79>U{l@Tj-s#>S}!DlBD8zBt6#_Px#!?eQsD| z@y32$B-8nC9#GpyyikiK&yy*3^h7j>7~99Kq_ zzc?+Daq%XJ?OL1c_bMrn8%nhPR{y4Ai;OGz>FmAtGQi_fN3#2j04S4TP;7jf@EJ=IKQs#4hD#EyquV1W*wDX0r_}Li z>;XsqM^oyq&Y>F42jJf%?|%lHe{~f>xWKyt{D$6aHj)LyjS{QPA`@nmScVGO&d!}M zsX{FvR~nV&rtwdb%d#r4Ro<=Bj#FQq>X+M`yrOrLrjA{?71Q#n(*@-v3%J8%UPWaY zi7A-=@*{c+S;yh259l-mckP_miB2pb!?e^OwBe;cELMTGrB~GDN@?&I6Rb|T+`aS0j>_GdsdUeH>w@URarB3jH(acd~l|;~NHGVz< z^{-UsyiSw*c4Y_P<--v-$AqVq595v@u#YXrk*&+r&dawCrD+|~0(7aIYpNGi_}No zTvCDuEZMK#4&~K~V2O8sQZs*NxU>ImybcnXQw-33DW)`6UupJH?&e)SnF&v`7@zw_ z#dS$Ws*oNr;6tLSnH9E{sb&5YZyCJ?&CCf;od`^t`@Fev!e3}^AL);kR6Czre-A~$ z^R_Nugwxm=Hu?Qc~6gJhlU?IXsP}(zn}_+Te}&tCN>17emFo=f)qEImq91?zU-swYX^^N z6iVi@U-2SrkHpN&E)_x175)xel0(kG!};f(_6oUo+G-tio}0s z7qaO46gu5Qz}0}CTg-zLRyRps5-a`HEZDa%@=c6*y}pCo1!d?uR$s{#zUd!P?RVEz zN8BIx&z>O7iKIe_V%a#P!Z2a{sX4e;|CXuJG$6NH^oN&MUdYY=Mr&Yh?`<$A0kSe7 zH5WMv?HKkZJK`zV@d!#wc$1Rkz+D@IxEZ3orR@GRkE?7U$J}Mt?Vx6#NBsVS+zMp)DkH1FW~-$9(FCB{H-5L8WXLp zc~ny58tdW#4M33OcEL}7RhZ71!!JP9_Y{$Vs8u&Mwco!niFA8ty z(jPJwl>e%RZ1(Ui+g9@hWs>91=1$W9b5nK=R#~<>aAIUqQW>{MT~AORJMLrD^F^lI z57O4QFHRdbzQ-{h{m{m7jS(m=V|qRD-r$4LMjb`Gy{Gj7nyn4q*84Z`Y)4@_ZWOHiKtwb(NCQ&<++gAUkw9!ei~O(CPXZS6*aGi zY}V4a{j_YZECFcO%+p_3YRy@}dnZ)jCLW?45Juy1a6y{2WDE|hDTkN^fJKR<4^-?y zbjcX`W4nHEe(oanx7tYMirqlUj}*8hU^pf?JRmC-9K%w8BDh(ATMZ=t=6PSK5Y_?p zxpQ0sz4Qq&n(FU6BAer2;e8xW6Uf1?_DZ8?)^8)lXO3RX$dB%(MG4)7GoBk`WpRKRh{?J`u)C|u?*d+W=!acaIuw~ zraEGWhuaXpSI4f9GiK%c!OF&BcN`u;A562!2n2c~i6)HT% z(FgE3j(Wf&qUoQQ?-u@n{?-zl804Il_=-N+_08j=w`F2QEoW<8V^t?y0IKdJ?5X2H z(qd{WXn$viF_Pg>{fK!X7 zpsrvtRp(M=_mT#x1rF9#{R&G5c(pn!ZLPKgt#{qxQc>6c3P>g5(=^*|f9{VJ*8C^afmLsiZFB7bUl~Uy261Ae1&Hg!MWy-gATP$8=(6 zvl_l#*{yLrj&MsUvMR{Sxd7i5+WIT<@UzIDkcg7QrIg7j_vfK!RxYmi&52oK=H)w_ zy}Lz|%&d=l_faxtK10vca^)BdKCaa#NjsFm2T9apDDB)%lEaxji;c;fAV0F&q=)MNVqs^JfTf7O za*CzP)zBg(46DM^t65EWulAcXaHrM4*{Wx*WHi9wsEOYc+TNjll56XZX}CTKqoFU4 z#i#zJAR&xTK=m$}DZiNG-L>LvK4zcI=ecgrDwGa0F--~FdTC;YS34CLJckop6E5jR zo<*Ze&%E5UrA`*Ca*pO1Ul#VH#Xkg`LQaua!cB6oNWZ~&BCNSW29UL!Gx?Xkcc9OaZedu zCF9Jl*%pa3t%=~+*bS7lLJ_D=`*ICiEiE~;b8>;|!g;=lNSS(iEovhwe{Qnd3s8W! z-IAZ5w4u9rGwUCfeP0@XMtTK8iz<88did4GR5sy$(EnzN?7J$#vQ=+uzEQ}Gl>}P| zhSGuOREtJTfE#mn zX?gMT%8{Dv#b@ZkeSI?U8yB{cwd(n~hr|LpY+7d0R9K09jI)s$hlyGG1dXfp6qUZr4c@&%|Oo$2^u* z-*oJs4_HOluS-@Dm?{jcIPp}XY37hB&r;p&V{tj zHgcFbgUlJ3)7Ro2E2cV_#=}t1y!Mute#WP(WqeZ@|BHP0H~{zG{_f)M zHcT33aYgl@D^0bkH6Beu2~uLA#G?|MA(cn+6JL+hFZ1 z^NCFcRAap2VuWbxM{Y_qsQ~B;!KeC9=*YPGyQE6v3|aizp8nok>#Aq7{$3qp>-VS) z(YM)+A|Ezyd5xu4IwImbv+Atit?r3QpZ|t`)H*3tEs8Ml4IMUyWGk5wRTRa7i}T8U z=>04daG}xF*ta=>G(W;WT?nG#{tV)lPe%goDmsd)LEXXziZ=0f?@fo?hKI|8KNPq$ z{h|n|;_-hsKZ|~o!Po`9VCqJk&@lriHu+=f_zXF9feE14Z0i>wc^_17pYM|^nxQp) z-*&Y0LHeEU`XdjjQo+%VpB6QxLbA$~oGe%R7szjeTx%#8Vh0OmW(Qb0f-9BwK5ugN zpAZ75#U-pb=Z^1udM6q_ZErde4MykiR>W?a^ZELSkZVY8s5Ss=G=o7z7*U`0@D73s zD)b?qzZkU@(VT`Pi4qJoxbmYZ({EQAkxrY_HDJn87mAl7=$!A%zgy;|8=(WT7>Jdx z$)f03HBZ$aaX`Cv1R>n63@kCDZ-IBWau{OYW|rqLkr;Ex!a`7fykzpg`+~M?UUW$n zZd7D<+#}yECvlNHZtZU_8BpwK8iJ&yn`YE26LQ0{Y}LyEr20HSLKarO#NY2RSg3So zfD%Xf&|e%rKDsl|S^k0~Y>xms`@M3)cbVPZ`lE&+E-7V>0%rWs{4sTYSBUvZ0E@BJ z1Qs2u3cs&aAki4(?e#fzfRx^%naTx)j&YA~{{;#& z?n)k3eA0konC7kqT#weE-DOgv;uC@g9fe8UyQMx&Hpr zz;gPMmg%(phR=x!FShBPv??t;aGr&YQq!PPA%X}LTvQ2cXR_+EX=$Qu?I|A)gDP+Em6R_w}t&82Y zy>WF-V90S`S`Oz>+c{Xjq zdr%=^Zpp|oJoZT>pa`&A;rs+oW9v)#4f`due<4x@{$XiWffdMZovWCQa^Pd^bmf>b(fUK;>#6T__mxuP0?SHZ1 zlLYY*H>D@W)a9?s00#M)TA&_8VM*$zyiyv_aKg%R+rygL+id3y{k?+?o4A-J-;_Lb z(8pOuasaMwQhf0~P-Qp^UfQY76)~XU+OgrPXBp16OwZ;18lBi7312fH2z1=ba>R%& zIIKxR0eyC%^OIU%oQ^06_Jq9^o;>5iXeTVVL?;KE;AX*U0Vj@)d3&DeqS`xJ8Sd?T zGnr^^FQ3C;4Lnyvfjwn>s zjOI=2Y9e)S273BNnx0>4TJiHMT9r<<&FJB%9rk;7*-Zt1h>N-|!-)d!(}3>^-{u&{ zy+(_jo!E&>z9dp2{lW8!7&rHgdDvs#Ug$Ju+Hp8ML+aBAd&=kw&&}Qwx&h|^nbN`^ ze!{-{3A$uMxcK^T&6F&Gzmw*}nmrcg z62zxMLP7tCL`y0@A}RZ!_ad4&QBXgKC0D1_!}>P_h%$LjYb7PvvMyU0sK?GyX3S7l z#KlDz`n-2N=gOM-$votgw|)1j7D_G=7Y}X0zyF1s&w0# z&z&boJHIlM4CU#X1xO~>R72)&Y`lz03o^cIi1&)v2jqn2##64>*{Ocn?A}RX0R0z? zM}xo0BR;Y5n@l40xh0d7R)?rd)H6`MoPr&A{G*tvkte6r3Jh8C5xyXF;Z(JfwzZKh zxO#H$-4pSv8zKXkYJIo+!NbjrZ0)Tx&Z8r#rCOB(Dx)U3l@St`^5sL~^ zrsM>EEx_$CCR6t@4tr3|CgY@Yzr34wz?I_}SvPyx0f^YvT0#QvcL>=RA}&M>20=&v zb(dz6&?8E%=pEkJ%uB#31NlBzq^X^21a^nch=e8YTMan~VWG zA`$|x2?UL-)zTvGv}OPOA)s?qa9FtU&l0S>Z9vrcQY88h;AW&_nb>;^37)& z)z8!e31{2 zV*W^SfA|%l8-txMahycxFhBTaC0-&t3V}^5Gwe;p*uu;{c47hg~PgSShq}mRHZ~Z*~N8{RO|bVYv9?Eudd6oe-22J zkvqvRhb)ZZ9v_ZV?KYz|*`+kI6j$0Y`B(R0Vp1yxvX#d;*9{}5B~?s`c*NH>KG?}* zFOT5oF_ZeMS#CGh-+p&55o|V^{dS%36|qK6IWlLbFR0a>>57Y9Ih0a2(~xPLA@2-c z?3*t4Y8WX}TZ8I>1szxprNtj%+AZGZAMLC`A|e0&DDs?E7n5KW=R<`QO@iYLGn{qA z+H4YQ%6b==}lDD>lxtXC$(xj zzO-Jl{6?xR{!2VZvm^|EzIRjn!N!)Te#lliohkQULr(4iH0mNpI&5q5M!#%d3>B-M zaG2cnWW1+XPwN&^BX2`aeWkwP`}U1vM`TH~)q3PHM6;ZA z<^&XIe=BD0?vY~Fry}~^W``(<@q&|#bhf0ta@CTW@j9Xrjqb=0fK6xK>+Q|mK{i70 zP7}~4(-4so_=-DOgS47RYYyYX|K>V^knGl{9Mk`4;bp~Z0Xya?%~$y}s8=m=Wa}dz zK=h1X{XO*}*((t)+Kp_4eu02TtqU}}KREah5DEeow0bp#Y{EbaF9h%c#**!r@4NrZ z)Og}NBGpygSZ=F1Z(MLh;HL+!33%h9gwlcem2P=gv^jfR8izaevTM&-dL}mI99?)t zp)eY6GOOs#Iz0BsQmR)I!%?6laY#Qg?c8uodeak&$MTISDPbK|%1f~ZOz?qc%_FVf z!m8~>JN{P8E54EP8_lWqTD*^qNQTs3^9{8l5|6pu8XheqT?LMQ9*Q;rqT9qwoYUyp zkpJ?1WzFFdR6mgCX(W5P;kApiQ;$Q1=-h77n}f~824k~a#JnzCg8eEpexy&lA`-&F zbJj(M_~md4&6}2wDV-j_t*WIJ#K5>L=>!_N<_msWU@Pl}s6>Mci8iVK_&$(#B=!Q^ zN!??nkP(bo&&%2HSjXcff|;4rXo7Z9X|@PclRtV@307b6A1HPb%!&IX1qL^B3rY(C zK4TT?_eTHw+y0M6v4q;Wxi>ytX6$n1nm&4m7(VQP4lau!p zf(Zo)&6HWcy+$4GwH)5TrBb9|bfzq+=wXMmc|*->P9_(2=2!hCtbww|$ofjPYmrig zPPWgwJGCtaES)S4jUnf)#tGL6@4k6#Sq_x)MQ2u8Xs(@m6HiN6f?({_=U@C9FOOUw zKCCoT1p1h3&sxMj+k}0@;8@QI`q#m{;``$S6xX-$x3Jz>zipe-7L;`HZw!EATC%=j zq4ddlh&8)}#wb(z)RLxO9X3$3$p0e396R1PQWa}viip)mtQUL{ZSp~$l%LBWWyvdSO#{ITFyZ!mC{VuYJ&&qit1znFY3h2>A zGWsI@CRbu@{jRlQX*$j68WDm1&fP{Gjkz!xc+;65XF zcOhY38Rpw?-9cYIQt#eqU;|zIy8}6ZcSo;U(gj<=aW*Q*cSalmiH0;jABC~i8wtbQ zaI9=t`l!*gcL8rlrC722F;6@r+%qqE-Lo%C5~=ivRcGU&BN6?MBL95q_;cd|ho7GI z0M-k!h0M|>n(2U30!=!6>VKrE1^?RPj_g)jC(2iH*s(_A;%Pip$f*0kdDcpA&ohX; zD(Uq7S;jRY^o>SBMxCFcy<+O6I-<83QOXRlQ6~a-oYS}c`|@pvzh*Ub z8^ACToN;~G{6Yy0y%@tbNVXl*C1;pXU-z#CqQ|VFYvK`}rhrG>jvh^%2_l*z)oE9( zL{`>_hA4sqhs-|j3vb0S**j6pd~!FB+}c_K{OiR5X5omVrnd_|KJ_bkIpRLG*tf>f zmoT9?GxAA6&K(hw+f?#wEu7G5Z*Q4@RCh-1eKpv`0Z6?9B?*#@e_b*t(fguz#VFwB zP{^UKeV`P=Skh0nIH1~Ih~JhCfB>=eSvN9~qDus%I&07*;%oriRbo7I^JRDrKp=uNj5rGHpF0pAwod!w;B8s0__OsU?fp!5hJ%3iWqOFeT^4wc#1cSlNTZs9 zDl^7q8lijN5kB*OJiJg|7bHb`1dE2L3j7mp&=N1 zm|KO(`l)_A+k!Y1dOr-jt8c6+|&oYJV*w)73iCF?y;H#v@`2}F~)=Tf7d zP;m0nY{4?zDreXrlHfpck@71@-rNt>J*LG9$brh#xixBO!nk**)U6!B&waH8B-@UT zj_NeNnax0b?Of|wgjc1b@8?surk=zMq{l=R`=ntfv=GHLQG$PrpZkzFvJ4}`ZIa!S zGX)DSkMg$B&tKeL0_gmAzfSO-L0+qLY5g|pi#9Gug?68^O1`r*9W6E*OOw{dH)95U z4T?w#_~ex-_rN3M_zz_4V;kwOcgRO#siwW@9i9ZU#y0&HO8N_)U}-S{4=m<~jt#%+bS+5LSh9%HabI1DK?$31%ba9G!H3v{;vbhg z!rxN5i&E){QJt4>eqgGhf%kI zua_*<2R$*m{g;pNDMM-%YaYVd#$Wh3S7o!Xpky6hu?G|1{SaX)3e zGrE-;kL#+7^yfAvEelU(r`T<32ZcgrReQI9u)J#H|ti-b$TM ze~5HkqVirN8Fi61Cb3g37-V2)*ZC5vnBr=2x zZdG|D1_e+ipF5zy*#ok8!~`dQ9)P|A8(n})cvox7B+6)>u2;Na9s~DiAJ~_h;IB>&>!`x5xPRR z!1~Vp`wFu1B2{SbtKQ3N*b@7>?hRMqpY%`Uyh%X#) zJ3I89`2#Wu(i+U0eUjnR64G+aQKvN{{J%fn0km zw?47B3ciXUbfh<774KT_wNL`}@=?(*y%kfH5*IjSGl$m%ReZwHAdy_L&u{n=I#-Q{ zgEHGNF-%r`z*2o98qy0l`(@1X{bZ+j#5K714Fs*JO6&ldw0HRKGp={W4Gb^_7~8;k zgTuXdoSFNIwqR%R?BA;Xm@&Q{*@kKn*?MaGDAKkNfAzy_9-e-gEIiC~hs71S zEL;2lZ)|0OCU-YuUIxaO3?^s}#F*^5f0#-Yw-vrlyUt8b;v`T}A?k7y5PJTPVoH}V z-+z2RY1Q|xQoq@W7kjjVW@BtnL4`*H2B0d;1rpKyZ2u^6_Bpo~F^5WqM|#ObJsyDv zl;)X+_kp4+W2ZNt}irfE0}om1r+dqtxRc<3iN&XG%Rsi(v7c z4(j&znZw#OObyvK)`44F@(**0b_xR+GzSKNE!1t=Ur;r=?IqropJ$-f$PECY$9lrbF>|9hhEI3bQ!_5+oV14^B9Yh!Pk8z{_LH1wFg!RV<0h|+dVTM^Q)q^PGs5!r8qTEn_G{q~UMp}#b;vV(An`RK zR4_U@+bVm$8%NJM;;(HP_9WF-ahA_#r#C=$G6~hG3AJpV4F$#(mRHLO+x;(o zGalZvZ^|0VKHl%nJMnFz;ZUi?(&NagQpxS8_Xa1IOD$syV{}eV?*=HLnCk|mQR(B7 zV#kqy*D{fWY5@24kP;w*8DlB3`RpK{yH8~L(RG$ZN;!NqMoj>`vfSYG-ML0ow$mq} zksmQYxHl@MqG&$1yyi>W9U~O3W4miT%fVL0+6#YH``xEiZ?l^@(Ee%&qY7K_FX|s~ zc%_P;BaE`z{2OOTj1Nc&6q2!oLh%QB9m2Ox4p-@M%y0@peL0cxX z20TCutzjE_wb-u1AeM@~)78}ssvjCnSAPL;8qG21UolyO515~uhCO08ov>VD(Qkc! zcbKX;BA+H0_GR7$P_r*#ipAYfK}mO3uFjMD@$KR}rjKej6H$^Kl?+BT(ku#vS7Q79 z^f}fe{CBN^JyD8{>CtH1yZ?eNWJU~slew;54kKSaVczWx>co{QhzSDU|$^hU0Dp_SSG^0EG7$I#yj zxe6`ME`IMOLo-*n)8G+ndZWRk1lNk$%vTaEspH>Q#~%GxqC1l;;o!>elet9j1NF0> z2s;6qre>k2l;jv*Y6v7Bi@`8y(IaQtOnKgTbFr zR$!~eRV>}je#0BheW)(a=@+{WuejYdo}XwD(#dnJHm@d<%0P~h?0o)CXu7FKyAPf* zCsX`HF8g>aeu;27ICOiSrSgz35h#+|8z|_=w)id9%`$7#V&1Cn2U#@*<-w+*tFF!N zDu5Xso!o;Y^T*tc>B`UwWFjrgI8PIv4dm3?{W@OpB-~{v8H`ZB`T&3*G^MP~n8~l} z8G&zFAv#sigfGDe$IbG)4p)@-ftG}>Jsf5oT*ZsPNsP_&bvGWsDG1Eh%F^)W4>I3 zw;5E2rTrnxj7Ta;;rkES#QtUk&MYQCo@RIUHue=}Ne{m>FC&?%%{vFD2tN9pgAI}0 z->s59yKbv$BP-F`cL!geMdA>?-Qduv22l%6b*R zUq5+a`+24>?La6f+awb0s@%EY20h%gq2Nu>|O9E-0RAC?X7&c!3@qeeRDqT&0qRp-*_$J z4*!x^JO@H@&8%z~kZ#+(b9++a;kXdQ&%;HEu!&`L)n8%L*6YdjIseBT24K5(6Rn@| zzVGquuWO7_(IvkG=5mgKaV(L$dYt=YY}mO+!}$F9@jAkqx63wofuiR|Ivau?GY!v7 z{r@m~A}vHF6HzGl{BtxoddEv)+4$%ZwM#Z~8}QKup)hJA73#q2k8*YrjUDIv{&{ue z>=QMoUIblmn9tv*wb_W9Id4*|b*la*IA8EMo87r9_>cA7!GtRjrHyj5#S?0zH(^q3 z+r&gFb?%xe80=s9nRH>P`&`cXLlN?7Mf%T3Y41U6$+cD5E>kkrf~U}EhV^adA)A$v zg}}y@wnlYN9P?dkD;sYxISy00sj2PAmvofjPj3!$yR+#)QN6E7KWY>SM!lt^{i zVUWTu$s@b!#?{w+^uqik|DhXJ`G8(KN&!6*Nu6&)UgK08g71^213K^HHTklxgjs1` z!_!iZa6AG8ohkG|vR`piiP7K)mSB-4TD^{RVMc1aErTBJsVc-!|F~|64 z@XfRWGhBl~NUk+MVUUKiwio2tMO0uT*J)v#ae@;|BNZbtNv?b=D!*&XKF-GMHtPKh zheTX}l?5Y7BKxa%N0i}YcywN)e=ZO@JZAfsF`fDYgyW88^2r0bAL)e9_$M8q;vq3J zJ#KHG^59-?yZSidX+Ia3mcjFzw>}+O>Tai0pGU?xwLCqSx64>=&V{QGqxUp7^EtNH zsv3htqJck%NS94}+A*>tycjoH&FORJ|o@oBjK@1drM%a%_z;hahoH>Pk<2*ClM? zY|dAv-nRD27@NjeGH@bonyG($aQp3bJ_f4iXMp5DGQNaT<4h3X< z+ccP!p{%Vzoc`m`B;Hq`K|x2x$FLCGiu+j znypw3v9+k!JBY1CRcn@Nswh%o)*eAot42gyYOjWxtyLp-?AEHSHKS_pRh05RdB6X} zH^1ijJm-0?`#SeIxpSZE%C_?Me#D>mo0%Tk-xK%ZEEnwqWP`gD7WNw3ApaAr*Qe+N za{KZc&DEG35NC=G9Z^UE|LhGSxcnaim!91xrrcQ-(jx*pe)#Pl};t{KV9v3|MkR$B#H*gr%CxCNxUm;V*PeiY+=XPYf8%sM)e+aUWy z%SeHzQcS?g#SR4FlmS$yzDTUks1(v&==1sj4hphmJ$gyP3?_!kVyf{%4j7=ZlI1UV z)-3=SGc7oBOP5*<#4)d7;U{2Tu=ra11>Wb3VrtrHjMi!6xyzw%Ev~MaJ)vQGNubsT z2J!ZREtt0li|u>8dOd$4`I86Rn8UwZ=11_nWO(z&S98hLe`c1KKi~J!anF?v)ggBF z$sXe=QeD~qk%nj=L{0Ya1nufCsy;@&)?!f zjLb6r zctiG!pz_U$oUFw4H>=z7N^^q~5G?_#sQGP{^?>D554n_6^j+#{@7#rfG*;_`8^>PS zKQ|iDY4-aBEtj)j7H^ZjuSHDFf-n7t)ibvf z$zMBKxuvC=U5iF8dx=kpUW4hUH-qaMu=T$PBXeQ>)OD2$ggV4m3yG2K?eOa(@oFxj zOxn&|#I=95ncwOA>I`1*2o%5-LeyaCdr{Jy(~IhDHGvz(rdgrbWY5DsjnLAUG$|CN z+_~dppl0@nUu{r&&|WK0ba&D&H(?Q+%JEb&)a=e>`;RBD95dc|^{wD*! zUyMp;Y;Es|*C%xA~@SQ zj@IuyL%kj=rGped)TDF^ncT0xXpS^^YM0~Ar&#Lv<@ zM2&Wp%rB*917)tF&b2bXE1? ziY45`ixX>cwXFcGZaq*Ic_Ok4?zdC`e|bD@^Jl8Kq}g&Z7QxK449x+YqE+$Qp;_tl z`6&Xa88vAt3>*Uye7z0Awh^@Ld}{64soy=|He`Unf7@QZ9Xze+O5wv+i1EH0wot5R zVVc&%C}QveulR%eKp3!ZWR%v}>2iqke$oEbG9ASy_Hdo$EHcwi0((omTe`R>X6*fa zp)9>;!mW~k(Wf>%70%F@*Gj;C4M94028v`zHs7E6vi}#*=7(2qW55tMNZy)e0&OI% z&Oc_!JkHJDlZ2mEqYJ!C0RhTwk1qaDBE^HYlIq&V4KF0!{Z=S}eHa(~f&T-4aG!i| zXynBWYmcG8-QB{J_H(YqtFpZTGEnV^NtK+sid#f-v1|U|xXCYbZnkfOBz|A$^4(V} zgeTS>?dJW!-SaZaxYH1~>+zJoT{|KZ_~X$JbEe>aV{2)~SLAH#uV_Q!nBH|9)P82X zJGvDcK74)vEOePP^nUHf)P-$c|4f<4O|+_|d|ISsZZsh)m!IfW%j1j|{^cF_H&})z zkS|jk_5k^O;?(~8tN*#c`vKcgL5I$B2}PTw!8I9<5b_rrLKY6YoAJaaCLKj-MR?(I zpn0W~DwefA#f!WUIj$Di-1COy=*<9n0F@5st+on8bAY#Fz`naMg# z&jYlFJ72vT0pXXvH>uN$V3$%l3}{+D{@)`v0dc%iagh!-bIB6!c8_{XHZiH&Q?GZc zDgM!&8PMA^C2_<~^bH0$-)aMW)3^h$1Oi=tSHtGJ(h!h4AKL$Ug52VY$;O-b{0%pW zYnN0l^Bd7ek87-f2T$JvnnDgIj#NX9=ro;;FC`QY{ODIBlf-he`3M>%$ zo;uir1Fl|7fQW->IPT0JU=ch{<;VQ#8d6b_%HAQ($Oalnb4&&h`zDcUhct(1_dTV> zFiJq137P}$ShHi1!j!T-*ONm=4i9GH4t77hw!4<>mO+2nr?hGYAZR6EUahCG>T!B> z;vG~o&azH>Yb-h>|J67~{;sm}{l@QvrlZl_fkAc}d`V#h0Os-|fG`vS@R_SE ztX!2YeXYdpvnU(ANkuy8vUKTCtIstOe27fqf->d&cC{vfrC!jyg2XdX9pIfoI)66G zm29=y{Fn-Qv<(^J8L)_oIeK+&Zw;c4pg4jQtT15O-@3UPCEGH+6?VD-G;=${<*NPj z$b*&bs~dm*8zSAU6sWU)3&k9Umnkn!BLAdv?p`kRjbEbVs+grm7qgix_ZrP9JX!Her z?@5Df^Z69J2x9V{oLgbx+iWsQZX3?}`xygP{5ZSM!!Hg1xA-jsLH{h@+e36&a038M zC?9~P%qrQg+6HFnseO@#alw1kP*GzYSmkB>OOJj~ZIn^}&tmR$_OZ=1*#}cD(B*PJ zLWw1o2JbSr49u*&G&W#r>0WJG4$EOsM1F&A$7ve&fl7_#WhXf9FeFt5W%8u3^C31X zRmRwz4e-_-`dgMHI>i8%?}@3_2CP}$?XgnJ;182sKiuDzKD*1Y6=EA@d9C49PW=M! zbX$j;62!%yywn$P#15;r8~a)d@Rnns6A(Dl#a4<HNhQ=zl`n0vp7PR|K%6 z&Uf`ap`eS5%VP6%E|{I9=?HA!!1csT-YW+;3-9~g=swj08!K@0kd0QlOv64Yx|WB# z0%^kj9dLZinXoK`qL}q&(%i(j`A*xkMG5m+%o&;7)JtBj?ItU8UUjVK_TJqopEZ5Q z+Z~<3O72qF<|7xG9xkOng%p9frek;h=)eAs&YC6B70M6DNZ7mJzct5k?Hvs|A{b$h zx?F{jgR0*6snueIUq{!)nMIl;tQJQ%4%1`mgAEW>!#wj_9-)(tf)qNd|H4W1sshfS zIQRY)H8|NySl);w)V@&hgo`iOyqx-R)j1mVLnRaLm+KHp%huRlOv57EuU(+=G zNt{P0>^BL_TCl^ovs+9my%V`xrd7ethMb(->#%cDMhyTMuO30*#GV{QaJS6aB~Z2* zq~0KYDll+Se4x@~nY5rF)4^dyYfxu&3uFyuLQ?kC=|9_>IZq2gatkBiTgX!K@Ys0A zn+(sC-@CX0-M>L~eo9S)dOcB312_Ov4+krSYKK$mwsDzPlUbF2FtWia#OU`nz6eG* z9go^*(w@S8Dr0wav=k#%MA*QAOmcgq9Rm$Sz#LEnMSD3pr=QP4^FaxHK*<+Tk5vK7w{VExYw$vU>K2$`j>JTJL#jgo-h zB@aM@DscrbgM=P#R!9xe_i1ibIwgRE75o3CWSYKAv-rN54v+zW9;Y35_qJr0{VAwr z3HNRsog-&<<7H#tRnaU>gMihhPwB=&UtIa$c1NpChd5$R{@tHWEPqh*s_ecRvZV-Zcm zWVBbbe}B4RZIj!4{+%Y-5<34+WQ_hbMK!T>ipKyzn~vXtw80_%Uo*|EvP6- z9<5!JI;Ia00CuTT?J#^UzPk29$Tlhrx>}GJ&7s4@DzV!LH`!7fD9!+)wwn>1(e?=n zI3aC=3Vks)PCx_QHT*ML!h920+6R(J@fP5P2!AYbGE*M(Oy_b#cStj$_^!}mo0 P1AKI~4YbPcScm-=eWKC$ diff --git a/web/JiShe.CollectBus.Migration.Host/wwwroot/libs/bootstrap/css/bootstrap.min.css b/web/JiShe.CollectBus.Migration.Host/wwwroot/libs/bootstrap/css/bootstrap.min.css deleted file mode 100644 index ed3905e..0000000 --- a/web/JiShe.CollectBus.Migration.Host/wwwroot/libs/bootstrap/css/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.HttpApi/CollectBusController.cs b/web/JiShe.CollectBus.Migration.HttpApi/CollectBusController.cs deleted file mode 100644 index fb10997..0000000 --- a/web/JiShe.CollectBus.Migration.HttpApi/CollectBusController.cs +++ /dev/null @@ -1,12 +0,0 @@ -using JiShe.CollectBus.Localization; -using Volo.Abp.AspNetCore.Mvc; - -namespace JiShe.CollectBus.Migration; - -public abstract class CollectBusController : AbpControllerBase -{ - protected CollectBusController() - { - LocalizationResource = typeof(CollectBusResource); - } -} diff --git a/web/JiShe.CollectBus.Migration.HttpApi/CollectBusMigrationHttpApiModule.cs b/web/JiShe.CollectBus.Migration.HttpApi/CollectBusMigrationHttpApiModule.cs deleted file mode 100644 index c02a3a9..0000000 --- a/web/JiShe.CollectBus.Migration.HttpApi/CollectBusMigrationHttpApiModule.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Localization.Resources.AbpUi; -using JiShe.CollectBus.Localization; -using Volo.Abp.AspNetCore.Mvc; -using Volo.Abp.Localization; -using Volo.Abp.Modularity; -using Microsoft.Extensions.DependencyInjection; - -namespace JiShe.CollectBus.Migration; - -[DependsOn( - typeof(CollectBusMigrationApplicationModule), - typeof(CollectBusMigrationApplicationContractsModule), - typeof(AbpAspNetCoreMvcModule) - )] -public class CollectBusMigrationHttpApiModule : AbpModule -{ - public override void PreConfigureServices(ServiceConfigurationContext context) - { - PreConfigure(mvcBuilder => - { - mvcBuilder.AddApplicationPartIfNotExists(typeof(CollectBusMigrationHttpApiModule).Assembly); - }); - - Configure(options => - { - options.ConventionalControllers - .Create(typeof(CollectBusMigrationApplicationModule).Assembly); - }); - } - - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.Resources - .Get() - .AddBaseTypes(typeof(AbpUiResource)); - }); - } -} diff --git a/web/JiShe.CollectBus.Migration.HttpApi/FodyWeavers.xml b/web/JiShe.CollectBus.Migration.HttpApi/FodyWeavers.xml deleted file mode 100644 index 1715698..0000000 --- a/web/JiShe.CollectBus.Migration.HttpApi/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/web/JiShe.CollectBus.Migration.HttpApi/JiShe.CollectBus.Migration.HttpApi.csproj b/web/JiShe.CollectBus.Migration.HttpApi/JiShe.CollectBus.Migration.HttpApi.csproj deleted file mode 100644 index 5047d92..0000000 --- a/web/JiShe.CollectBus.Migration.HttpApi/JiShe.CollectBus.Migration.HttpApi.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - net8.0 - enable - JiShe.CollectBus - True - - - - - - - - - - - - - - - - - - - - - diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/.config/dotnet-tools.json b/web/JiShe.CollectBusEPO.HttpApi.Host/.config/dotnet-tools.json deleted file mode 100644 index 76ca931..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/.config/dotnet-tools.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "dotnet-ef": { - "version": "8.0.10", - "commands": [ - "dotnet-ef" - ], - "rollForward": false - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostConst.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostConst.cs deleted file mode 100644 index e4783b7..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostConst.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace JiShe.CollectBusEPO -{ - public static class CollectBusEPOHttpApiHostConst - { - /// - /// 跨域策略名 - /// - public const string DefaultCorsPolicyName = "Default"; - - /// - /// Cookies名称 - /// - public const string DefaultCookieName = "JiShe.MicroService.Http.Api"; - - /// - /// SwaggerUi 端点 - /// - public const string SwaggerUiEndPoint = "/swagger"; - - /// - /// Hangfire 端点 - /// - public const string HangfireDashboardEndPoint = "/hangfire"; - - /// - /// CAP 端点 - /// - public const string CapDashboardEndPoint = "/cap"; - - - public const string MoreEndPoint = "https://doc.cncore.club/"; - - - /// - /// HMiniprofiler端点 - /// - public const string MiniprofilerEndPoint = "/profiler/results-index"; - - /// - /// HealthCheck端点 - /// - public const string HealthCheckEndPoint = "/health"; - - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostModule.Configure.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostModule.Configure.cs deleted file mode 100644 index 8f5f93a..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostModule.Configure.cs +++ /dev/null @@ -1,179 +0,0 @@ -using JiShe.CollectBusEPO.HttpApi.Host.Swaggers; - -namespace JiShe.CollectBusEPO; - -public partial class CollectBusEPOHttpApiHostModule -{ - - /// - /// 配置JWT - /// - private void ConfigureJwtAuthentication(ServiceConfigurationContext context, IConfiguration configuration) - { - context.Services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - }) - .AddJwtBearer(options => - { - options.TokenValidationParameters = - new TokenValidationParameters() - { - // 是否开启签名认证 - ValidateIssuerSigningKey = true, - ValidateIssuer = true, - ValidateAudience = true, - ValidateLifetime = true, - ClockSkew = TimeSpan.Zero, - ValidIssuer = configuration["Jwt:Issuer"], - ValidAudience = configuration["Jwt:Audience"], - IssuerSigningKey = - new SymmetricSecurityKey( - Encoding.ASCII.GetBytes(configuration["Jwt:SecurityKey"])) - }; - - options.Events = new JwtBearerEvents - { - OnMessageReceived = currentContext => - { - var path = currentContext.HttpContext.Request.Path; - if (path.StartsWithSegments("/login")) - { - return Task.CompletedTask; - } - - var accessToken = string.Empty; - if (currentContext.HttpContext.Request.Headers.ContainsKey("Authorization")) - { - accessToken = currentContext.HttpContext.Request.Headers["Authorization"]; - if (!string.IsNullOrWhiteSpace(accessToken)) - { - accessToken = accessToken.Split(" ").LastOrDefault(); - } - } - - if (accessToken.IsNullOrWhiteSpace()) - { - accessToken = currentContext.Request.Query["access_token"].FirstOrDefault(); - } - - if (accessToken.IsNullOrWhiteSpace()) - { - accessToken = currentContext.Request.Cookies[@CollectBusEPOHttpApiHostConst.DefaultCookieName]; - } - - currentContext.Token = accessToken; - currentContext.Request.Headers.Remove("Authorization"); - currentContext.Request.Headers.Add("Authorization", $"Bearer {accessToken}"); - - return Task.CompletedTask; - } - }; - }); - } - - - /// - /// Redis缓存 - /// - private void ConfigureCache(ServiceConfigurationContext context) - { - Configure( - options => { options.KeyPrefix = "MicroService:"; }); - var configuration = context.Services.GetConfiguration(); - var redis = ConnectionMultiplexer.Connect(configuration.GetValue("Redis:Configuration")); - context.Services - .AddDataProtection() - .PersistKeysToStackExchangeRedis(redis, "MicroService-Protection-Keys"); - } - - /// - /// 配置Identity - /// - private void ConfigureIdentity(ServiceConfigurationContext context) - { - context.Services.Configure(options => { options.Lockout = new LockoutOptions() { AllowedForNewUsers = false }; }); - } - - - private void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration) - { - context.Services.AddSwaggerGen( - options => - { - configuration.GetSection("SwaggerConfig").Get>().ForEach(group => - { - options.SwaggerDoc(group.GroupName, - new OpenApiInfo { Title = group.Title, Version = group.Version }); - }); - - options.DocInclusionPredicate((docName, apiDes) => - { - if (docName == "Basic" && apiDes.GroupName.IsNullOrWhiteSpace()) return true; - return docName == apiDes.GroupName; - }); - - options.EnableAnnotations(); - options.DocumentFilter(); - options.SchemaFilter(); - var xmlPaths = Directory.GetFiles(AppContext.BaseDirectory, "*.xml") - .Where(a => a.EndsWith("Application.App.xml") || - a.EndsWith("Application.Contracts.App.xml") || - a.EndsWith("Application.xml") || - a.EndsWith("Application.Contracts.xml") || - a.EndsWith("HttpApi.xml") || - a.EndsWith("HttpApi.Host.xml")) - .Distinct() - .ToList(); - foreach (var xml in xmlPaths) options.IncludeXmlComments(xml, true); - }); - } - - - - /// - /// 审计日志 - /// - private void ConfigureAuditLog(ServiceConfigurationContext context) - { - Configure - ( - options => - { - options.IsEnabled = true; - options.EntityHistorySelectors.AddAllEntities(); - options.ApplicationName = "JiShe.MicroService"; - } - ); - - Configure( - options => - { - options.IgnoredUrls.Add("/AuditLogs/page"); - options.IgnoredUrls.Add("/hangfire/stats"); - options.IgnoredUrls.Add("/hangfire/recurring/trigger"); - options.IgnoredUrls.Add("/cap"); - options.IgnoredUrls.Add("/"); - }); - } - - //private void ConfigurationMultiTenancy() - //{ - // Configure(options => { options.IsEnabled = MultiTenancyConsts.IsEnabled; }); - //} - - private void ConfigureCustom(ServiceConfigurationContext context, IConfiguration configuration) - { - context.Services.AddSingleton(); - //context.Services.AddStorage(configuration); - } - - /// - /// 配置Serilog - /// - private void ConfigureSerilog(ServiceConfigurationContext context) - { - context.Services.AddSerilog(); - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostModule.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostModule.cs deleted file mode 100644 index e97e840..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/CollectBusEPOHttpApiHostModule.cs +++ /dev/null @@ -1,73 +0,0 @@ -using JiShe.CollectBusEPO.Extensions.HealthCheck; -using JiShe.CollectBusEPO.HttpApi.Host.Swaggers; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Volo.Abp.Swashbuckle; - -namespace JiShe.CollectBusEPO; - -[DependsOn( - typeof(CollectBusEPOHttpApiModule), - typeof(AbpAspNetCoreAuthenticationJwtBearerModule), - typeof(AbpAspNetCoreSerilogModule), - typeof(AbpSwashbuckleModule), - typeof(CollectBusEPOApplicationModule) -)] -public partial class CollectBusEPOHttpApiHostModule : AbpModule -{ - public override void OnPostApplicationInitialization(ApplicationInitializationContext context) - { - base.OnPostApplicationInitialization(context); - } - - public override void ConfigureServices(ServiceConfigurationContext context) - { - var configuration = context.Services.GetConfiguration(); - ConfigureSerilog(context); - - ConfigureCache(context); - ConfigureSwaggerServices(context, configuration); - ConfigureJwtAuthentication(context, configuration); - ConfigureIdentity(context); - ConfigureAuditLog(context); - ConfigureCustom(context, configuration); - - } - - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - var app = context.GetApplicationBuilder(); - var configuration = context.GetConfiguration(); - app.UseCorrelationId(); - app.UseStaticFiles(); - - - app.UseRouting(); - app.UseCors(CollectBusEPOHttpApiHostConst.DefaultCorsPolicyName); - - - app.UseAuthorization(); - app.UseSwagger(); - app.UseAbpSwaggerUI(options => - { - configuration.GetSection("SwaggerConfig").Get>().ForEach(group => - { - options.SwaggerEndpoint($"/swagger/{group.GroupName}/swagger.json", group.Title); //分组显示 - }); - options.DocExpansion(DocExpansion.None); - options.DefaultModelsExpandDepth(-1); - }); - - app.UseAuditing(); - app.UseAbpSerilogEnrichers(); - app.UseUnitOfWork(); - app.UseConfiguredEndpoints(endpoints => - { - //endpoints.MapHealthChecks("/health", new HealthCheckOptions - //{ - // Predicate = _ => true, - // ResponseWriter = HealthCheckResponse.Writer - //}); - }); - - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Controllers/HomeController.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/Controllers/HomeController.cs deleted file mode 100644 index 4e5ab2f..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Controllers/HomeController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace JiShe.CollectBusEPO.Controllers -{ - public class HomeController : AbpController - { - private readonly ILogger _logger; - private readonly IConfiguration configuration; - - public HomeController(ILogger logger, IConfiguration configuration) - { - _logger = logger; - this.configuration = configuration; - } - - public ActionResult Index() - { - return Redirect("/Monitor"); - } - - /// - /// ������� - /// - /// - [HttpGet] - public IActionResult Health() - { - return Ok(); - } - } -} diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/Dockerfile b/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/Dockerfile deleted file mode 100644 index 75a20a7..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -FROM docker.jisheyun.com/jisheyun/aspnetcore:8.0.10_basic_ub24 AS base - -# 这里确定使用的端口 -EXPOSE 8080 -EXPOSE 443 - - -COPY publish /app - -WORKDIR /app -ARG BUD_ENV=Development - -# 这里确定生产环境还是DEV环境等 -ENV RUN_ENV=$BUD_ENV - -# 根据 RUN_ENV 的值删除特定的配置文件 -# 只在构建过程中执行的指令 -RUN if [ "$RUN_ENV" = "Development" ]; then \ - rm -f ./appsettings.Production.json; \ - fi - -RUN if [ "$RUN_ENV" = "Production" ]; then \ - rm -f ./appsettings.Development.json; \ - fi - -ENTRYPOINT ["dotnet", "JiShe.MicroService.HttpApi.Host.dll"] - -##See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. -# -#FROM mcr.microsoft.com/dotnet/aspnet:8.0.10-noble AS base - # -#WORKDIR /app -# -##USER root -# -## apt-get源 使用aliyun的源 -#COPY ["sources.list", "/etc/apt/"] -# -## 转换时区 -#RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ - #&& echo 'Asia/Shanghai' >/etc/timezone \ -## 更新系统并安装网络调试工具 - #&& apt-get update \ - #&& apt-get install -y \ - #vim \ - #net-tools \ - #iputils-ping \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/dockerbuild_dev.ps1 b/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/dockerbuild_dev.ps1 deleted file mode 100644 index 09dc2cd..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/dockerbuild_dev.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -# 汾 -$version = "1.0.2" - -$csprojFile = '..\.\*.csproj' -$files = Get-ChildItem -Path $csprojFile -$fullPath = $files[0] -$name = $fullPath.BaseName - -Write-Host "׼¼ֿ" -docker login 192.168.111.248:10001 --username=jenkins --password GTCz3pV8kdme4rh5 - -$lowerName = $name.toLower() - -$tagName = "$($lowerName):$($version)" - -Write-Host "ʼ" -Write-Host $tagName - -docker build --build-arg BUD_ENV=Development --progress=plain -t $tagName . - -Write-Host "ɹ"; - -docker tag $tagName "192.168.111.248:10001/jisheyun/jishe.testservice:$($version)" - -Write-Host "ʼ;"; -#docker push "192.168.111.248:10001/jisheyun/jishe.testservice:$($version)" diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/dockerbuild_pro.ps1 b/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/dockerbuild_pro.ps1 deleted file mode 100644 index 5145ec3..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/dockerbuild_pro.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -# 汾 -$version = "1.0.2" - -$csprojFile = '..\.\*.csproj' -$files = Get-ChildItem -Path $csprojFile -$fullPath = $files[0] -$name = $fullPath.BaseName - -Write-Host "׼¼ֿ" -docker login 192.168.111.248:10001 --username=jenkins --password GTCz3pV8kdme4rh5 - -$lowerName = $name.toLower() - -$tagName = "$($lowerName):$($version)" - -Write-Host "ʼ" -Write-Host $tagName - -docker build --build-arg BUD_ENV=Production --progress=plain -t $tagName . - -Write-Host "ɹ"; - -docker tag $tagName "192.168.111.248:10001/jisheyun/jishe.testservice:$($version)" - -Write-Host "ʼ;"; -#docker push "192.168.111.248:10001/jisheyun/jishe.testservice:$($version)" diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/sources.list b/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/sources.list deleted file mode 100644 index d2814c4..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Docker/sources.list +++ /dev/null @@ -1,10 +0,0 @@ -deb http://mirrors.aliyun.com/ubuntu/ noble main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ noble main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ noble-security main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ noble-security main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ noble-updates main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ noble-updates main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ noble-proposed main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ noble-proposed main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ noble-backports main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ noble-backports main restricted universe multiverse \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Extensions/HealthCheck/Extensions.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/Extensions/HealthCheck/Extensions.cs deleted file mode 100644 index 9a65556..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Extensions/HealthCheck/Extensions.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace JiShe.CollectBusEPO.Extensions.HealthCheck -{ - public static class Extensions - { - /// - /// 添加监控检查,支持配置文件配置 - /// - /// - /// - /// - public static IServiceCollection AddHealthChecks( - this IServiceCollection services, IConfiguration configuration) - { - if (Convert.ToBoolean(configuration["HealthCheck:IsEnable"])) - { - var healthChecksService = services - .AddHealthChecks(); - - //添加对mysql的监控检查 - //if (Convert.ToBoolean(configuration["HealthCheck:MySql:IsEnable"])) - //{ - // var connectionString = configuration["HealthCheck:MySql:Connection"]; - // if (connectionString.IsNullOrWhiteSpace()) - // connectionString = configuration.GetConnectionString("Default"); - // healthChecksService.AddMySql( - // connectionString, - // "Mysql", - // HealthStatus.Degraded, - // new string[] { "db", "sql", "mysql" } - // ); - //} - //添加对Pings的监控检查 - //if (Convert.ToBoolean(configuration["HealthCheck:Pings:IsEnable"])) - // healthChecksService.AddPingHealthCheck(setup => - // { - // setup.AddHost(configuration["HealthCheck:Pings:Host"], Convert.ToInt32(configuration["HealthCheck:Pings:TimeOut"])); - // }, tags: new string[] { "ping" }); - - ////添加对postgresql的监控检查 - //if (Convert.ToBoolean(configuration["HealthCheck:PostgreSql:IsEnable"])) - // healthChecksService.AddNpgSql( - // configuration["ConnectionStrings:Default"], - // name: configuration["HealthCheck:PostgreSql:Name"], - // failureStatus: HealthStatus.Degraded, - // tags: new string[] { "db", "sql", "postgresql" } - // ); - - ////添加对Redis的监控检查 - //if (Convert.ToBoolean(configuration["HealthCheck:Redis:IsEnable"])) - // healthChecksService.AddRedis( - // configuration["HealthCheck:Redis:ConnectionString"], - // tags: new string[] { "redis" } - // ); - - ////添加对RabbitMq的监控检查 - //if (Convert.ToBoolean(configuration["HealthCheck:RabbitMq:IsEnable"])) - // healthChecksService.AddRabbitMQ( - // rabbitConnectionString: configuration["HealthCheck:RabbitMq:ConnectionString"], - // tags: new string[] { "redis" } - // ); - - ////添加对sql server的监控检查 - //if (Convert.ToBoolean(configuration["HealthCheck:SqlServer:IsEnable"])) - // healthChecksService.AddSqlServer( - // configuration["ConnectionStrings:Default"], - // "SELECT 1;", - // configuration["HealthCheck:SqlServer:Name"], - // HealthStatus.Degraded, - // new string[] { "db", "sql", "sqlserver" } - - ////添加对Hangfire的监控检查 - //if (Convert.ToBoolean(configuration["HealthCheck:Hangfire:IsEnable"])) - // healthChecksService.AddHangfire(options => - // { - // options.MaximumJobsFailed = Convert.ToInt32(configuration["HealthCheck:Hangfire:MaximumJobsFailed"]); - // options.MinimumAvailableServers = Convert.ToInt32(configuration["HealthCheck:Hangfire:MinimumAvailableServers"]); - // }, tags: new string[] { "hangfire" }); - - ////添加对SignalR的监控检查 - //if (Convert.ToBoolean(configuration["HealthCheck:SignalR:IsEnable"])) - // healthChecksService.AddSignalRHub(configuration["HealthCheck:SignalR:Url"], tags: new string[] { "signalr" }); - } - return services; - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Extensions/HealthCheck/HealthCheckResponse.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/Extensions/HealthCheck/HealthCheckResponse.cs deleted file mode 100644 index 9fe52ef..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Extensions/HealthCheck/HealthCheckResponse.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Newtonsoft.Json; - -namespace JiShe.CollectBusEPO.Extensions.HealthCheck -{ - public class HealthCheckResponse - { - public static Task Writer(HttpContext context, HealthReport healthReport) - { - context.Response.ContentType = "application/json"; - - var result = JsonConvert.SerializeObject(new - { - status = healthReport.Status.ToString(), - errors = healthReport.Entries.Select(e => new - { - key = e.Key, - value = e.Value.Status.ToString() - }) - }); - return context.Response.WriteAsync(result); - - } - } -} diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/GlobalUsings.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/GlobalUsings.cs deleted file mode 100644 index c588db2..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/GlobalUsings.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Global using directives - -global using Microsoft.AspNetCore.Authentication.JwtBearer; -global using Microsoft.AspNetCore.Builder; -global using Microsoft.AspNetCore.DataProtection; -global using Microsoft.AspNetCore.Hosting; -global using Microsoft.AspNetCore.Http; -global using Microsoft.AspNetCore.Identity; -global using Microsoft.AspNetCore.Mvc; -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; -global using Microsoft.Extensions.Logging; -global using Microsoft.IdentityModel.Tokens; -global using Microsoft.OpenApi.Models; -global using Serilog; -global using StackExchange.Redis; -global using Swashbuckle.AspNetCore.SwaggerUI; -global using System; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Threading.Tasks; -global using Volo.Abp; -global using Volo.Abp.AspNetCore.Auditing; -global using Volo.Abp.AspNetCore.Authentication.JwtBearer; -global using Volo.Abp.AspNetCore.Mvc; -global using Volo.Abp.AspNetCore.Serilog; -global using Volo.Abp.Auditing; -global using Volo.Abp.Caching; -global using Volo.Abp.Modularity; diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/JiShe.CollectBusEPO.HttpApi.Host.csproj b/web/JiShe.CollectBusEPO.HttpApi.Host/JiShe.CollectBusEPO.HttpApi.Host.csproj deleted file mode 100644 index 1c0e309..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/JiShe.CollectBusEPO.HttpApi.Host.csproj +++ /dev/null @@ -1,62 +0,0 @@ - - - net8.0 - JiShe.CollectBusEPO - true - JiShe.MicroService-4681b4fd-151f-4221-84a4-929d86723e4c - Linux - ..\.. - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_ContentIncludedByDefault Remove="wwwroot\files\a.txt" /> - - - - - - diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Pages/Monitor.cshtml b/web/JiShe.CollectBusEPO.HttpApi.Host/Pages/Monitor.cshtml deleted file mode 100644 index 1825dbb..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Pages/Monitor.cshtml +++ /dev/null @@ -1,193 +0,0 @@ -@page "/Monitor" -@using JiShe.CollectBusEPO -@model JiShe.CollectBusEPO.Pages.Monitor - - -@{ - Layout = null; -} - - - - - - - - - - 后端服务 - - - -
- -
-
-
- - - -
-

- SwaggerUI -

-
-
-
- -
-
- - - -
-

- CAP面板 -

-
-
-
- -
-
- - - -
-

- Hangfire面板 -

-
-
-
-
-
- - - -
-

- Miniprofiler -

-
-
-
-
-
- - - -
-

- HealthCheck -

-
-
-
-
-
- - - \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Pages/Monitor.cshtml.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/Pages/Monitor.cshtml.cs deleted file mode 100644 index d57d898..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Pages/Monitor.cshtml.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace JiShe.CollectBusEPO.Pages -{ - public class Monitor : PageModel - { - public void OnGet() - { - - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Program.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/Program.cs deleted file mode 100644 index 20b7d21..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Program.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace JiShe.CollectBusEPO -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - - } - - private static IHostBuilder CreateHostBuilder(string[] args) - { - var env = Environment.GetEnvironmentVariable("RUN_ENV") ?? "Development"; - - return Host.CreateDefaultBuilder(args) - .UseEnvironment(env) - .ConfigureAppConfiguration((context, builder) => - { - var c = builder.Build(); - - builder.AddJsonFile($"appsettings.json", false, true); - builder.AddJsonFile($"appsettings.{env}.json", false, true); - - //builder.AddNacosV2Configuration(c.GetSection("NacosConfig")); - }) - .ConfigureWebHostDefaults(webBuilder => - { - var runPort = Environment.GetEnvironmentVariable("RUN_PORT") ?? "8088"; - webBuilder.ConfigureKestrel((context, options) => { options.Limits.MaxRequestBodySize = 1024 * 50; }); - webBuilder.UseStartup(); - webBuilder.UseUrls($"http://*:{runPort}"); - }) - .UseSerilog((context, loggerConfiguration) => - { - loggerConfiguration.ReadFrom.Configuration(context.Configuration); - }).UseAutofac(); - } - } -} diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Properties/launchSettings.json b/web/JiShe.CollectBusEPO.HttpApi.Host/Properties/launchSettings.json deleted file mode 100644 index 8481021..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Properties/launchSettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "profiles": { - "JiShe.MicroService.HttpApi.Host": { - "commandName": "Project", - "commandLineArgs": "serviceip=192.168.111.174 serviceport=8088", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:8088" - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Startup.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/Startup.cs deleted file mode 100644 index f655483..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Startup.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace JiShe.CollectBusEPO -{ - public class Startup - { - private readonly IConfiguration _configuration; - - public Startup(IConfiguration configuration) - { - _configuration = configuration; - } - - - public void ConfigureServices(IServiceCollection services) - { - services.AddApplication(); - } - - public void Configure(IApplicationBuilder app,IHostApplicationLifetime lifetime) - { - app.InitializeApplication(); - } - } -} diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/EnumSchemaFilter.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/EnumSchemaFilter.cs deleted file mode 100644 index c31c8c2..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/EnumSchemaFilter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Interfaces; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace JiShe.CollectBusEPO.HttpApi.Host.Swaggers -{ - /// - /// swagger 枚举映射, - /// 原因:前端代理生成枚举是数字 - /// - public class EnumSchemaFilter : ISchemaFilter - { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) - { - if (!context.Type.IsEnum) - return; - OpenApiArray openApiArray = new OpenApiArray(); - openApiArray.AddRange((IEnumerable)Enum.GetNames(context.Type).Select(n => new OpenApiString(n))); - schema.Extensions.Add("x-enumNames", openApiArray); - schema.Extensions.Add("x-enum-varnames", openApiArray); - } - } -} diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/HiddenAbpDefaultApiFilter.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/HiddenAbpDefaultApiFilter.cs deleted file mode 100644 index 31dbca5..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/HiddenAbpDefaultApiFilter.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace JiShe.CollectBusEPO.HttpApi.Host.Swaggers -{ - /// - /// 在使用nswag的时候,原生默认的api导致生产的代理类存在问题 - /// 所有隐藏原生的api,重写路由 - /// - public class HiddenAbpDefaultApiFilter : IDocumentFilter - { - public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) - { - foreach (ApiDescription apiDescription in context.ApiDescriptions) - { - if (apiDescription.TryGetMethodInfo(out MethodInfo _)) - { - string key = "/" + apiDescription.RelativePath; - if (IsHidden(key)) - swaggerDoc.Paths.Remove(key); - } - } - } - - private bool IsHidden(string key) - { - foreach (string hiddenAbpDefaultApi in GetHiddenAbpDefaultApiList()) - { - if (key.Contains(hiddenAbpDefaultApi)) - return true; - } - return false; - } - - private List GetHiddenAbpDefaultApiList() - { - return new List() - { - "/api/abp/multi-tenancy/tenants", - "/api/account", - "/api/feature-management/features", - "/api/permission-management/permissions", - "/api/identity/my-profile", - "/api/identity", - "/api/multi-tenancy/tenants", - "/api/setting-management/emailing", - "/configuration", - "/outputcache" - }; - } - } -} diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/SwaggerConfig.cs b/web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/SwaggerConfig.cs deleted file mode 100644 index 9b6de43..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/Swaggers/SwaggerConfig.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace JiShe.CollectBusEPO.HttpApi.Host.Swaggers -{ - public class SwaggerConfig - { - public string GroupName { get; set; } - - public string Title { get; set; } - - public string Version { get; set; } - } -} diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.Development.json b/web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.Development.json deleted file mode 100644 index c1860fa..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.Development.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.Production.json b/web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.Production.json deleted file mode 100644 index c1860fa..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.Production.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.json b/web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.json deleted file mode 100644 index 92d8dc2..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/appsettings.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "Serilog": { - "Using": [ - "Serilog.Sinks.Console", - "Serilog.Sinks.File" - ], - "MinimumLevel": { - "Default": "Debug", - "Override": { - "Microsoft": "Information", - "Volo.Abp": "Information", - "Hangfire": "Information", - "DotNetCore.CAP": "Information", - "Serilog.AspNetCore": "Information", - "Microsoft.EntityFrameworkCore": "Warning", - "Microsoft.AspNetCore": "Information" - } - }, - "WriteTo": [ - { - "Name": "Console" - }, - { - "Name": "File", - "Args": { - "path": "logs/logs-.txt", - "rollingInterval": "Day" - } - } - ] - }, - "ConnectionStrings": { - "Default": "mongodb://mongo_PmEeF3:lixiao1980@192.168.5.9:27017/JiSheCollectBus?authSource=admin&maxPoolSize=400&minPoolSize=10&waitQueueTimeoutMS=5000", - "Kafka": "192.168.5.9:29092,192.168.5.9:39092,192.168.5.9:49092", - "PrepayDB": "server=118.190.144.92;database=jishe.sysdb;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False", - "EnergyDB": "server=118.190.144.92;database=db_energy;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False" - }, - "Redis": { - "Configuration": "192.168.5.9:6380,password=1q2w3e!@#,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", - "MaxPoolSize": "50", - "DefaultDB": "14", - "HangfireDB": "13" - }, - "Kafka": { - "BootstrapServers": "192.168.5.9:29092,192.168.5.9:39092,192.168.5.9:49092", - "EnableFilter": true, - "EnableAuthorization": false, - "SecurityProtocol": "SaslPlaintext", - "SaslMechanism": "Plain", - "SaslUserName": "lixiao", - "SaslPassword": "lixiao1980", - "KafkaReplicationFactor": 3, - "NumPartitions": 30, - "FirstCollectionTime": "2025-04-22 16:07:00" - }, - "IoTDBOptions": { - "UserName": "root", - "Password": "root", - "ClusterList": [ "121.42.175.177:16667" ], - "PoolSize": 32, - "DataBaseName": "energy", - "OpenDebugMode": true, - "UseTableSessionPoolByDefault": false - }, - "App": { - "SelfUrl": "http://localhost:44315", - "CorsOrigins": "http://localhost:4200,http://localhost:3100" - }, - "Jwt": { - "Audience": "JiShe.CollectBus", - "SecurityKey": "dzehzRz9a8asdfasfdadfasdfasdfafsdadfasbasdf=", - "Issuer": "JiShe.CollectBus", - "ExpirationTime": 2 - }, - "HealthChecks": { - "IsEnable": false, - "HealthCheckDatabaseName": "HealthChecks", - "EvaluationTimeInSeconds": 10, - "MinimumSecondsBetweenFailureNotifications": 60 - }, - "SwaggerConfig": [ - { - "GroupName": "Basic", - "Title": "【后台管理】基础模块", - "Version": "V1" - }, - { - "GroupName": "Business", - "Title": "【后台管理】业务模块", - "Version": "V1" - } - ], - "ServerApplicationOptions": { - "ServerTagName": "JiSheCollectBus99", - "SystemType": "Energy", - "FirstCollectionTime": "2025-04-28 15:07:00", - "AutomaticVerificationTime": "16:07:00", - "AutomaticTerminalVersionTime": "17:07:00", - "AutomaticTelematicsModuleTime": "17:30:00", - "AutomaticDayFreezeTime": "02:30:00", - "AutomaticMonthFreezeTime": "03:30:00", - "DefaultProtocolPlugin": "T37612012ProtocolPlugin" - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/tempkey.jwk b/web/JiShe.CollectBusEPO.HttpApi.Host/tempkey.jwk deleted file mode 100644 index b4f8b7d..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/tempkey.jwk +++ /dev/null @@ -1 +0,0 @@ -{"AdditionalData":{},"Alg":"RS256","Crv":null,"D":"eH-Ld45J684VguyI2jACQEEVGtTx79Nt7ElT20JeUi-pbVnhJxxAJwnAe68d9Q8skpv6BxZs5QuyIORwfGpJK-lKVuo8EtyUQTuUuPmP4o1YK4cv2FIi5xN18tddTltg2JmJi1sN2AD5z-zwm45YNvoFxdQYlnUlA9lJL8QfG0HQLMQX1sv2_lgND8RfRGQBCpVXC2kcap4GvkFVQpKaQ5xaUqvNdH6ftwkGMdFfMRlHGhyYyabIjs4T16HslofvXiHbOCAzk37HtBNNsBpeoQuZul1-G7tAndHe1XtuGvuE-k1fyqdm3YiCU8wK7FpvQU3x6JqvfqmWgMAPnWKOtQ","DP":"hd3I9Nc3LlaZDoPg20JZu48kpv9rMMQ0rLtcZ0UPB_HpZWBC_No-3t3t1HFRbD4iz0MCpCR6gb5q4UsL2N2xh3Q8OhQ1Zrl76UKDZrVKs3oE0VVr9K2VUU3s1sytE4OWSv7PAHYURygcx_MrunMn8Ryd4ZJBQ_g5M2GvpBj1o5M","DQ":"T3ibciK4KxGGHqau9dnWxE6l3fGtcNw1GcjV93Lxe0NKTbfrwPEIFVVAyPsFS8QdmRXEgyunkDFKLHyq2hXBi2fZCwXGoWkUqVUKAKMaNYZaDDd_XeJKOu7mwNY8rgxNETHCyMSnnNNPNabBf2iiXmrBnMwesle2L1kwky42yns","E":"AQAB","K":null,"KeyId":"0D94529E740F5FD50EC6B8A19FF460D6","Kid":"0D94529E740F5FD50EC6B8A19FF460D6","Kty":"RSA","N":"w2jhtWb6a3kH4VqAPfuuvdF5hBh7U6gwMRnddHAH7zaVL6aYwjpCzNW56RQy4W4Zabv1My4Yw9GZpjrOcyr3Bm669eZjn_JC0O00eRTNStNgmg2kB_6AB6ROkEW7br1JC0jKr_dXhOYPBMmR-KHvpwaZXA9R1xOqY02S3JD0KB-TGMSC9q1RlVydt81aMOHFzWVyruLsYGdmcKhRPKDtL6GXpU_DG1gzzOYbk795x_D_UUTOJYuhIDEj7aYi3o32yXLEBvh4Q7HPLjLcWzV-EzCn6Ossl-EVJ2TrR53Eln03R93Vmo63DzQbLIOi3yTcZD2a3O6mpGLiqyZjUducIQ","Oth":null,"P":"y84cEtE6IRBeU2sDJxjDYb50HB4nAF7-bfvbEq0haQL2sFBz-Q-uOUAeXhaS74Gh_IagmW1TzOKblVilijMHCsfOFTWlCljaDoNfdl31enh50HMFvUu8IFjcWiQ9fjR5no8n5-jDeRO5zVfgcWoRHfVTOq4Di7WlVHJRr8FB-R8","Q":"9XRZvvMIvoumlDAVxWZHkb7_an0_dFwBLqc8v74XBZhiK5SLuBqmsJICB5kwNTKzORH7yedx-RXOJWkchJLlEtmH_cQO_6WKQQAyc1PMnLPa0tYkhjotWo8VRE9bAdVmrR0cnJeudfSJ-6gDpUBQYy9g1m8cAfWbTAk3at2_gr8","QI":"xRLXLwhTdNbevlEzANlDDnU9lDeKZWV9-YrvUxHMJBBygBNVqkNFWiee7LNUi82YgTHRn46rpHY9TZf6oQbgk6xa86MyDYXGU2uGO8DHm5oh20Spp7A-RlzZ4JRXAJ3eAYMZHQIP45JApVvKbgHh_pUGjNaiqZKZ9IN6H_L9k1s","Use":null,"X":null,"X5t":null,"X5tS256":null,"X5u":null,"Y":null,"KeySize":2048,"HasPrivateKey":true,"CryptoProviderFactory":{"CryptoProviderCache":{},"CustomCryptoProvider":null,"CacheSignatureProviders":true}} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/tempkey.rsa b/web/JiShe.CollectBusEPO.HttpApi.Host/tempkey.rsa deleted file mode 100644 index ed0defa..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi.Host/tempkey.rsa +++ /dev/null @@ -1 +0,0 @@ -{"KeyId":"600caa200caf5d805eba9f06ace9e236","Parameters":{"D":"KCNDHA96eimN+UqchSKocgYITGflaAIwxzCS5KqSTkYAFliPthQx7LySuLor4F1+uLvwnh3ZocyI3y43GZu+eVHD256sxdV8/UsQz1HC23RRFqcUiAZjze8K5VMVStrBOxaa/Ds1U9/bpuNE7jZdcgFIEHsdZtCACqwtlE4nlIs1/GLiokqjBOESgxJMy9WUeDbWcvoo+YdwgKf5jt6AZHOYSS+TokLL+Y7TEfGMXe3jZD9VtSMkBSM8wGB89zNGR0FZB9maCG/BCoRJqxdYRyeb4FFXJclQtK3DexyDVqlNZQaNKVHu0tVAnVNKKcd7Iex8gA+5DNqqucUA7C/F6Q==","DP":"fr9iaNb1W4YZ/NJ56+N3SCeDQYuKobq1qeaQWmHlQsOHKoHhNZJQZ5x0M9PQilou16AwVlNGCJncMwxsSUxXn6itG0LcBnvfMeo2v3xKcij1BtFR9qfXecwEn2nnhI3mpXtZxyCdP3NIYUp9qViLJUjGJqrbQk+OIAGRQd2rRe0=","DQ":"o1umLkDodtwvpCsDguQYSjd3iob+WHNmfe/9HyjADmUehP8b9SpUgcrb+QF301J8YmQMnYZKWW5rEwKOtwsWNswgXfMnXeWerlZmz0tj9y38YczS70liU0vETsRefhrRCaXHraMvneqYNNedhsrCNalWK+DNwcixi4L59vA8ofs=","Exponent":"AQAB","InverseQ":"btd1nwwxl/E3ryfDi2bN12TuVDvv7yoPvryIlLgu+FiLpe4vaA1omDLliQBcl7oeyA563HBUop4D5oE7si+jD64N8XgFz37dD3KqUokeQ4lrTSSOePT1K+nWIl30sqDd7YE4auz4CvSjm2wXmN31+CXW1hp3YWN2972yrUt+R5U=","Modulus":"uwMB6reAVtm/Cq0BRPZ0ozBq6g3wDh2kzqFKBf8I7u8d9p7i5ExLSrOWPupHwPr/IW1VUn2TKHrJ8OnyYhznKIRxqlxj0U3D2GXijz5kfFOoHK+mlfKaDMqweRoS0UzEz58kMlgwUoDraUj6dTHTPCVPo3TqA2ImRw50j6D+jobFrY5321EFvlirZViMPDAgB8Ca7wGCqNBcCxvIPYw1O6WZmcVmjG7umelD3XjcUIQlEbIyAmi/3gXAo7NdPmgOamla6bnSWsy429HfsNpXyCfPBzV3QS3ubpTekWPoPcOVZbWwVPYtFQbhRh8PmWATRx0cV6oePZNZGxGeJl8WYQ==","P":"wplelBfVmiOPmr6iUxtOgIzuvwSqvP6Rqmh8dhaGDiJjU8OqZ0tZhuh0G+xnMLPIHb2fMeg0dqZMJZ5iXaIi1QycYn/JKz1i4cUonJ6IIQeKKf67tvzn/BY0V0N8rJw8hVfzou+/5sRBCbiHtJ2KIN1YJQuWGFFfrZJOJzc95ss=","Q":"9gTGKoDiOdrY8kqIXJ2nMhoeNryAH4q3EUrROJ7simqc28oYlGx24Sco/wOoeB2xxrdcF5JYOlyJ7H2YY/huLvJISaw/wHLPskiKiYQ78tuNwW0ip+5ceB1dSToHcEe3sR30+OeTh0Z4ZKoqthKziFGIt3EhEgiGq1gjZuWB5gM="}} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/TemporaryFiles/.gitkeep b/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/TemporaryFiles/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/cap.png b/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/cap.png deleted file mode 100644 index 76c667e0525b3368071af5abc103685afcb1894d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11113 zcmdUV=R2HV)UJde`slq3gQ!uWmxR$GTJ+u`dM7#&Z4iVg(PK!2=)Lz&FnaWEM2$A0 zo@ajV^`3v=d^#T(<9eQ5*1Ffd?zQ%g)>Ky_#G}Q-z`!6>QHH+8z<9_D{5^n+4g6-i zM>qf<4?JHh$zha@((eLauxwwby}-bzOu)afd<1-d?51q!iGe}XjsAJi@A~5n1_sxi z3iO4Jui3#;l#kAAEB`U-z9rMc)7T|oZ@J`pnKz!nM4w(WPO{5qSY)%FO&F8(rj=BiXIUfi}|(CB*4H269s z6b6G0U{M>PKgjJIJX@y5J9Ss)Cy5UbZ<7uAT z$S?+F#Br>(Q{K1kdOa@j$?04Yf=kE2INuTQLb2MHFl}c^uDItB?z_9`Dk4lM{&qa4zE^YzUjO)`&qnr_QeLWR@UnwJQBsW09y?`nS}eX~7wbEYizp$hL- z#eAqP6Z1GY)*qELHW3RE1k-J6J|8+C16Eyi~6TJ$4IPu&j>S7QEgZGci z35SM%U}+42^4%uaba=Ef#&O_nImK|j>Z$ILN&Eg;VV(m5j$uR($=nqqORtw@rMG3q zN*lkdgt#HM@ueDfKcTWgxFrFGy@GC1U-DS>eC_~AmBPron)~*NgN-h*!b(>r`Awqgba9Y4+t+tJT2ib+eadu38ng>3jQsz^Y+?}4YJBHq6rj%tf?<}~~Ao&Hiycm0ia zLrNbjLIp{|NM^q}8At+FImzr|rQ)mxnADMbW$8@j!*6PuvDA!k23 z74C@hLm>j$z0lTP>@*u{#>`sZV-`SzaP5S3pPMZ@|4+-uQCOEWqfCTO<34YW1r5qKkz5AKSA z(ChRJLO|cCU&A;P1!_ooqh06X;#)X;&+O|2m3#7`nnkx4O(s{M>(|(hO(bVHHGuz; z=Kz?kKX-aXp~}DE9rei|rg&NReVr$*U^5?hsT!0>1^5}t-jHO`67H|iy53T0PKQ*eSTT?x9(R0YDN96y8paB`!hargd{)O=+;5hH zmJ41qa&!hw6In1}YyCXr6cy;~Y9Gn10Cb`hB7c-S)C)XtOG>Tu#Md>vq)*M~KPVX4z zJgWt=Jm@iT+7*trYmsJxJ184d5vn>hM~j7sH9y5Wo*nst0k)L57pRGFE%squeJ)5{d21ZWvbp6UtVV`DWKMg;zRZ*-G z8gJiGHAov(m`z@xd7Wy=^wsrRk)SY#04M$WSVV+bOpIBcv>PAPiU(l$qr|D>@$u(V zz#LHN--!tzkU^jCE0niX)a`4Lt9LF6sP*5-j0=&b@9M836TZBZ(v}LilB@{->Do`( z5Oc(etZ>W?hbhMa;e*}ALIvcYkA^>k-Q}wrSR^)?njtJZrH;t^ZB+w(J}9cizG5Ws zUg3x4qj&+%;Stn$s~fhHu}r7(49%s)Lal}(^sFDf$#L&mqOG6(+py}nsq34-M}YvJ zh-_eGt2eyFL$te!mT*&c2-*rZeedJ{@ncAE!_kvw)xck47e&P4waECJ6KxfSd%3ObT<_l+##TL@BEm@fLyVRlYmq6T>?hzn^~R#h+C~ zK=4?1?J1;dxZ|#TwIzToST&8n6nb_AB%lcvCgC3y;e!b4go$+PL z%+�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 diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/hangfire.png b/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/hangfire.png deleted file mode 100644 index 5cdeb40f0157e93361de3ca004454f7e840639f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36278 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 diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/healthchecks.png b/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/healthchecks.png deleted file mode 100644 index 63950e40afe4d63b6ba37d9fd6b65c684b08e474..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8990 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 Q(?X`9qODw}_$1_i0Pre=od5s; diff --git a/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/miniprofiler.png b/web/JiShe.CollectBusEPO.HttpApi.Host/wwwroot/images/miniprofiler.png deleted file mode 100644 index 244c7029ac13d24d9f11101d0d4ff550cf233dfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22147 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 P1AKI~4YbPcScm-=eWKC$ diff --git a/web/JiShe.CollectBusEPO.HttpApi/CollectBusEPOHttpApiModule.cs b/web/JiShe.CollectBusEPO.HttpApi/CollectBusEPOHttpApiModule.cs deleted file mode 100644 index 100383b..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi/CollectBusEPOHttpApiModule.cs +++ /dev/null @@ -1,39 +0,0 @@ - -using JiShe.CollectBus; -using JiShe.CollectBus.Localization; -using Microsoft.Extensions.DependencyInjection; - -namespace JiShe.CollectBusEPO -{ - [DependsOn( - typeof(CollectBusEPOApplicationContractsModule), - typeof(CollectBusEPOApplicationModule), - typeof(AbpAspNetCoreMvcModule) - )] - public class CollectBusEPOHttpApiModule : AbpModule - { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - PreConfigure(mvcBuilder => - { - mvcBuilder.AddApplicationPartIfNotExists(typeof(CollectBusEPOHttpApiModule).Assembly); - }); - - Configure(options => - { - options.ConventionalControllers - .Create(typeof(CollectBusEPOHttpApiModule).Assembly); - }); - } - - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.Resources - .Get() - .AddBaseTypes(typeof(AbpUiResource)); - }); - } - } -} diff --git a/web/JiShe.CollectBusEPO.HttpApi/Controllers/CollectBusEPOController.cs b/web/JiShe.CollectBusEPO.HttpApi/Controllers/CollectBusEPOController.cs deleted file mode 100644 index cd8ffa8..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi/Controllers/CollectBusEPOController.cs +++ /dev/null @@ -1,14 +0,0 @@ -using JiShe.CollectBus.Localization; - -namespace JiShe.CollectBusEPO.Controllers -{ - /* Inherit your controllers from this class. - */ - public abstract class CollectBusEPOController : AbpController - { - protected CollectBusEPOController() - { - LocalizationResource = typeof(CollectBusResource); - } - } -} \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi/GlobalUsings.cs b/web/JiShe.CollectBusEPO.HttpApi/GlobalUsings.cs deleted file mode 100644 index 99bd72c..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi/GlobalUsings.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Global using directives - -global using Localization.Resources.AbpUi; -global using Volo.Abp.AspNetCore.Mvc; -global using Volo.Abp.Localization; -global using Volo.Abp.Modularity; \ No newline at end of file diff --git a/web/JiShe.CollectBusEPO.HttpApi/JiShe.CollectBusEPO.HttpApi.csproj b/web/JiShe.CollectBusEPO.HttpApi/JiShe.CollectBusEPO.HttpApi.csproj deleted file mode 100644 index 52da299..0000000 --- a/web/JiShe.CollectBusEPO.HttpApi/JiShe.CollectBusEPO.HttpApi.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - net8.0 - JiShe.CollectBusEPO - - - - - - - - - - - -