From 9da1745573270a2a145c4300f085321c17a327d8 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Mon, 24 Mar 2025 20:54:31 +0800 Subject: [PATCH 01/18] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...er.cs => ScheduledMeterReadingConsumer.cs} | 12 ++-- .../JiShe.CollectBus.Application.csproj | 3 +- .../BasicScheduledMeterReadingService.cs | 7 ++- ...nergySystemScheduledMeterReadingService.cs | 62 ++++++++++++++----- .../CollectBusHostModule.Configure.cs | 10 +++ .../CollectBusHostModule.cs | 2 +- 6 files changed, 71 insertions(+), 25 deletions(-) rename src/JiShe.CollectBus.Application/Consumers/{WorkerConsumer.cs => ScheduledMeterReadingConsumer.cs} (52%) diff --git a/src/JiShe.CollectBus.Application/Consumers/WorkerConsumer.cs b/src/JiShe.CollectBus.Application/Consumers/ScheduledMeterReadingConsumer.cs similarity index 52% rename from src/JiShe.CollectBus.Application/Consumers/WorkerConsumer.cs rename to src/JiShe.CollectBus.Application/Consumers/ScheduledMeterReadingConsumer.cs index 53ad836..cdc731a 100644 --- a/src/JiShe.CollectBus.Application/Consumers/WorkerConsumer.cs +++ b/src/JiShe.CollectBus.Application/Consumers/ScheduledMeterReadingConsumer.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.Common.Models; +using JiShe.CollectBus.IotSystems.MessageIssueds; using MassTransit; using Microsoft.Extensions.Logging; using TouchSocket.Sockets; @@ -12,9 +13,9 @@ namespace JiShe.CollectBus.Consumers /// /// 定时抄读任务消费者 /// - public class WorkerConsumer : IConsumer + public class ScheduledMeterReadingConsumer : IConsumer { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly ITcpService _tcpService; /// @@ -22,7 +23,7 @@ namespace JiShe.CollectBus.Consumers /// /// /// - public WorkerConsumer(ILogger logger, + public ScheduledMeterReadingConsumer(ILogger logger, ITcpService tcpService) { _logger = logger; @@ -30,9 +31,10 @@ namespace JiShe.CollectBus.Consumers } - public async Task Consume(ConsumeContext context) + public async Task Consume(ConsumeContext context) { - await _tcpService.SendAsync(context.Message.ClientId, context.Message.Message); + _logger.LogError($"{nameof(ScheduledMeterReadingConsumer)} 集中器的消息消费{context.Message.FocusAddress}"); + await _tcpService.SendAsync(context.Message.FocusAddress, context.Message.MessageHexString); } } } diff --git a/src/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj b/src/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj index 94c4faf..078728a 100644 --- a/src/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj +++ b/src/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj @@ -15,7 +15,8 @@ - + + diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs index bb3c743..14e0a6a 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs @@ -26,6 +26,7 @@ using JiShe.CollectBus.Repository.MeterReadingRecord; using JiShe.CollectBus.Workers; using MassTransit; using MassTransit.Internals.GraphValidation; +using MassTransit.Transports; using Microsoft.Extensions.Logging; using Volo.Abp.Domain.Repositories; using static FreeSql.Internal.GlobalFilter; @@ -41,10 +42,9 @@ namespace JiShe.CollectBus.ScheduledMeterReading private readonly ICapPublisher _capBus; private readonly IMeterReadingRecordRepository _meterReadingRecordsRepository; - public BasicScheduledMeterReadingService( ILogger logger, - ICapPublisher capBus, + ICapPublisher capBus, IMeterReadingRecordRepository meterReadingRecordsRepository) { _capBus = capBus; @@ -432,8 +432,11 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; + _ = _capBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500) ,ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, tempMsg); + //await _massTransitBus.Publish(tempMsg); + meterTaskInfosList.Add(ammerterItem.Value); } } diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs index 24507fe..61cc2e5 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs @@ -12,6 +12,7 @@ using JiShe.CollectBus.IotSystems.MeterReadingRecords; using JiShe.CollectBus.IotSystems.Watermeter; using JiShe.CollectBus.Repository; using JiShe.CollectBus.Repository.MeterReadingRecord; +using MassTransit; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using Volo.Abp.Domain.Repositories; @@ -27,10 +28,10 @@ namespace JiShe.CollectBus.ScheduledMeterReading public class EnergySystemScheduledMeterReadingService : BasicScheduledMeterReadingService { - public EnergySystemScheduledMeterReadingService(ILogger logger, - ICapPublisher capBus, IMeterReadingRecordRepository _meterReadingRecordsRepository) :base(logger, capBus, _meterReadingRecordsRepository) + public EnergySystemScheduledMeterReadingService(ILogger logger, + ICapPublisher capBus, IMeterReadingRecordRepository meterReadingRecordsRepository) : base(logger, capBus, meterReadingRecordsRepository) { - + } public sealed override string SystemType => SystemTypeConst.Energy; @@ -63,21 +64,50 @@ namespace JiShe.CollectBus.ScheduledMeterReading //[Route($"ammeter/list")] public override async Task> GetAmmeterInfoList(string gatherCode = "V4-Gather-8890") { - string sql = $@"SELECT C.ID,C.Name,C.FocusID,C.SingleRate,C.MeteringCode,C.Code AS BrandType,C.Baudrate,C.Password,C.MeteringPort,C.[Address] AS AmmerterAddress,C.TypeName,C.Protocol,C.TripState,C.[State],B.[Address],B.AreaCode,B.AutomaticReport,D.DataTypes,B.TimeDensity,A.GatherCode,C.Special,C.[ProjectID],B.AbnormalState,B.LastTime,CONCAT(B.AreaCode, B.[Address]) AS FocusAddress,(select top 1 DatabaseBusiID from TB_Project where ID = B.ProjectID) AS DatabaseBusiID - FROM TB_GatherInfo(NOLOCK) AS A - INNER JOIN TB_FocusInfo(NOLOCK) AS B ON A.ID = B.GatherInfoID AND B.RemoveState >= 0 AND B.State>=0 - INNER JOIN TB_AmmeterInfo(NOLOCK) AS C ON B.ID = C.FocusID AND C.State>= 0 AND C.State<100 - INNER JOIN TB_AmmeterGatherItem(NOLOCK) AS D ON C.ID = D.AmmeterID AND D.State>=0 - WHERE 1=1 and C.Special = 0 "; - //TODO 记得移除特殊表过滤 - if (!string.IsNullOrWhiteSpace(gatherCode)) + List ammeterInfos = new List(); + ammeterInfos.Add(new AmmeterInfo() { - sql = $@"{sql} AND A.GatherCode = '{gatherCode}'"; - } - return await SqlProvider.Instance.Change(DbEnum.EnergyDB) - .Ado - .QueryAsync(sql); + Baudrate = 2400, + FocusAddress = "402440506", + Name = "三相电表", + FocusID = 1, + DatabaseBusiID = 1, + MeteringCode = 2, + AmmerterAddress = "402410040506", + ID = 9980, + TypeName = 3, + }); + ammeterInfos.Add(new AmmeterInfo() + { + Baudrate = 2400, + FocusAddress = "542400504", + Name = "单相电表", + FocusID = 1, + DatabaseBusiID = 1, + MeteringCode = 2, + AmmerterAddress = "542410000504", + ID = 9981, + TypeName = 1, + }); + + return ammeterInfos; + + //string sql = $@"SELECT C.ID,C.Name,C.FocusID,C.SingleRate,C.MeteringCode,C.Code AS BrandType,C.Baudrate,C.Password,C.MeteringPort,C.[Address] AS AmmerterAddress,C.TypeName,C.Protocol,C.TripState,C.[State],B.[Address],B.AreaCode,B.AutomaticReport,D.DataTypes,B.TimeDensity,A.GatherCode,C.Special,C.[ProjectID],B.AbnormalState,B.LastTime,CONCAT(B.AreaCode, B.[Address]) AS FocusAddress,(select top 1 DatabaseBusiID from TB_Project where ID = B.ProjectID) AS DatabaseBusiID + // FROM TB_GatherInfo(NOLOCK) AS A + // INNER JOIN TB_FocusInfo(NOLOCK) AS B ON A.ID = B.GatherInfoID AND B.RemoveState >= 0 AND B.State>=0 + // INNER JOIN TB_AmmeterInfo(NOLOCK) AS C ON B.ID = C.FocusID AND C.State>= 0 AND C.State<100 + // INNER JOIN TB_AmmeterGatherItem(NOLOCK) AS D ON C.ID = D.AmmeterID AND D.State>=0 + // WHERE 1=1 and C.Special = 0 "; + ////TODO 记得移除特殊表过滤 + + //if (!string.IsNullOrWhiteSpace(gatherCode)) + //{ + // sql = $@"{sql} AND A.GatherCode = '{gatherCode}'"; + //} + //return await SqlProvider.Instance.Change(DbEnum.EnergyDB) + // .Ado + // .QueryAsync(sql); } /// diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs index 6ffa1d0..374eaa5 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs @@ -20,6 +20,7 @@ using JiShe.CollectBus.Plugins; using JiShe.CollectBus.Consumers; using JiShe.CollectBus.Protocol.Contracts; using JiShe.CollectBus.IotSystems.MessageReceiveds; +using JiShe.CollectBus.IotSystems.MessageIssueds; namespace JiShe.CollectBus.Host @@ -302,6 +303,9 @@ namespace JiShe.CollectBus.Host .SetTimeLimitStart(BatchTimeLimitStart.FromLast) .SetConcurrencyLimit(10)); }); + + rider.AddConsumer(); + rider.UsingKafka((c, cfg) => { cfg.Host(configuration.GetConnectionString("Kafka")); @@ -329,6 +333,12 @@ namespace JiShe.CollectBus.Host configurator.ConfigureConsumer(c); configurator.ConfigureConsumeTopology = false; }); + + //cfg.TopicEndpoint(ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, ProtocolConst.SubscriberGroup, configurator => + //{ + // configurator.ConfigureConsumer(c); + // configurator.ConfigureConsumeTopology = false; + //}); }); }); }); diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.cs index 933641b..6f24b1e 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.cs @@ -25,7 +25,7 @@ namespace JiShe.CollectBus.Host typeof(AbpAspNetCoreSerilogModule), typeof(AbpSwashbuckleModule), typeof(CollectBusApplicationModule), - typeof(CollectBusMongoDbModule), + typeof(CollectBusMongoDbModule), typeof(AbpCachingStackExchangeRedisModule), typeof(AbpBackgroundWorkersHangfireModule) )] From af9b0d8a77b08857014ae1ba419b27208eabf555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=9B=8A?= Date: Mon, 24 Mar 2025 21:55:22 +0800 Subject: [PATCH 02/18] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=A1=A8=E5=88=86?= =?UTF-8?q?=E7=89=87=E7=AD=96=E7=95=A5=E7=BB=9F=E4=B8=80=E5=87=BA=E5=8F=A3?= =?UTF-8?q?=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Subscribers/SubscriberAppService.cs | 53 +++++++++++++++---- .../Extensions/DateTimeExtensions.cs | 10 ++++ .../ShardingStrategy/DayShardingStrategy.cs | 9 ++-- .../Abstracts/BaseProtocolPlugin.cs | 2 +- .../Interfaces/IProtocolPlugin.cs | 2 +- 5 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs index fc05bc7..7dbe586 100644 --- a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs +++ b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs @@ -1,7 +1,10 @@ using System; +using System.Linq; using System.Threading.Tasks; using DotNetCore.CAP; using JiShe.CollectBus.Common.Enums; +using JiShe.CollectBus.Common.Extensions; +using JiShe.CollectBus.Common.Helpers; using JiShe.CollectBus.Common.Models; using JiShe.CollectBus.IotSystems.Devices; using JiShe.CollectBus.IotSystems.MessageReceiveds; @@ -9,6 +12,7 @@ using JiShe.CollectBus.IotSystems.MeterReadingRecords; using JiShe.CollectBus.Protocol.Contracts; using JiShe.CollectBus.Protocol.Contracts.Interfaces; using JiShe.CollectBus.Protocol.Contracts.Models; +using JiShe.CollectBus.Repository.MeterReadingRecord; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using TouchSocket.Sockets; @@ -16,16 +20,16 @@ using Volo.Abp.Domain.Repositories; namespace JiShe.CollectBus.Subscribers { - public class SubscriberAppService : CollectBusAppService, ISubscriberAppService,ICapSubscribe + public class SubscriberAppService : CollectBusAppService, ISubscriberAppService, ICapSubscribe { private readonly ILogger _logger; private readonly ITcpService _tcpService; private readonly IServiceProvider _serviceProvider; private readonly IRepository _messageReceivedLoginEventRepository; private readonly IRepository _messageReceivedHeartbeatEventRepository; - private readonly IRepository _messageReceivedEventRepository; + private readonly IRepository _messageReceivedEventRepository; private readonly IRepository _deviceRepository; - private readonly IRepository _meterReadingRecordsRepository; + private readonly IMeterReadingRecordRepository _meterReadingRecordsRepository; /// /// Initializes a new instance of the class. @@ -38,12 +42,12 @@ namespace JiShe.CollectBus.Subscribers /// The message received event repository. /// The device repository. /// The device repository. - public SubscriberAppService(ILogger logger, - ITcpService tcpService, IServiceProvider serviceProvider, - IRepository messageReceivedLoginEventRepository, - IRepository messageReceivedHeartbeatEventRepository, - IRepository messageReceivedEventRepository, - IRepository deviceRepository, IRepository meterReadingRecordsRepository) + public SubscriberAppService(ILogger logger, + ITcpService tcpService, IServiceProvider serviceProvider, + IRepository messageReceivedLoginEventRepository, + IRepository messageReceivedHeartbeatEventRepository, + IRepository messageReceivedEventRepository, + IRepository deviceRepository, IMeterReadingRecordRepository meterReadingRecordsRepository) { _logger = logger; _tcpService = tcpService; @@ -79,7 +83,7 @@ namespace JiShe.CollectBus.Subscribers throw new ArgumentOutOfRangeException(); } var device = await _deviceRepository.FindAsync(a => a.Number == issuedEventMessage.DeviceNo); - if (device!=null) + if (device != null) { await _tcpService.SendAsync(device.ClientId, issuedEventMessage.Message); } @@ -96,7 +100,34 @@ namespace JiShe.CollectBus.Subscribers else { //todo 会根据不同的协议进行解析,然后做业务处理 - TB3761FN fN = await protocolPlugin.AnalyzeAsync(receivedMessage); + TB3761 fN = await protocolPlugin.AnalyzeAsync(receivedMessage); + if(fN == null) + { + Logger.LogError($"数据解析失败:{receivedMessage.Serialize()}"); + return; + } + var tb3761FN = fN.FnList.FirstOrDefault(); + if (tb3761FN == null) + { + Logger.LogError($"数据解析失败:{receivedMessage.Serialize()}"); + return; + } + + //todo 查找是否有下发任务 + + + + await _meterReadingRecordsRepository.InsertAsync(new MeterReadingRecords() + { + ReceivedMessageHexString = receivedMessage.MessageHexString, + AFN = fN.Afn, + Fn = tb3761FN.Fn, + Pn = 0, + FocusAddress = "", + MeterAddress = "", + DataResult = tb3761FN.Text, + }); + //await _messageReceivedEventRepository.InsertAsync(receivedMessage); } } diff --git a/src/JiShe.CollectBus.Common/Extensions/DateTimeExtensions.cs b/src/JiShe.CollectBus.Common/Extensions/DateTimeExtensions.cs index b12778f..904d4b9 100644 --- a/src/JiShe.CollectBus.Common/Extensions/DateTimeExtensions.cs +++ b/src/JiShe.CollectBus.Common/Extensions/DateTimeExtensions.cs @@ -167,5 +167,15 @@ namespace JiShe.CollectBus.Common.Extensions ) ); } + + /// + /// 获取数据表分片策略 + /// + /// + /// + public static string GetDataTableShardingStrategy(this DateTime dateTime) + { + return $"{dateTime:yyyyMMddHHmm}"; + } } } diff --git a/src/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs b/src/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs index 75157e5..f26136d 100644 --- a/src/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs +++ b/src/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs @@ -1,4 +1,5 @@ -using System; +using JiShe.CollectBus.Common.Extensions; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -21,7 +22,7 @@ namespace JiShe.CollectBus.ShardingStrategy public string GetCollectionName(DateTime dateTime) { var baseName = typeof(TEntity).Name; - return $"{baseName}_{dateTime:yyyyMMddHHmm}"; + return $"{baseName}_{dateTime.GetDataTableShardingStrategy()}"; } /// @@ -31,7 +32,7 @@ namespace JiShe.CollectBus.ShardingStrategy public string GetCurrentCollectionName() { var baseName = typeof(TEntity).Name; - return $"{baseName}_{DateTime.Now:yyyyMMddHHmm}"; + return $"{baseName}_{DateTime.Now.GetDataTableShardingStrategy()}"; } /// @@ -49,7 +50,7 @@ namespace JiShe.CollectBus.ShardingStrategy while (current <= end) { - months.Add($"{baseName}_{current:yyyyMMddHHmm}"); + months.Add($"{baseName}_{current.GetDataTableShardingStrategy()}"); current = current.AddMonths(1); } diff --git a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs index 609dcdb..4638983 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs @@ -55,7 +55,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts //await _protocolInfoCache.Get() } - public abstract Task AnalyzeAsync(MessageReceived messageReceived, Action? sendAction = null) where T : TB3761FN; + public abstract Task AnalyzeAsync(MessageReceived messageReceived, Action? sendAction = null) where T : TB3761; /// /// 登录帧解析 diff --git a/src/JiShe.CollectBus.Protocol.Contracts/Interfaces/IProtocolPlugin.cs b/src/JiShe.CollectBus.Protocol.Contracts/Interfaces/IProtocolPlugin.cs index 5ad92c1..2f48cd2 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/Interfaces/IProtocolPlugin.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/Interfaces/IProtocolPlugin.cs @@ -14,7 +14,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Interfaces Task AddAsync(); - Task AnalyzeAsync(MessageReceived messageReceived, Action? sendAction = null) where T : TB3761FN; + Task AnalyzeAsync(MessageReceived messageReceived, Action? sendAction = null) where T : TB3761; Task LoginAsync(MessageReceivedLogin messageReceived); From a9b8323a3ad453658cb3a4f2220d025947bf3330 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Thu, 27 Mar 2025 08:38:19 +0800 Subject: [PATCH 03/18] =?UTF-8?q?=E6=9A=82=E5=AD=98=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Consumers/ReceivedConsumer.cs | 2 +- .../JiShe.CollectBus.Application.csproj | 6 +- .../Plugins/CloseMonitor.cs | 14 +-- .../Plugins/ServerMonitor.cs | 4 +- .../Plugins/TcpMonitor.cs | 114 +++++++++++------- .../Plugins/UdpMonitor.cs | 3 +- .../BasicScheduledMeterReadingService.cs | 2 +- ...nergySystemScheduledMeterReadingService.cs | 46 +++---- .../Subscribers/SubscriberAppService.cs | 16 ++- .../IotSystems/Devices/Device.cs | 6 + .../JiShe.CollectBus.Host.csproj | 4 +- src/JiShe.CollectBus.Host/appsettings.json | 6 +- .../Abstracts/BaseProtocolPlugin.cs | 8 +- 13 files changed, 138 insertions(+), 93 deletions(-) diff --git a/src/JiShe.CollectBus.Application/Consumers/ReceivedConsumer.cs b/src/JiShe.CollectBus.Application/Consumers/ReceivedConsumer.cs index 9309c55..4f7b5eb 100644 --- a/src/JiShe.CollectBus.Application/Consumers/ReceivedConsumer.cs +++ b/src/JiShe.CollectBus.Application/Consumers/ReceivedConsumer.cs @@ -49,7 +49,7 @@ namespace JiShe.CollectBus.Consumers var list = new List(); foreach (var contextItem in context.Message) { - await protocolPlugin.AnalyzeAsync(contextItem.Message); + await protocolPlugin.AnalyzeAsync(contextItem.Message); list.Add(contextItem.Message); } await _messageReceivedEventRepository.InsertManyAsync(list); diff --git a/src/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj b/src/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj index 078728a..6b71b26 100644 --- a/src/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj +++ b/src/JiShe.CollectBus.Application/JiShe.CollectBus.Application.csproj @@ -15,14 +15,14 @@ - + - - + + diff --git a/src/JiShe.CollectBus.Application/Plugins/CloseMonitor.cs b/src/JiShe.CollectBus.Application/Plugins/CloseMonitor.cs index 19b66d8..f8651c7 100644 --- a/src/JiShe.CollectBus.Application/Plugins/CloseMonitor.cs +++ b/src/JiShe.CollectBus.Application/Plugins/CloseMonitor.cs @@ -7,10 +7,10 @@ using TouchSocket.Sockets; namespace JiShe.CollectBus.Plugins { - public partial class TcpCloseMonitor(ILogger logger) : PluginBase + public partial class TcpCloseMonitor(ILogger logger) : PluginBase, ITcpReceivedPlugin { - [GeneratorPlugin(typeof(ITcpReceivedPlugin))] - public async Task OnTcpReceived(ITcpSessionClient client, ReceivedDataEventArgs e) + + public async Task OnTcpReceived(ITcpSession client, ReceivedDataEventArgs e) { try { @@ -19,21 +19,21 @@ namespace JiShe.CollectBus.Plugins catch (CloseException ex) { logger.LogInformation("拦截到CloseException"); - client.Close(ex.Message); + await client.CloseAsync(ex.Message); } catch (Exception exx) { - // ignored + } finally { + } } } - public partial class UdpCloseMonitor(ILogger logger) : PluginBase + public partial class UdpCloseMonitor(ILogger logger) : PluginBase, IUdpReceivedPlugin { - [GeneratorPlugin(typeof(IUdpReceivedPlugin))] public Task OnUdpReceived(IUdpSessionBase client, UdpReceivedDataEventArgs e) { throw new NotImplementedException(); diff --git a/src/JiShe.CollectBus.Application/Plugins/ServerMonitor.cs b/src/JiShe.CollectBus.Application/Plugins/ServerMonitor.cs index 39174db..2b3c15a 100644 --- a/src/JiShe.CollectBus.Application/Plugins/ServerMonitor.cs +++ b/src/JiShe.CollectBus.Application/Plugins/ServerMonitor.cs @@ -5,9 +5,8 @@ using TouchSocket.Sockets; namespace JiShe.CollectBus.Plugins { - public partial class ServerMonitor(ILogger logger) : PluginBase + public partial class ServerMonitor(ILogger logger) : PluginBase, IServerStartedPlugin, IServerStopedPlugin { - [GeneratorPlugin(typeof(IServerStartedPlugin))] public Task OnServerStarted(IServiceBase sender, ServiceStateEventArgs e) { switch (sender) @@ -32,7 +31,6 @@ namespace JiShe.CollectBus.Plugins return e.InvokeNext(); } - [GeneratorPlugin(typeof(IServerStopedPlugin))] public Task OnServerStoped(IServiceBase sender,ServiceStateEventArgs e) { logger.LogInformation("服务已停止"); diff --git a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs index 8ff138a..b3b43d3 100644 --- a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs +++ b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs @@ -1,5 +1,7 @@ using System; +using System.Runtime.CompilerServices; using System.Threading.Tasks; +using DeviceDetectorNET.Parser.Device; using DotNetCore.CAP; using JiShe.CollectBus.Ammeters; using JiShe.CollectBus.Common.Enums; @@ -20,7 +22,7 @@ using Volo.Abp.Domain.Repositories; namespace JiShe.CollectBus.Plugins { - public partial class TcpMonitor : PluginBase, ITransientDependency + public partial class TcpMonitor : PluginBase, ITransientDependency, ITcpReceivedPlugin, ITcpConnectingPlugin, ITcpConnectedPlugin, ITcpClosedPlugin { private readonly ICapPublisher _capBus; private readonly ILogger _logger; @@ -34,9 +36,9 @@ namespace JiShe.CollectBus.Plugins /// /// /// - public TcpMonitor(ICapPublisher capBus, - ILogger logger, - IRepository deviceRepository, + public TcpMonitor(ICapPublisher capBus, + ILogger logger, + IRepository deviceRepository, IDistributedCache ammeterInfoCache) { _capBus = capBus; @@ -45,8 +47,7 @@ namespace JiShe.CollectBus.Plugins _ammeterInfoCache = ammeterInfoCache; } - [GeneratorPlugin(typeof(ITcpReceivedPlugin))] - public async Task OnTcpReceived(ITcpSessionClient client, ReceivedDataEventArgs e) + public async Task OnTcpReceived(ITcpSession client, ReceivedDataEventArgs e) { var messageHexString = Convert.ToHexString(e.ByteBlock.Span); var hexStringList = messageHexString.StringToPairs(); @@ -55,15 +56,21 @@ namespace JiShe.CollectBus.Plugins var aTuple = (Tuple)hexStringList.GetAnalyzeValue(CommandChunkEnum.A); if (aFn.HasValue && fn.HasValue && aTuple != null && !string.IsNullOrWhiteSpace(aTuple.Item1)) { + var tcpSessionClient = (ITcpSessionClient)client; + + if ((AFN)aFn == AFN.链路接口检测) { switch (fn) { case 1: - await OnTcpLoginReceived(client, messageHexString, aTuple.Item1); + await OnTcpLoginReceived(tcpSessionClient, messageHexString, aTuple.Item1); break; case 3: - await OnTcpHeartbeatReceived(client, messageHexString, aTuple.Item1); + //心跳帧有两种情况: + //1. 集中器先有登录帧,再有心跳帧 + //2. 集中器没有登录帧,只有心跳帧 + await OnTcpHeartbeatReceived(tcpSessionClient, messageHexString, aTuple.Item1); break; default: _logger.LogError($"指令初步解析失败,指令内容:{messageHexString}"); @@ -72,7 +79,7 @@ namespace JiShe.CollectBus.Plugins } else { - await OnTcpNormalReceived(client, messageHexString, aTuple.Item1); + await OnTcpNormalReceived(tcpSessionClient, messageHexString, aTuple.Item1); } } else @@ -83,24 +90,31 @@ namespace JiShe.CollectBus.Plugins await e.InvokeNext(); } - [GeneratorPlugin(typeof(ITcpConnectingPlugin))] - public async Task OnTcpConnecting(ITcpSessionClient client, ConnectingEventArgs e) + //[GeneratorPlugin(typeof(ITcpConnectingPlugin))] + public async Task OnTcpConnecting(ITcpSession client, ConnectingEventArgs e) { - _logger.LogInformation($"[TCP] ID:{client.Id} IP:{client.GetIPPort()}正在连接中..."); + var tcpSessionClient = (ITcpSessionClient)client; + + _logger.LogInformation($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}正在连接中..."); await e.InvokeNext(); } - [GeneratorPlugin(typeof(ITcpConnectedPlugin))] - public async Task OnTcpConnected(ITcpSessionClient client, ConnectedEventArgs e) + //[GeneratorPlugin(typeof(ITcpConnectedPlugin))] + public async Task OnTcpConnected(ITcpSession client, ConnectedEventArgs e) { - _logger.LogInformation($"[TCP] ID:{client.Id} IP:{client.GetIPPort()}已连接"); + var tcpSessionClient = (ITcpSessionClient)client; + + + _logger.LogInformation($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}已连接"); await e.InvokeNext(); } - [GeneratorPlugin(typeof(ITcpClosedPlugin))] - public async Task OnTcpClosed(ITcpSessionClient client, ClosedEventArgs e) + //[GeneratorPlugin(typeof(ITcpClosedPlugin))]//ITcpSessionClient + public async Task OnTcpClosed(ITcpSession client, ClosedEventArgs e) { - var entity = await _deviceRepository.FindAsync(a=>a.ClientId == client.Id); + + var tcpSessionClient = (ITcpSessionClient)client; + var entity = await _deviceRepository.FindAsync(a => a.ClientId == tcpSessionClient.Id); if (entity != null) { entity.UpdateByOnClosed(); @@ -108,7 +122,7 @@ namespace JiShe.CollectBus.Plugins } else { - _logger.LogWarning($"[TCP] ID:{client.Id} IP:{client.GetIPPort()}已关闭连接,但采集程序检索失败"); + _logger.LogWarning($"[TCP] ID:{tcpSessionClient.Id} IP:{client.GetIPPort()}已关闭连接,但采集程序检索失败"); } await e.InvokeNext(); @@ -123,9 +137,24 @@ namespace JiShe.CollectBus.Plugins /// private async Task OnTcpLoginReceived(ITcpSessionClient client, string messageHexString, string deviceNo) { + string oldClientId = $"{client.Id}"; + + await client.ResetIdAsync(deviceNo); + + var entity = await _deviceRepository.FindAsync(a => a.Number == deviceNo); + if (entity == null) + { + await _deviceRepository.InsertAsync(new Device(deviceNo, oldClientId, DateTime.Now, DateTime.Now, DeviceStatus.Online)); + } + else + { + entity.UpdateByLoginAndHeartbeat(oldClientId); + await _deviceRepository.UpdateAsync(entity); + } + var messageReceivedLoginEvent = new MessageReceivedLogin { - ClientId = client.Id, + ClientId = deviceNo, ClientIp = client.IP, ClientPort = client.Port, MessageHexString = messageHexString, @@ -133,23 +162,36 @@ namespace JiShe.CollectBus.Plugins MessageId = NewId.NextGuid().ToString() }; await _capBus.PublishAsync(ProtocolConst.SubscriberReceivedLoginEventName, messageReceivedLoginEvent); - var entity = await _deviceRepository.FindAsync(a => a.Number == deviceNo); - if (entity == null) - { - await _deviceRepository.InsertAsync(new Device(deviceNo, client.Id,DateTime.Now, DateTime.Now, DeviceStatus.Online)); - } - else - { - entity.UpdateByLoginAndHeartbeat(client.Id); - await _deviceRepository.UpdateAsync(entity); - } } private async Task OnTcpHeartbeatReceived(ITcpSessionClient client, string messageHexString, string deviceNo) { + string clientId = deviceNo; + string oldClientId = $"{client.Id}"; + + var entity = await _deviceRepository.FindAsync(a => a.Number == deviceNo); + if (entity == null) //没有登录帧的设备,只有心跳帧 + { + await client.ResetIdAsync(clientId); + await _deviceRepository.InsertAsync(new Device(deviceNo, oldClientId, DateTime.Now, DateTime.Now, DeviceStatus.Online)); + } + else + { + if (clientId != oldClientId) + { + entity.UpdateByLoginAndHeartbeat(oldClientId); + } + else + { + entity.UpdateByLoginAndHeartbeat(); + } + + await _deviceRepository.UpdateAsync(entity); + } + var messageReceivedHeartbeatEvent = new MessageReceivedHeartbeat { - ClientId = client.Id, + ClientId = clientId, ClientIp = client.IP, ClientPort = client.Port, MessageHexString = messageHexString, @@ -157,16 +199,6 @@ namespace JiShe.CollectBus.Plugins MessageId = NewId.NextGuid().ToString() }; await _capBus.PublishAsync(ProtocolConst.SubscriberReceivedHeartbeatEventName, messageReceivedHeartbeatEvent); - var entity = await _deviceRepository.FindAsync(a => a.Number == deviceNo); - if (entity == null) - { - await _deviceRepository.InsertAsync(new Device(deviceNo, client.Id, DateTime.Now, DateTime.Now, DeviceStatus.Online)); - } - else - { - entity.UpdateByLoginAndHeartbeat(client.Id); - await _deviceRepository.UpdateAsync(entity); - } } private async Task OnTcpNormalReceived(ITcpSessionClient client, string messageHexString, string deviceNo) diff --git a/src/JiShe.CollectBus.Application/Plugins/UdpMonitor.cs b/src/JiShe.CollectBus.Application/Plugins/UdpMonitor.cs index f975a03..b6a576c 100644 --- a/src/JiShe.CollectBus.Application/Plugins/UdpMonitor.cs +++ b/src/JiShe.CollectBus.Application/Plugins/UdpMonitor.cs @@ -5,9 +5,8 @@ using TouchSocket.Sockets; namespace JiShe.CollectBus.Plugins { - public partial class UdpMonitor : PluginBase + public partial class UdpMonitor : PluginBase, IUdpReceivedPlugin { - [GeneratorPlugin(typeof(IUdpReceivedPlugin))] public async Task OnUdpReceived(IUdpSessionBase client, UdpReceivedDataEventArgs e) { var udpSession = client as UdpSession; diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs index 14e0a6a..b1ea2d6 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs @@ -200,7 +200,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading foreach (var ammeter in item) { //处理ItemCode - if (string.IsNullOrWhiteSpace(ammeter.ItemCodes)) + if (string.IsNullOrWhiteSpace(ammeter.ItemCodes) && !string.IsNullOrWhiteSpace(ammeter.DataTypes)) { var itemArr = ammeter.DataTypes.Split(',').ToList(); diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs index 61cc2e5..70cecfa 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs @@ -70,44 +70,48 @@ namespace JiShe.CollectBus.ScheduledMeterReading { Baudrate = 2400, FocusAddress = "402440506", - Name = "三相电表", - FocusID = 1, + Name = "张家祠工务(三相电表)", + FocusID = 95780, DatabaseBusiID = 1, - MeteringCode = 2, + MeteringCode = 1, AmmerterAddress = "402410040506", - ID = 9980, + ID = 127035, TypeName = 3, + DataTypes = "449,503,581,582,583,584,585,586,587,588,589,590,591,592,593,594,597,598,599,600,601,602,603,604,605,606,607,608,661,663,677,679", + TimeDensity = 15, }); ammeterInfos.Add(new AmmeterInfo() { Baudrate = 2400, FocusAddress = "542400504", - Name = "单相电表", - FocusID = 1, + Name = "五号配(长芦二所四排)(单相电表)", + FocusID = 69280, DatabaseBusiID = 1, MeteringCode = 2, AmmerterAddress = "542410000504", - ID = 9981, + ID = 95594, TypeName = 1, + DataTypes = "581,589,592,597,601", + TimeDensity = 15, }); return ammeterInfos; - //string sql = $@"SELECT C.ID,C.Name,C.FocusID,C.SingleRate,C.MeteringCode,C.Code AS BrandType,C.Baudrate,C.Password,C.MeteringPort,C.[Address] AS AmmerterAddress,C.TypeName,C.Protocol,C.TripState,C.[State],B.[Address],B.AreaCode,B.AutomaticReport,D.DataTypes,B.TimeDensity,A.GatherCode,C.Special,C.[ProjectID],B.AbnormalState,B.LastTime,CONCAT(B.AreaCode, B.[Address]) AS FocusAddress,(select top 1 DatabaseBusiID from TB_Project where ID = B.ProjectID) AS DatabaseBusiID - // FROM TB_GatherInfo(NOLOCK) AS A - // INNER JOIN TB_FocusInfo(NOLOCK) AS B ON A.ID = B.GatherInfoID AND B.RemoveState >= 0 AND B.State>=0 - // INNER JOIN TB_AmmeterInfo(NOLOCK) AS C ON B.ID = C.FocusID AND C.State>= 0 AND C.State<100 - // INNER JOIN TB_AmmeterGatherItem(NOLOCK) AS D ON C.ID = D.AmmeterID AND D.State>=0 - // WHERE 1=1 and C.Special = 0 "; - ////TODO 记得移除特殊表过滤 + string sql = $@"SELECT C.ID,C.Name,C.FocusID,C.SingleRate,C.MeteringCode,C.Code AS BrandType,C.Baudrate,C.Password,C.MeteringPort,C.[Address] AS AmmerterAddress,C.TypeName,C.Protocol,C.TripState,C.[State],B.[Address],B.AreaCode,B.AutomaticReport,D.DataTypes,B.TimeDensity,A.GatherCode,C.Special,C.[ProjectID],B.AbnormalState,B.LastTime,CONCAT(B.AreaCode, B.[Address]) AS FocusAddress,(select top 1 DatabaseBusiID from TB_Project where ID = B.ProjectID) AS DatabaseBusiID + FROM TB_GatherInfo(NOLOCK) AS A + INNER JOIN TB_FocusInfo(NOLOCK) AS B ON A.ID = B.GatherInfoID AND B.RemoveState >= 0 AND B.State>=0 + INNER JOIN TB_AmmeterInfo(NOLOCK) AS C ON B.ID = C.FocusID AND C.State>= 0 AND C.State<100 + INNER JOIN TB_AmmeterGatherItem(NOLOCK) AS D ON C.ID = D.AmmeterID AND D.State>=0 + WHERE 1=1 and C.Special = 0 "; + //TODO 记得移除特殊表过滤 - //if (!string.IsNullOrWhiteSpace(gatherCode)) - //{ - // sql = $@"{sql} AND A.GatherCode = '{gatherCode}'"; - //} - //return await SqlProvider.Instance.Change(DbEnum.EnergyDB) - // .Ado - // .QueryAsync(sql); + if (!string.IsNullOrWhiteSpace(gatherCode)) + { + sql = $@"{sql} AND A.GatherCode = '{gatherCode}'"; + } + return await SqlProvider.Instance.Change(DbEnum.EnergyDB) + .Ado + .QueryAsync(sql); } /// diff --git a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs index 7dbe586..b2f7400 100644 --- a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs +++ b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using DeviceDetectorNET.Parser.Device; using DotNetCore.CAP; using JiShe.CollectBus.Common.Enums; using JiShe.CollectBus.Common.Extensions; @@ -62,6 +63,8 @@ namespace JiShe.CollectBus.Subscribers [CapSubscribe(ProtocolConst.SubscriberIssuedEventName)] public async Task IssuedEvent(IssuedEventMessage issuedEventMessage) { + _logger.LogWarning($"集中器地址{issuedEventMessage.ClientId} 回复下发内容IssuedEvent:{issuedEventMessage.MessageId}"); + switch (issuedEventMessage.Type) { case IssuedEventType.Heartbeat: @@ -82,11 +85,14 @@ namespace JiShe.CollectBus.Subscribers default: throw new ArgumentOutOfRangeException(); } - var device = await _deviceRepository.FindAsync(a => a.Number == issuedEventMessage.DeviceNo); - if (device != null) - { - await _tcpService.SendAsync(device.ClientId, issuedEventMessage.Message); - } + + //var device = await _deviceRepository.FindAsync(a => a.Number == issuedEventMessage.DeviceNo); + //if (device != null) + //{ + // await _tcpService.SendAsync(device.ClientId, issuedEventMessage.Message); + //} + + await _tcpService.SendAsync(issuedEventMessage.ClientId, issuedEventMessage.Message); } [CapSubscribe(ProtocolConst.SubscriberReceivedEventName)] diff --git a/src/JiShe.CollectBus.Domain/IotSystems/Devices/Device.cs b/src/JiShe.CollectBus.Domain/IotSystems/Devices/Device.cs index c31dbc9..a18c9d8 100644 --- a/src/JiShe.CollectBus.Domain/IotSystems/Devices/Device.cs +++ b/src/JiShe.CollectBus.Domain/IotSystems/Devices/Device.cs @@ -64,6 +64,12 @@ namespace JiShe.CollectBus.IotSystems.Devices Status = DeviceStatus.Online; } + public void UpdateByLoginAndHeartbeat() + { + LastOnlineTime = DateTime.Now; + Status = DeviceStatus.Online; + } + public void UpdateByOnClosed() { LastOfflineTime = DateTime.Now; diff --git a/src/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj b/src/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj index 0b178ee..b70752b 100644 --- a/src/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj +++ b/src/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj @@ -25,8 +25,8 @@ - - + + diff --git a/src/JiShe.CollectBus.Host/appsettings.json b/src/JiShe.CollectBus.Host/appsettings.json index 50c459b..69bdcc1 100644 --- a/src/JiShe.CollectBus.Host/appsettings.json +++ b/src/JiShe.CollectBus.Host/appsettings.json @@ -5,11 +5,11 @@ "Serilog.Sinks.File" ], "MinimumLevel": { - "Default": "Warning", + "Default": "Information", "Override": { - "Microsoft": "Information", + "Microsoft": "Warning", "Volo.Abp": "Warning", - "Hangfire": "Information", + "Hangfire": "Warning", "DotNetCore.CAP": "Warning", "Serilog.AspNetCore": "Information", "Microsoft.EntityFrameworkCore": "Warning", diff --git a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs index 4638983..978a056 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs @@ -602,7 +602,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts /// /// /// - public virtual TB3761FN AnalyzeReadingDataAsync(MessageReceived messageReceived, + public virtual TB3761 AnalyzeReadingDataAsync(MessageReceived messageReceived, Action? sendAction = null) { var hexStringList = messageReceived.MessageHexString.StringToPairs(); @@ -664,7 +664,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts } } - return tb3761Fn; + return tb3761; } /// @@ -673,7 +673,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts /// /// /// - public virtual TB3761FN AnalyzeReadingTdcDataAsync(MessageReceived messageReceived, + public virtual TB3761 AnalyzeReadingTdcDataAsync(MessageReceived messageReceived, Action? sendAction = null) { @@ -717,7 +717,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts } } - return tb3761Fn; + return tb3761; //var freezeDensity = (FreezeDensity)Convert.ToInt32(hexDatas.Skip(5).Take(1)); //var addMinute = 0; //switch (freezeDensity) From 5a294b437c97721f1704315c1c07bdbe29f895da Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Tue, 1 Apr 2025 22:50:34 +0800 Subject: [PATCH 04/18] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Plugins/TcpMonitor.cs | 27 ++++++++++---- .../BasicScheduledMeterReadingService.cs | 35 +++++++++++++------ ...nergySystemScheduledMeterReadingService.cs | 2 +- .../Subscribers/SubscriberAppService.cs | 7 ++-- .../CollectBusHostModule.Configure.cs | 24 ++++++++----- .../CollectBusHostModule.cs | 4 +-- .../Abstracts/BaseProtocolPlugin.cs | 12 ++++--- 7 files changed, 73 insertions(+), 38 deletions(-) diff --git a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs index b3b43d3..4801404 100644 --- a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs +++ b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs @@ -24,7 +24,7 @@ namespace JiShe.CollectBus.Plugins { public partial class TcpMonitor : PluginBase, ITransientDependency, ITcpReceivedPlugin, ITcpConnectingPlugin, ITcpConnectedPlugin, ITcpClosedPlugin { - private readonly ICapPublisher _capBus; + private readonly IPublishEndpoint _producerBus; private readonly ILogger _logger; private readonly IRepository _deviceRepository; private readonly IDistributedCache _ammeterInfoCache; @@ -32,16 +32,16 @@ namespace JiShe.CollectBus.Plugins /// /// /// - /// + /// /// /// /// - public TcpMonitor(ICapPublisher capBus, + public TcpMonitor(IPublishEndpoint producerBus, ILogger logger, IRepository deviceRepository, IDistributedCache ammeterInfoCache) { - _capBus = capBus; + _producerBus = producerBus; _logger = logger; _deviceRepository = deviceRepository; _ammeterInfoCache = ammeterInfoCache; @@ -161,7 +161,9 @@ namespace JiShe.CollectBus.Plugins DeviceNo = deviceNo, MessageId = NewId.NextGuid().ToString() }; - await _capBus.PublishAsync(ProtocolConst.SubscriberReceivedLoginEventName, messageReceivedLoginEvent); + //await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedLoginEventName, messageReceivedLoginEvent); + + await _producerBus.Publish( messageReceivedLoginEvent); } private async Task OnTcpHeartbeatReceived(ITcpSessionClient client, string messageHexString, string deviceNo) @@ -198,12 +200,13 @@ namespace JiShe.CollectBus.Plugins DeviceNo = deviceNo, MessageId = NewId.NextGuid().ToString() }; - await _capBus.PublishAsync(ProtocolConst.SubscriberReceivedHeartbeatEventName, messageReceivedHeartbeatEvent); + //await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedHeartbeatEventName, messageReceivedHeartbeatEvent); + await _producerBus.Publish(messageReceivedHeartbeatEvent); } private async Task OnTcpNormalReceived(ITcpSessionClient client, string messageHexString, string deviceNo) { - await _capBus.PublishAsync(ProtocolConst.SubscriberReceivedEventName, new MessageReceived + await _producerBus.Publish(new MessageReceived { ClientId = client.Id, ClientIp = client.IP, @@ -212,6 +215,16 @@ namespace JiShe.CollectBus.Plugins DeviceNo = deviceNo, MessageId = NewId.NextGuid().ToString() }); + + //await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedEventName, new MessageReceived + //{ + // ClientId = client.Id, + // ClientIp = client.IP, + // ClientPort = client.Port, + // MessageHexString = messageHexString, + // DeviceNo = deviceNo, + // MessageId = NewId.NextGuid().ToString() + //}); } } } diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs index b1ea2d6..f65e51f 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs @@ -39,15 +39,15 @@ namespace JiShe.CollectBus.ScheduledMeterReading public abstract class BasicScheduledMeterReadingService : CollectBusAppService, IScheduledMeterReadingService { private readonly ILogger _logger; - private readonly ICapPublisher _capBus; + private readonly IPublishEndpoint _producerBus; private readonly IMeterReadingRecordRepository _meterReadingRecordsRepository; public BasicScheduledMeterReadingService( ILogger logger, - ICapPublisher capBus, + IPublishEndpoint producerBus, IMeterReadingRecordRepository meterReadingRecordsRepository) { - _capBus = capBus; + _producerBus = producerBus; _logger = logger; _meterReadingRecordsRepository = meterReadingRecordsRepository; } @@ -298,8 +298,10 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; - _ = _capBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500), ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); - + //_ = _producerBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500), ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + _= _producerBus.Publish(tempMsg); + + meterTaskInfosList.Add(ammerterItem.Value); } } @@ -363,7 +365,9 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; - _= _capBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500), ProtocolConst.AmmeterSubscriberWorkerFiveMinuteIssuedEventName, tempMsg); + //_= _producerBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500), ProtocolConst.AmmeterSubscriberWorkerFiveMinuteIssuedEventName, tempMsg); + + _ = _producerBus.Publish(tempMsg); meterTaskInfosList.Add(ammerterItem.Value); } @@ -433,9 +437,9 @@ namespace JiShe.CollectBus.ScheduledMeterReading TimeDensity = timeDensity.ToString(), }; - _ = _capBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500) ,ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, tempMsg); + //_ = _producerBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500) ,ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, tempMsg); - //await _massTransitBus.Publish(tempMsg); + _ = _producerBus.Publish(tempMsg); meterTaskInfosList.Add(ammerterItem.Value); } @@ -823,7 +827,10 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; - await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + //await _producerBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + + _ = _producerBus.Publish(tempMsg); + meterTaskInfosList.Add(ammerterItem.Value); } @@ -889,7 +896,10 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; - await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + //await _producerBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + + _ = _producerBus.Publish(tempMsg); + meterTaskInfosList.Add(ammerterItem.Value); } @@ -954,7 +964,10 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; - await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + //await _producerBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + + _ = _producerBus.Publish(tempMsg); + meterTaskInfosList.Add(ammerterItem.Value); } diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs index 70cecfa..78276e9 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs @@ -29,7 +29,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading { public EnergySystemScheduledMeterReadingService(ILogger logger, - ICapPublisher capBus, IMeterReadingRecordRepository meterReadingRecordsRepository) : base(logger, capBus, meterReadingRecordsRepository) + IPublishEndpoint producerBus, IMeterReadingRecordRepository meterReadingRecordsRepository) : base(logger, producerBus, meterReadingRecordsRepository) { } diff --git a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs index b2f7400..242b216 100644 --- a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs +++ b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs @@ -62,19 +62,18 @@ namespace JiShe.CollectBus.Subscribers [CapSubscribe(ProtocolConst.SubscriberIssuedEventName)] public async Task IssuedEvent(IssuedEventMessage issuedEventMessage) - { - _logger.LogWarning($"集中器地址{issuedEventMessage.ClientId} 回复下发内容IssuedEvent:{issuedEventMessage.MessageId}"); - + { switch (issuedEventMessage.Type) { case IssuedEventType.Heartbeat: - _logger.LogInformation($"IssuedEvent:{issuedEventMessage.MessageId}"); + _logger.LogWarning($"集中器地址{issuedEventMessage.ClientId} 心跳回复下发内容:{issuedEventMessage.Serialize()}"); var heartbeatEntity = await _messageReceivedHeartbeatEventRepository.GetAsync(a => a.MessageId == issuedEventMessage.MessageId); heartbeatEntity.AckTime = Clock.Now; heartbeatEntity.IsAck = true; await _messageReceivedHeartbeatEventRepository.UpdateAsync(heartbeatEntity); break; case IssuedEventType.Login: + _logger.LogWarning($"集中器地址{issuedEventMessage.ClientId} 登录回复下发内容:{issuedEventMessage.Serialize()}"); var loginEntity = await _messageReceivedLoginEventRepository.GetAsync(a => a.MessageId == issuedEventMessage.MessageId); loginEntity.AckTime = Clock.Now; loginEntity.IsAck = true; diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs index 374eaa5..f0a02c9 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs @@ -21,6 +21,7 @@ using JiShe.CollectBus.Consumers; using JiShe.CollectBus.Protocol.Contracts; using JiShe.CollectBus.IotSystems.MessageReceiveds; using JiShe.CollectBus.IotSystems.MessageIssueds; +using Confluent.Kafka; namespace JiShe.CollectBus.Host @@ -279,9 +280,13 @@ namespace JiShe.CollectBus.Host /// The configuration. public void ConfigureMassTransit(ServiceConfigurationContext context, IConfiguration configuration) { + + //context.Services.AddSingleton(); + context.Services.AddMassTransit(x => { x.UsingInMemory(); + x.AddConfigureEndpointsCallback((c, name, cfg) => { @@ -308,37 +313,38 @@ namespace JiShe.CollectBus.Host rider.UsingKafka((c, cfg) => { + cfg.Host(configuration.GetConnectionString("Kafka")); cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedHeartbeatEventName, ProtocolConst.SubscriberGroup, configurator => { configurator.ConfigureConsumer(c); - configurator.ConfigureConsumeTopology = false; + configurator.AutoOffsetReset = AutoOffsetReset.Earliest; }); cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedLoginEventName, ProtocolConst.SubscriberGroup, configurator => { configurator.ConfigureConsumer(c); - configurator.ConfigureConsumeTopology = false; + configurator.AutoOffsetReset = AutoOffsetReset.Earliest; }); cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedEventName, ProtocolConst.SubscriberGroup, configurator => { configurator.ConfigureConsumer(c); - configurator.ConfigureConsumeTopology = false; + configurator.AutoOffsetReset = AutoOffsetReset.Earliest; }); cfg.TopicEndpoint(ProtocolConst.SubscriberIssuedEventName, ProtocolConst.SubscriberGroup, configurator => { configurator.ConfigureConsumer(c); - configurator.ConfigureConsumeTopology = false; + configurator.AutoOffsetReset = AutoOffsetReset.Earliest; }); - //cfg.TopicEndpoint(ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, ProtocolConst.SubscriberGroup, configurator => - //{ - // configurator.ConfigureConsumer(c); - // configurator.ConfigureConsumeTopology = false; - //}); + cfg.TopicEndpoint(ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, ProtocolConst.SubscriberGroup, configurator => + { + configurator.ConfigureConsumer(c); + configurator.AutoOffsetReset = AutoOffsetReset.Earliest; + }); }); }); }); diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.cs index 6f24b1e..263be23 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.cs @@ -43,8 +43,8 @@ namespace JiShe.CollectBus.Host ConfigureNetwork(context, configuration); ConfigureJwtAuthentication(context, configuration); ConfigureHangfire(context); - ConfigureCap(context, configuration); - //ConfigureMassTransit(context, configuration); + //ConfigureCap(context, configuration); + ConfigureMassTransit(context, configuration); ConfigureAuditLog(context); ConfigureCustom(context, configuration); } diff --git a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs index 978a056..2fbe138 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs @@ -11,12 +11,13 @@ using JiShe.CollectBus.Protocol.Contracts.AnalysisData; using Microsoft.Extensions.DependencyInjection; using JiShe.CollectBus.IotSystems.MessageReceiveds; using JiShe.CollectBus.IotSystems.Protocols; +using MassTransit; namespace JiShe.CollectBus.Protocol.Contracts.Abstracts { public abstract class BaseProtocolPlugin : IProtocolPlugin { - private readonly ICapPublisher _capBus; + private readonly IPublishEndpoint _producerBus; private readonly ILogger _logger; private readonly IRepository _protocolInfoRepository; @@ -36,7 +37,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts _logger = serviceProvider.GetRequiredService>(); _protocolInfoRepository = serviceProvider.GetRequiredService>(); - _capBus = serviceProvider.GetRequiredService(); + _producerBus = serviceProvider.GetRequiredService(); } public abstract ProtocolInfo Info { get; } @@ -86,7 +87,8 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts }; var bytes = Build3761SendData.BuildSendCommandBytes(reqParam); - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); + //await _producerBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); + await _producerBus.Publish(new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); } /// @@ -124,7 +126,9 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts Fn = 1 }; var bytes = Build3761SendData.BuildSendCommandBytes(reqParam); - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); + //await _producerBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); + + await _producerBus.Publish(new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); } } From 838ef197e27663f5ad6bfc655257e0e4fcbc2979 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Wed, 2 Apr 2025 09:42:04 +0800 Subject: [PATCH 05/18] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E4=B8=BB=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CollectBusHostModule.Configure.cs | 55 ++++++++++++------- .../Extensions/ProtocolConstExtensions.cs | 29 ++++++++++ 2 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs index f0a02c9..3516bbb 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs @@ -22,6 +22,7 @@ using JiShe.CollectBus.Protocol.Contracts; using JiShe.CollectBus.IotSystems.MessageReceiveds; using JiShe.CollectBus.IotSystems.MessageIssueds; using Confluent.Kafka; +using MassTransit.SqlTransport.Topology; namespace JiShe.CollectBus.Host @@ -273,6 +274,11 @@ namespace JiShe.CollectBus.Host } + /// + /// Configures the mass transit. + /// + /// The context. + /// The configuration. /// /// Configures the mass transit. /// @@ -280,20 +286,29 @@ namespace JiShe.CollectBus.Host /// The configuration. public void ConfigureMassTransit(ServiceConfigurationContext context, IConfiguration configuration) { + var consumerConfig = new ConsumerConfig { GroupId = ProtocolConst.SubscriberGroup }; + var producerConfig = new ProducerConfig(); - //context.Services.AddSingleton(); - - context.Services.AddMassTransit(x => + context.Services + .ConfigureKafkaTestOptions(options => { - x.UsingInMemory(); - - x.AddConfigureEndpointsCallback((c, name, cfg) => - { - cfg.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30))); - cfg.UseMessageRetry(r => r.Immediate(5)); - cfg.UseInMemoryOutbox(c); - }); +#if DEBUG + options.CleanTopicsOnStart = true;// 测试时,每次启动都删除topic,生产环境不需要 +#endif + options.CreateTopicsIfNotExists = true; + options.TopicNames = ProtocolConstExtensions.GetAllTopicNames(); + }) + .AddMassTransit(x => + { + x.UsingInMemory((context, cfg) => cfg.ConfigureEndpoints(context)); + + //x.AddConfigureEndpointsCallback((c, name, cfg) => + //{ + // cfg.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30))); + // cfg.UseMessageRetry(r => r.Immediate(5)); + // cfg.UseInMemoryOutbox(c); + //}); x.AddRider(rider => { @@ -310,37 +325,37 @@ namespace JiShe.CollectBus.Host }); rider.AddConsumer(); - + rider.UsingKafka((c, cfg) => { - - cfg.Host(configuration.GetConnectionString("Kafka")); + List hosts = new List() { "121.42.242.91:29092", "121.42.242.91:39092", "121.42.242.91:49092" }; + cfg.Host(hosts); - cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedHeartbeatEventName, ProtocolConst.SubscriberGroup, configurator => + cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedHeartbeatEventName, consumerConfig, configurator => { - configurator.ConfigureConsumer(c); configurator.AutoOffsetReset = AutoOffsetReset.Earliest; + configurator.ConfigureConsumer(c); }); - cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedLoginEventName, ProtocolConst.SubscriberGroup, configurator => + cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedLoginEventName, consumerConfig, configurator => { configurator.ConfigureConsumer(c); configurator.AutoOffsetReset = AutoOffsetReset.Earliest; }); - cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedEventName, ProtocolConst.SubscriberGroup, configurator => + cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedEventName, consumerConfig, configurator => { configurator.ConfigureConsumer(c); configurator.AutoOffsetReset = AutoOffsetReset.Earliest; }); - cfg.TopicEndpoint(ProtocolConst.SubscriberIssuedEventName, ProtocolConst.SubscriberGroup, configurator => + cfg.TopicEndpoint(ProtocolConst.SubscriberIssuedEventName, consumerConfig, configurator => { configurator.ConfigureConsumer(c); configurator.AutoOffsetReset = AutoOffsetReset.Earliest; }); - cfg.TopicEndpoint(ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, ProtocolConst.SubscriberGroup, configurator => + cfg.TopicEndpoint(ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, consumerConfig, configurator => { configurator.ConfigureConsumer(c); configurator.AutoOffsetReset = AutoOffsetReset.Earliest; diff --git a/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs b/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs new file mode 100644 index 0000000..9455144 --- /dev/null +++ b/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.Protocol.Contracts +{ + public class ProtocolConstExtensions + { + /// + /// 自动获取 ProtocolConst 类中所有 Kafka 主题名称 + /// (通过反射筛选 public const string 且字段名以 "EventName" 结尾的常量) + /// + public static List GetAllTopicNames() + { + return typeof(ProtocolConst) + .GetFields(BindingFlags.Public | BindingFlags.Static) + .Where(f => + f.IsLiteral && + !f.IsInitOnly && + f.FieldType == typeof(string) && + f.Name.EndsWith("EventName")) // 通过命名规则过滤主题字段 + .Select(f => (string)f.GetRawConstantValue()!) + .ToList(); + } + } +} From c57bd15b92819204cb8807e08c4b98c039222fc5 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Wed, 2 Apr 2025 14:06:40 +0800 Subject: [PATCH 06/18] =?UTF-8?q?=E5=B0=81=E8=A3=85IoTDBProvider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- JiShe.CollectBus.sln | 7 + .../Attribute/ATTRIBUTEColumnAttribute.cs | 16 ++ .../Attribute/FIELDColumnAttribute.cs | 16 ++ .../Attribute/TAGColumnAttribute.cs | 16 ++ .../CollectBusIoTDBModule.cs | 19 ++ .../DeviceMetadata.cs | 24 ++ .../DevicePathBuilder.cs | 37 +++ .../IIoTDBProvider.cs | 38 +++ .../IoTDBProvider.cs | 262 ++++++++++++++++++ .../IoTEntity.cs | 19 ++ .../JiShe.CollectBus.IoTDBProvider.csproj | 12 + .../Options/IoTDBOptions.cs | 32 +++ .../Options/PagedResult.cs | 25 ++ .../Options/QueryCondition.cs | 27 ++ .../Options/QueryOptions.cs | 27 ++ 15 files changed, 577 insertions(+) create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/IIoTDBProvider.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/IoTEntity.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Options/PagedResult.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Options/QueryCondition.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs diff --git a/JiShe.CollectBus.sln b/JiShe.CollectBus.sln index 674eca4..0a7a6e7 100644 --- a/JiShe.CollectBus.sln +++ b/JiShe.CollectBus.sln @@ -31,6 +31,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.FreeSql", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.FreeRedisProvider", "src\JiShe.CollectBus.FreeRedisProvider\JiShe.CollectBus.FreeRedisProvider.csproj", "{C06C4082-638F-2996-5FED-7784475766C1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.IoTDBProvider", "src\JiShe.CollectBus.IoTDBProvider\JiShe.CollectBus.IoTDBProvider.csproj", "{A3F3C092-0A25-450B-BF6A-5983163CBEF5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -89,6 +91,10 @@ Global {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 + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -107,6 +113,7 @@ Global {8BA01C3D-297D-42DF-BD63-EF07202A0A67} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} {FE0457D9-4038-4A17-8808-DCAD06CFC0A0} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} {C06C4082-638F-2996-5FED-7784475766C1} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {A3F3C092-0A25-450B-BF6A-5983163CBEF5} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs b/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs new file mode 100644 index 0000000..42820bc --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// Column分类标记特性(TAG字段) + /// + [AttributeUsage(AttributeTargets.Property)] + public class TAGColumnAttribute : Attribute + { + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs b/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs new file mode 100644 index 0000000..2c48a65 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// Column分类标记特性(FIELD字段) + /// + [AttributeUsage(AttributeTargets.Property)] + public class FIELDColumnAttribute : Attribute + { + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs b/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs new file mode 100644 index 0000000..28f6a46 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// Column分类标记特性(ATTRIBUTE字段) + /// + [AttributeUsage(AttributeTargets.Property)] + public class ATTRIBUTEColumnAttribute : Attribute + { + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs b/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs new file mode 100644 index 0000000..5a8902c --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Modularity; + +namespace JiShe.CollectBus.IoTDBProvider +{ + public class CollectBusIoTDBModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.Configure(context.Services.GetConfiguration().GetSection(nameof(IoTDBOptions))); + context.Services.AddSingleton(); + } + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs b/src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs new file mode 100644 index 0000000..dea65fb --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs @@ -0,0 +1,24 @@ +using Apache.IoTDB; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// 设备元数据 + /// + public class DeviceMetadata + { + public List Measurements { get; } = new(); + public List Tags { get; } = new(); + + public List GetDataTypes() + { + // 根据实际类型映射TSDataType + return Measurements.Select(_ => TSDataType.TEXT).ToList(); + } + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs b/src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs new file mode 100644 index 0000000..ef6d7e4 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// 设备路径构建器 + /// + public static class DevicePathBuilder + { + /// + /// 构建存储组路径 + /// + /// + /// + public static string BuildStorageGroupPath() where T : IoTEntity + { + var type = typeof(T); + return $"root.{type.GetProperty("SystemName")?.Name}.{type.GetProperty("ProjectCode")?.Name}"; + } + + /// + /// 构建设备路径 + /// + /// + /// + /// + public static string BuildDevicePath(T entity) where T : IoTEntity + { + return $"root.{entity.SystemName}.{entity.ProjectCode}.{entity.DeviceId}"; + } + } + +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/IIoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/IIoTDBProvider.cs new file mode 100644 index 0000000..026a95a --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/IIoTDBProvider.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// IoTDB数据源 + /// + public interface IIoTDBProvider + { + /// + /// 插入数据 + /// + /// + /// + /// + Task InsertAsync(T entity) where T : IoTEntity; + + /// + /// 批量插入数据 + /// + /// + /// + /// + Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity; + + /// + /// 查询数据 + /// + /// + /// + /// + Task> QueryAsync(QueryOptions options) where T : IoTEntity, new(); + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs new file mode 100644 index 0000000..f2a5546 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs @@ -0,0 +1,262 @@ +using Apache.IoTDB; +using Apache.IoTDB.DataStructure; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// IoTDB数据源 + /// + public class IoTDBProvider : IIoTDBProvider, IDisposable + { + private readonly IoTDBOptions _options; + private readonly SessionPool _sessionPool; + private static readonly ConcurrentDictionary _metadataCache = new(); + + public IoTDBProvider(IOptions options) + { + _options = options.Value; + _sessionPool = new SessionPool( + _options.ClusterList, + _options.UserName, + _options.Password, + _options.PoolSize); + _sessionPool.Open(false).Wait(); + } + + /// + /// 获取设备元数据 + /// + /// + /// + private DeviceMetadata GetMetadata() where T : IoTEntity + { + return _metadataCache.GetOrAdd(typeof(T), type => + { + var metadata = new DeviceMetadata(); + foreach (var prop in type.GetProperties()) + { + var attr = prop.GetCustomAttribute(); + if (attr != null) + { + metadata.Tags.Add(prop.Name); + } + else if (prop.Name != nameof(IoTEntity.Timestamp)) + { + metadata.Measurements.Add(prop.Name); + } + } + return metadata; + }); + } + + /// + /// 插入数据 + /// + /// + /// + /// + public async Task InsertAsync(T entity) where T : IoTEntity + { + var metadata = GetMetadata(); + var storageGroup = DevicePathBuilder.BuildStorageGroupPath(); + await EnsureStorageGroupCreated(storageGroup); + + var tablet = BuildTablet(new[] { entity }, metadata); + await _sessionPool.InsertAlignedTabletAsync(tablet); + } + + /// + /// 批量插入数据 + /// + /// + /// + /// + public async Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity + { + var metadata = GetMetadata(); + var storageGroup = DevicePathBuilder.BuildStorageGroupPath(); + await EnsureStorageGroupCreated(storageGroup); + + var batchSize = 1000; + var batches = entities.Chunk(batchSize); + + foreach (var batch in batches) + { + var tablet = BuildTablet(batch, metadata); + await _sessionPool.InsertAlignedTabletAsync(tablet); + } + } + + /// + /// 构建表模型 + /// + /// + /// + /// + /// + private Tablet BuildTablet(IEnumerable entities, DeviceMetadata metadata) where T : IoTEntity + { + var devicePath = DevicePathBuilder.BuildDevicePath(entities.First()); + var timestamps = new List(); + var values = new List>(); + + foreach (var entity in entities) + { + timestamps.Add(entity.Timestamp); + var rowValues = new List(); + foreach (var measurement in metadata.Measurements) + { + var value = typeof(T).GetProperty(measurement)?.GetValue(entity); + rowValues.Add(value ?? DBNull.Value); + } + values.Add(rowValues); + } + + return new Tablet( + devicePath, + metadata.Measurements, + metadata.GetDataTypes(), + values, + timestamps + ) + { + Tags = metadata.Tags.ToDictionary( + t => t, + t => typeof(T).GetProperty(t)?.GetValue(entities.First())?.ToString()) + }; + } + + /// + /// 查询数据 + /// + /// + /// + /// + public async Task> QueryAsync(QueryOptions options) where T : IoTEntity, new() + { + var query = BuildQuery(options); + var sessionDataSet = await _sessionPool.ExecuteQueryStatementAsync(query); + + var result = new PagedResult + { + TotalCount = await GetTotalCount(options), + Items = ParseResults(sessionDataSet, options.PageSize) + }; + + return result; + } + + /// + /// 构建查询语句 + /// + /// + /// + /// + private string BuildQuery(QueryOptions options) where T : IoTEntity + { + var metadata = GetMetadata(); + var sb = new StringBuilder("SELECT "); + sb.AppendJoin(", ", metadata.Measurements); + sb.Append($" FROM {DevicePathBuilder.BuildStorageGroupPath()}"); + + if (options.Conditions.Any()) + { + sb.Append(" WHERE "); + sb.AppendJoin(" AND ", options.Conditions.Select(TranslateCondition)); + } + + sb.Append($" LIMIT {options.PageSize} OFFSET {options.Page * options.PageSize}"); + 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($"Operator {condition.Operator} not supported") + }; + } + + /// + /// 获取查询条件的总数量 + /// + /// + /// + /// + private async Task GetTotalCount(QueryOptions options) where T : IoTEntity + { + var countQuery = $"SELECT COUNT(*) FROM {DevicePathBuilder.BuildStorageGroupPath()}"; + if (options.Conditions.Any()) + { + countQuery += " WHERE " + string.Join(" AND ", options.Conditions.Select(TranslateCondition)); + } + + var result = await _sessionPool.ExecuteQueryStatementAsync(countQuery); + return result.HasNext() ? Convert.ToInt32(result.Next().Values[0]) : 0; + } + + /// + /// 解析查询结果 + /// + /// + /// + /// + /// + private IEnumerable ParseResults(SessionDataSet dataSet, int pageSize) where T : IoTEntity, new() + { + var results = new List(); + var metadata = GetMetadata(); + + while (dataSet.HasNext() && results.Count < pageSize) + { + var record = dataSet.Next(); + var entity = new T + { + Timestamp = record.Timestamps + }; + + foreach (var measurement in metadata.Measurements) + { + var value = record.GetValue(measurement); + typeof(T).GetProperty(measurement)?.SetValue(entity, value); + } + + results.Add(entity); + } + return results; + } + + private async Task EnsureStorageGroupCreated(string storageGroup) + { + if (!await _sessionPool.CheckStorageGroupExists(storageGroup)) + { + await _sessionPool.SetStorageGroupAsync(storageGroup); + } + } + + /// + /// 释放资源 + /// + public void Dispose() + { + _sessionPool?.Close().Wait(); + } + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/IoTEntity.cs b/src/JiShe.CollectBus.IoTDBProvider/IoTEntity.cs new file mode 100644 index 0000000..7c277df --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/IoTEntity.cs @@ -0,0 +1,19 @@ +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// IoT实体基类 + /// + public abstract class IoTEntity + { + [TAGColumn] + public string SystemName { get; set; } + + [TAGColumn] + public string ProjectCode { get; set; } + + [TAGColumn] + public string DeviceId { get; set; } + + public long Timestamp { get; set; } + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj b/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj new file mode 100644 index 0000000..37504aa --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj @@ -0,0 +1,12 @@ + + + + net8.0 + enable + enable + + + + + + diff --git a/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs b/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs new file mode 100644 index 0000000..5cdd256 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// IOTDB配置 + /// + public class IoTDBOptions + { + /// + /// 集群列表 + /// + public List ClusterList { get; set; } + /// + /// 用户名 + /// + public string UserName { get; set; } + /// + /// 密码 + /// + public string Password { get; set; } + + /// + /// 连接池大小 + /// + public int PoolSize { get; set; } = 3; + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Options/PagedResult.cs b/src/JiShe.CollectBus.IoTDBProvider/Options/PagedResult.cs new file mode 100644 index 0000000..b707355 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Options/PagedResult.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// 查询结果 + /// + /// + public class PagedResult + { + /// + /// 总条数 + /// + public int TotalCount { get; set; } + + /// + /// 数据集合 + /// + public IEnumerable Items { get; set; } + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Options/QueryCondition.cs b/src/JiShe.CollectBus.IoTDBProvider/Options/QueryCondition.cs new file mode 100644 index 0000000..b1aa53b --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Options/QueryCondition.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// 查询条件 + /// + public class QueryCondition + { + /// + /// 字段 + /// + public string Field { get; set; } + /// + /// 操作符 + /// + public string Operator { get; set; } + /// + /// 值 + /// + public object Value { get; set; } + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs b/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs new file mode 100644 index 0000000..019bd28 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// 查询条件 + /// + public class QueryOptions + { + /// + /// 分页 + /// + public int Page { get; set; } + /// + /// 分页大小 + /// + public int PageSize { get; set; } + /// + /// 查询条件 + /// + public List Conditions { get; } = new(); + } +} From a5f806c481be56fa1b84dfe9b309ce18c6cb20a1 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Wed, 2 Apr 2025 17:23:52 +0800 Subject: [PATCH 07/18] =?UTF-8?q?IoTDB=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Plugins/TcpMonitor.cs | 1 - .../Attribute/ATTRIBUTEColumnAttribute.cs | 6 +- .../Attribute/FIELDColumnAttribute.cs | 2 +- .../Attribute/TAGColumnAttribute.cs | 4 +- .../DeviceMetadata.cs | 24 ---- .../{ => Interface}/IIoTDBProvider.cs | 0 .../IoTEntity.cs | 19 --- .../JiShe.CollectBus.IoTDBProvider.csproj | 3 +- .../Provider/DeviceMetadata.cs | 30 +++++ .../{ => Provider}/DevicePathBuilder.cs | 26 ++-- .../{ => Provider}/IoTDBProvider.cs | 116 +++++++++++------- .../Provider/IoTEntity.cs | 37 ++++++ 12 files changed, 163 insertions(+), 105 deletions(-) delete mode 100644 src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs rename src/JiShe.CollectBus.IoTDBProvider/{ => Interface}/IIoTDBProvider.cs (100%) delete mode 100644 src/JiShe.CollectBus.IoTDBProvider/IoTEntity.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Provider/DeviceMetadata.cs rename src/JiShe.CollectBus.IoTDBProvider/{ => Provider}/DevicePathBuilder.cs (72%) rename src/JiShe.CollectBus.IoTDBProvider/{ => Provider}/IoTDBProvider.cs (70%) create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs diff --git a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs index 4801404..7083387 100644 --- a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs +++ b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs @@ -57,7 +57,6 @@ namespace JiShe.CollectBus.Plugins if (aFn.HasValue && fn.HasValue && aTuple != null && !string.IsNullOrWhiteSpace(aTuple.Item1)) { var tcpSessionClient = (ITcpSessionClient)client; - if ((AFN)aFn == AFN.链路接口检测) { diff --git a/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs b/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs index 42820bc..1780bf9 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs @@ -5,12 +5,12 @@ using System.Text; using System.Threading.Tasks; namespace JiShe.CollectBus.IoTDBProvider -{ +{ /// - /// Column分类标记特性(TAG字段) + /// Column分类标记特性(ATTRIBUTE字段),也就是属性字段 /// [AttributeUsage(AttributeTargets.Property)] - public class TAGColumnAttribute : Attribute + public class ATTRIBUTEColumnAttribute : Attribute { } } diff --git a/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs b/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs index 2c48a65..2f8470b 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace JiShe.CollectBus.IoTDBProvider { /// - /// Column分类标记特性(FIELD字段) + /// Column分类标记特性(FIELD字段),数据列字段 /// [AttributeUsage(AttributeTargets.Property)] public class FIELDColumnAttribute : Attribute diff --git a/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs b/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs index 28f6a46..b6149fe 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs @@ -7,10 +7,10 @@ using System.Threading.Tasks; namespace JiShe.CollectBus.IoTDBProvider { /// - /// Column分类标记特性(ATTRIBUTE字段) + /// Column分类标记特性(TAG字段),标签字段 /// [AttributeUsage(AttributeTargets.Property)] - public class ATTRIBUTEColumnAttribute : Attribute + public class TAGColumnAttribute : Attribute { } } diff --git a/src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs b/src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs deleted file mode 100644 index dea65fb..0000000 --- a/src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Apache.IoTDB; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace JiShe.CollectBus.IoTDBProvider -{ - /// - /// 设备元数据 - /// - public class DeviceMetadata - { - public List Measurements { get; } = new(); - public List Tags { get; } = new(); - - public List GetDataTypes() - { - // 根据实际类型映射TSDataType - return Measurements.Select(_ => TSDataType.TEXT).ToList(); - } - } -} diff --git a/src/JiShe.CollectBus.IoTDBProvider/IIoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs similarity index 100% rename from src/JiShe.CollectBus.IoTDBProvider/IIoTDBProvider.cs rename to src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs diff --git a/src/JiShe.CollectBus.IoTDBProvider/IoTEntity.cs b/src/JiShe.CollectBus.IoTDBProvider/IoTEntity.cs deleted file mode 100644 index 7c277df..0000000 --- a/src/JiShe.CollectBus.IoTDBProvider/IoTEntity.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace JiShe.CollectBus.IoTDBProvider -{ - /// - /// IoT实体基类 - /// - public abstract class IoTEntity - { - [TAGColumn] - public string SystemName { get; set; } - - [TAGColumn] - public string ProjectCode { get; set; } - - [TAGColumn] - public string DeviceId { get; set; } - - public long Timestamp { get; set; } - } -} diff --git a/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj b/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj index 37504aa..717b0f6 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj +++ b/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj @@ -6,7 +6,8 @@ enable - + + diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/DeviceMetadata.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/DeviceMetadata.cs new file mode 100644 index 0000000..8e84776 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/DeviceMetadata.cs @@ -0,0 +1,30 @@ +using Apache.IoTDB; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// 设备元数据 + /// + public class DeviceMetadata + { + /// + /// 测量值集合,用于构建Table的测量值,也就是field参数 + /// + public List Measurements { get; } = new(); + + /// + /// 列类型集合,用于构建Table的列类型,也就是columnCategory参数 + /// + public List ColumnCategories { get; } = new(); + + /// + /// 值类型集合,用于构建Table的值类型,也就是dataType参数 + /// + public ListDataTypes { get; } = new(); + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs similarity index 72% rename from src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs rename to src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs index ef6d7e4..3166ca6 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs @@ -11,27 +11,29 @@ namespace JiShe.CollectBus.IoTDBProvider /// public static class DevicePathBuilder { - /// - /// 构建存储组路径 - /// - /// - /// - public static string BuildStorageGroupPath() where T : IoTEntity - { - var type = typeof(T); - return $"root.{type.GetProperty("SystemName")?.Name}.{type.GetProperty("ProjectCode")?.Name}"; - } - /// /// 构建设备路径 /// /// /// /// - public static string BuildDevicePath(T entity) where T : IoTEntity + public static string GetDeviceId(T entity) where T : IoTEntity { return $"root.{entity.SystemName}.{entity.ProjectCode}.{entity.DeviceId}"; } + + + /// + /// 获取表名称 + /// + /// + /// + /// + public static string GetTableName() where T : IoTEntity + { + var type = typeof(T); + return $"{type.Name}"; + } } } diff --git a/src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs similarity index 70% rename from src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs rename to src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs index f2a5546..449884a 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs @@ -17,17 +17,20 @@ namespace JiShe.CollectBus.IoTDBProvider public class IoTDBProvider : IIoTDBProvider, IDisposable { private readonly IoTDBOptions _options; - private readonly SessionPool _sessionPool; + private readonly TableSessionPool _sessionPool; private static readonly ConcurrentDictionary _metadataCache = new(); public IoTDBProvider(IOptions options) { _options = options.Value; - _sessionPool = new SessionPool( - _options.ClusterList, - _options.UserName, - _options.Password, - _options.PoolSize); + + _sessionPool = new TableSessionPool.Builder() + .SetNodeUrls(_options.ClusterList) + .SetUsername(_options.UserName) + .SetPassword(_options.Password) + .SetFetchSize(_options.PoolSize) + .Build(); + _sessionPool.Open(false).Wait(); } @@ -43,14 +46,27 @@ namespace JiShe.CollectBus.IoTDBProvider var metadata = new DeviceMetadata(); foreach (var prop in type.GetProperties()) { - var attr = prop.GetCustomAttribute(); - if (attr != null) + //标签列 + var attrTAG = prop.GetCustomAttribute(); + if (attrTAG != null) { - metadata.Tags.Add(prop.Name); + metadata.ColumnCategories.Add(ColumnCategory.TAG); } - else if (prop.Name != nameof(IoTEntity.Timestamp)) + + //属性列 + var attrATTRIBUTE = prop.GetCustomAttribute(); + if (attrATTRIBUTE != null) { + metadata.ColumnCategories.Add(ColumnCategory.ATTRIBUTE); + } + + //数据列 + var attrFIELD = prop.GetCustomAttribute(); + if (attrFIELD != null) + { + metadata.ColumnCategories.Add(ColumnCategory.FIELD); metadata.Measurements.Add(prop.Name); + metadata.DataTypes.Add(GetDataTypeFromStr(prop.PropertyType.Name)); } } return metadata; @@ -66,11 +82,9 @@ namespace JiShe.CollectBus.IoTDBProvider public async Task InsertAsync(T entity) where T : IoTEntity { var metadata = GetMetadata(); - var storageGroup = DevicePathBuilder.BuildStorageGroupPath(); - await EnsureStorageGroupCreated(storageGroup); var tablet = BuildTablet(new[] { entity }, metadata); - await _sessionPool.InsertAlignedTabletAsync(tablet); + await _sessionPool.InsertAsync(tablet); } /// @@ -81,9 +95,7 @@ namespace JiShe.CollectBus.IoTDBProvider /// public async Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity { - var metadata = GetMetadata(); - var storageGroup = DevicePathBuilder.BuildStorageGroupPath(); - await EnsureStorageGroupCreated(storageGroup); + var metadata = GetMetadata(); var batchSize = 1000; var batches = entities.Chunk(batchSize); @@ -91,7 +103,7 @@ namespace JiShe.CollectBus.IoTDBProvider foreach (var batch in batches) { var tablet = BuildTablet(batch, metadata); - await _sessionPool.InsertAlignedTabletAsync(tablet); + await _sessionPool.InsertAsync(tablet); } } @@ -104,34 +116,33 @@ namespace JiShe.CollectBus.IoTDBProvider /// private Tablet BuildTablet(IEnumerable entities, DeviceMetadata metadata) where T : IoTEntity { - var devicePath = DevicePathBuilder.BuildDevicePath(entities.First()); + var deviceId = DevicePathBuilder.GetDeviceId(entities.First()); var timestamps = new List(); var values = new List>(); foreach (var entity in entities) { - timestamps.Add(entity.Timestamp); + timestamps.Add(entity.Timestamps); var rowValues = new List(); foreach (var measurement in metadata.Measurements) { var value = typeof(T).GetProperty(measurement)?.GetValue(entity); - rowValues.Add(value ?? DBNull.Value); + if(value == null) + { + throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,属性{measurement}值为空,不符合IoTDB设计标准,请赋值以后重新处理。"); + } + rowValues.Add(value); } values.Add(rowValues); } return new Tablet( - devicePath, + deviceId, metadata.Measurements, - metadata.GetDataTypes(), + metadata.DataTypes, values, timestamps - ) - { - Tags = metadata.Tags.ToDictionary( - t => t, - t => typeof(T).GetProperty(t)?.GetValue(entities.First())?.ToString()) - }; + ); } /// @@ -165,7 +176,7 @@ namespace JiShe.CollectBus.IoTDBProvider var metadata = GetMetadata(); var sb = new StringBuilder("SELECT "); sb.AppendJoin(", ", metadata.Measurements); - sb.Append($" FROM {DevicePathBuilder.BuildStorageGroupPath()}"); + sb.Append($" FROM {DevicePathBuilder.GetTableName()}"); if (options.Conditions.Any()) { @@ -202,7 +213,7 @@ namespace JiShe.CollectBus.IoTDBProvider /// private async Task GetTotalCount(QueryOptions options) where T : IoTEntity { - var countQuery = $"SELECT COUNT(*) FROM {DevicePathBuilder.BuildStorageGroupPath()}"; + var countQuery = $"SELECT COUNT(*) FROM {DevicePathBuilder.GetTableName()}"; if (options.Conditions.Any()) { countQuery += " WHERE " + string.Join(" AND ", options.Conditions.Select(TranslateCondition)); @@ -224,33 +235,35 @@ namespace JiShe.CollectBus.IoTDBProvider var results = new List(); var metadata = GetMetadata(); + var properties = typeof(T).GetProperties(); + while (dataSet.HasNext() && results.Count < pageSize) { var record = dataSet.Next(); var entity = new T { - Timestamp = record.Timestamps + Timestamps = record.Timestamps }; + foreach (var measurement in metadata.Measurements) { - var value = record.GetValue(measurement); - typeof(T).GetProperty(measurement)?.SetValue(entity, value); + var value = record.Values; + + var prop = properties.FirstOrDefault(p => + p.Name.Equals(measurement, StringComparison.OrdinalIgnoreCase)); + if (prop != null) + { + typeof(T).GetProperty(measurement)?.SetValue(entity, value); + } + } results.Add(entity); } return results; } - - private async Task EnsureStorageGroupCreated(string storageGroup) - { - if (!await _sessionPool.CheckStorageGroupExists(storageGroup)) - { - await _sessionPool.SetStorageGroupAsync(storageGroup); - } - } - + /// /// 释放资源 /// @@ -258,5 +271,24 @@ namespace JiShe.CollectBus.IoTDBProvider { _sessionPool?.Close().Wait(); } + + private TSDataType GetDataTypeFromStr(string str) + { + return str switch + { + "BOOLEAN" => TSDataType.BOOLEAN, + "INT32" => TSDataType.INT32, + "INT64" => TSDataType.INT64, + "FLOAT" => TSDataType.FLOAT, + "DOUBLE" => TSDataType.DOUBLE, + "TEXT" => TSDataType.TEXT, + "NULLTYPE" => TSDataType.NONE, + "TIMESTAMP" => TSDataType.TIMESTAMP, + "DATE" => TSDataType.DATE, + "BLOB" => TSDataType.BLOB, + "STRING" => TSDataType.STRING, + _ => TSDataType.STRING + }; + } } } diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs new file mode 100644 index 0000000..2b090de --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs @@ -0,0 +1,37 @@ +namespace JiShe.CollectBus.IoTDBProvider +{ + /// + /// IoT实体基类 + /// + public abstract class IoTEntity + { + /// + /// 系统名称 + /// + [TAGColumn] + public string SystemName { get; set; } + + /// + /// 项目编码 + /// + [TAGColumn] + public string ProjectCode { get; set; } + + /// + /// 设备类型集中器、电表、水表、流量计、传感器等 + /// + [TAGColumn] + public string DeviceType { get; set; } + + /// + /// 设备ID + /// + [TAGColumn] + public string DeviceId { get; set; } + + /// + /// 时间戳 + /// + public long Timestamps { get; set; } + } +} From e1d4126db08262f4247418cea8cdc350003b51dc Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Wed, 2 Apr 2025 17:46:33 +0800 Subject: [PATCH 08/18] =?UTF-8?q?iotdb=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CollectBusApplicationModule.cs | 2 ++ .../Samples/SampleAppService.cs | 30 ++++++++++++++++- .../Ammeters/ElectricityMeter.cs | 24 ++++++++++++++ .../JiShe.CollectBus.Domain.csproj | 1 + .../CollectBusHostModule.Configure.cs | 16 +++++----- src/JiShe.CollectBus.Host/appsettings.json | 32 +++++++++++-------- .../Provider/IoTEntity.cs | 4 +-- 7 files changed, 85 insertions(+), 24 deletions(-) create mode 100644 src/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs diff --git a/src/JiShe.CollectBus.Application/CollectBusApplicationModule.cs b/src/JiShe.CollectBus.Application/CollectBusApplicationModule.cs index 86b36e7..085ab8e 100644 --- a/src/JiShe.CollectBus.Application/CollectBusApplicationModule.cs +++ b/src/JiShe.CollectBus.Application/CollectBusApplicationModule.cs @@ -15,6 +15,7 @@ using JiShe.CollectBus.Workers; using Volo.Abp.BackgroundWorkers.Hangfire; using JiShe.CollectBus.MongoDB; using JiShe.CollectBus.ScheduledMeterReading; +using JiShe.CollectBus.IoTDBProvider; namespace JiShe.CollectBus; @@ -25,6 +26,7 @@ namespace JiShe.CollectBus; typeof(AbpAutoMapperModule), typeof(AbpBackgroundWorkersHangfireModule), typeof(CollectBusFreeRedisModule), + typeof(CollectBusIoTDBModule), typeof(CollectBusFreeSqlModule) )] public class CollectBusApplicationModule : AbpModule diff --git a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs index 6235a55..f76ce54 100644 --- a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs +++ b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs @@ -1,6 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; +using JiShe.CollectBus.Ammeters; using JiShe.CollectBus.FreeSql; +using JiShe.CollectBus.IoTDBProvider; using JiShe.CollectBus.IotSystems.PrepayModel; using Microsoft.AspNetCore.Authorization; @@ -8,6 +11,31 @@ namespace JiShe.CollectBus.Samples; public class SampleAppService : CollectBusAppService, ISampleAppService { + + private readonly IIoTDBProvider _iotDBProvider; + + public SampleAppService(IIoTDBProvider iotDBProvider) + { + _iotDBProvider = iotDBProvider; + } + + + public async Task AddReadingAsync() + { + ElectricityMeter meter = new ElectricityMeter() + { + SystemName = "Energy", + DeviceId = "402440506" + , + DeviceType = "Ammeter", + Current = 10, + MeterModel = "DDZY-1980", + ProjectCode = "10059", + Voltage = 10 + }; + await _iotDBProvider.InsertAsync(meter); + } + public Task GetAsync() { return Task.FromResult( diff --git a/src/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs b/src/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs new file mode 100644 index 0000000..d335fca --- /dev/null +++ b/src/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs @@ -0,0 +1,24 @@ +using JiShe.CollectBus.IoTDBProvider; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.Ammeters +{ + public class ElectricityMeter : IoTEntity + { + [ATTRIBUTEColumn] + public string MeterModel { get; set; } + + [FIELDColumn] + public double Voltage { get; set; } + + [FIELDColumn] + public double Current { get; set; } + + [FIELDColumn] + public double Power => Voltage * Current; + } +} diff --git a/src/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj b/src/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj index 20068a5..16eef7f 100644 --- a/src/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj +++ b/src/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj @@ -24,6 +24,7 @@ + diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs index 3516bbb..40f498e 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs @@ -290,15 +290,15 @@ namespace JiShe.CollectBus.Host var producerConfig = new ProducerConfig(); context.Services - .ConfigureKafkaTestOptions(options => - { +// .ConfigureKafkaTestOptions(options => +// { -#if DEBUG - options.CleanTopicsOnStart = true;// 测试时,每次启动都删除topic,生产环境不需要 -#endif - options.CreateTopicsIfNotExists = true; - options.TopicNames = ProtocolConstExtensions.GetAllTopicNames(); - }) +//#if DEBUG +// options.CleanTopicsOnStart = true;// 测试时,每次启动都删除topic,生产环境不需要 +//#endif +// options.CreateTopicsIfNotExists = true; +// options.TopicNames = ProtocolConstExtensions.GetAllTopicNames(); +// }) .AddMassTransit(x => { x.UsingInMemory((context, cfg) => cfg.ConfigureEndpoints(context)); diff --git a/src/JiShe.CollectBus.Host/appsettings.json b/src/JiShe.CollectBus.Host/appsettings.json index 69bdcc1..b506272 100644 --- a/src/JiShe.CollectBus.Host/appsettings.json +++ b/src/JiShe.CollectBus.Host/appsettings.json @@ -1,4 +1,4 @@ - { +{ "Serilog": { "Using": [ "Serilog.Sinks.Console", @@ -20,7 +20,7 @@ { "Name": "Console" }, - { + { "Name": "File", "Args": { "path": "logs/logs-.txt", @@ -33,17 +33,17 @@ "SelfUrl": "http://localhost:44315", "CorsOrigins": "http://localhost:4200,http://localhost:3100" }, - "ConnectionStrings": { - "Default": "mongodb://admin:admin02023@118.190.144.92:37117,118.190.144.92:37119,118.190.144.92:37120/JiSheCollectBus?authSource=admin&maxPoolSize=400&minPoolSize=10&waitQueueTimeoutMS=5000", - "Kafka": "121.42.242.91:29092,121.42.242.91:39092,121.42.242.91: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": "118.190.144.92:6379,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", - "DefaultDB": "14", - "HangfireDB": "15" - }, + "ConnectionStrings": { + "Default": "mongodb://admin:admin02023@118.190.144.92:37117,118.190.144.92:37119,118.190.144.92:37120/JiSheCollectBus?authSource=admin&maxPoolSize=400&minPoolSize=10&waitQueueTimeoutMS=5000", + "Kafka": "121.42.242.91:29092,121.42.242.91:39092,121.42.242.91: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": "118.190.144.92:6379,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", + "DefaultDB": "14", + "HangfireDB": "15" + }, "Jwt": { "Audience": "JiShe.CollectBus", "SecurityKey": "dzehzRz9a8asdfasfdadfasdfasdfafsdadfasbasdf=", @@ -80,5 +80,11 @@ "Password": "123456", "Port": 5672 } + }, + "IoTDBOptions": { + "UserName": "root", + "Password": "root", + "ClusterList": [ "192.168.56.102:6667"], + "PoolSize": 10 } } \ No newline at end of file diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs index 2b090de..45964fa 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs @@ -30,8 +30,8 @@ public string DeviceId { get; set; } /// - /// 时间戳 + /// 当前时间戳,单位秒 /// - public long Timestamps { get; set; } + public long Timestamps { get; set; } = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } } From 5772ce906d9dc0afe838892ea4cd8388fdaeaa97 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Thu, 3 Apr 2025 15:38:31 +0800 Subject: [PATCH 09/18] =?UTF-8?q?=E5=AE=8C=E5=96=84IOTDB=20Provider?= =?UTF-8?q?=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Samples/SampleAppService.cs | 5 +- .../AttributeInfo/NumericalOrderAttribute.cs | 28 + .../Extensions/EnumExtensions.cs | 68 ++ .../Helpers/CommonHelper.cs | 764 ++++++++++++++++++ .../Helpers/JsonHelper.cs | 28 + .../Helpers/SelectResult.cs | 35 + .../Interface/IIoTDBProvider.cs | 15 +- .../JiShe.CollectBus.IoTDBProvider.csproj | 3 + .../Options/QueryOptions.cs | 5 + .../Provider/DeviceMetadata.cs | 8 +- .../Provider/IoTDBProvider.cs | 396 ++++++--- 11 files changed, 1252 insertions(+), 103 deletions(-) create mode 100644 src/JiShe.CollectBus.Common/AttributeInfo/NumericalOrderAttribute.cs create mode 100644 src/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs create mode 100644 src/JiShe.CollectBus.Common/Helpers/CommonHelper.cs create mode 100644 src/JiShe.CollectBus.Common/Helpers/SelectResult.cs diff --git a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs index f76ce54..9c2a86f 100644 --- a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs +++ b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs @@ -25,15 +25,14 @@ public class SampleAppService : CollectBusAppService, ISampleAppService ElectricityMeter meter = new ElectricityMeter() { SystemName = "Energy", - DeviceId = "402440506" - , + DeviceId = "402440506", DeviceType = "Ammeter", Current = 10, MeterModel = "DDZY-1980", ProjectCode = "10059", Voltage = 10 }; - await _iotDBProvider.InsertAsync(meter); + await _iotDBProvider.InsertAsync(meter,2); } public Task GetAsync() diff --git a/src/JiShe.CollectBus.Common/AttributeInfo/NumericalOrderAttribute.cs b/src/JiShe.CollectBus.Common/AttributeInfo/NumericalOrderAttribute.cs new file mode 100644 index 0000000..a062f16 --- /dev/null +++ b/src/JiShe.CollectBus.Common/AttributeInfo/NumericalOrderAttribute.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.Common.AttributeInfo +{ + /// + /// 排序序号 + /// + public class NumericalOrderAttribute : Attribute + { + /// + /// 序号 + /// + public int Index { get; set; } + + /// + /// 排序序号 + /// + /// + public NumericalOrderAttribute(int index) + { + Index = index; + } + } +} diff --git a/src/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs b/src/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs new file mode 100644 index 0000000..222984e --- /dev/null +++ b/src/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs @@ -0,0 +1,68 @@ +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 ToEnumNameDictionary() where TEnum : Enum + { + return Enum.GetValues(typeof(TEnum)) + .Cast() + .ToDictionary( + e => e, + e => e.ToString() + ); + } + + } +} diff --git a/src/JiShe.CollectBus.Common/Helpers/CommonHelper.cs b/src/JiShe.CollectBus.Common/Helpers/CommonHelper.cs new file mode 100644 index 0000000..fa38d1d --- /dev/null +++ b/src/JiShe.CollectBus.Common/Helpers/CommonHelper.cs @@ -0,0 +1,764 @@ +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.AttributeInfo; + +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; + } + + /// + /// 取得某月的第一天 + /// + /// 要取得月份第一天的时间 + /// + public static DateTime FirstDayOfMonth(this DateTime datetime) + { + return datetime.AddDays(1 - datetime.Day); + } + + /// + /// 取得某月的最后一天 + /// + /// 要取得月份最后一天的时间 + /// + public static DateTime LastDayOfMonth(this DateTime datetime) + { + return datetime.AddDays(1 - datetime.Day).AddMonths(1).AddDays(-1); + } + + /// + /// 取得某月第一天0点以及最后一天的23:59:59时间范围 + /// + /// + /// + public static Tuple GetMonthDateRange(this DateTime datetime) + { + var lastDayOfMonthDate = LastDayOfMonth(datetime); + return new Tuple(datetime.FirstDayOfMonth(), new DateTime(lastDayOfMonthDate.Year, lastDayOfMonthDate.Month, lastDayOfMonthDate.Day, 23, 59, 59)); + } + + /// + /// 取得某一天0点到当月最后一天的23:59:59时间范围 + /// + /// + /// + public static Tuple GetCurrentDateToLastDayRange(this DateTime datetime) + { + var lastDayOfMonthDate = LastDayOfMonth(datetime); + return new Tuple(datetime.Date, new DateTime(lastDayOfMonthDate.Year, lastDayOfMonthDate.Month, lastDayOfMonthDate.Day, 23, 59, 59)); + } + + /// + /// 取得某一天0点到23:59:59时间范围 + /// + /// + /// + public static Tuple GetCurrentDateRange(this DateTime datetime) + { + return new Tuple(datetime.Date, new DateTime(datetime.Year, datetime.Month, datetime.Day, 23, 59, 59)); + } + + /// + /// 获取指定枚举的所有 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; + } + + + + /// + /// 将传入的字符串中间部分字符替换成特殊字符 + /// + /// 需要替换的字符串 + /// 前保留长度 + /// 尾保留长度 + /// 特殊字符 + /// 被特殊字符替换的字符串 + 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; + } + } +} diff --git a/src/JiShe.CollectBus.Common/Helpers/JsonHelper.cs b/src/JiShe.CollectBus.Common/Helpers/JsonHelper.cs index 029ae1f..bdd46f6 100644 --- a/src/JiShe.CollectBus.Common/Helpers/JsonHelper.cs +++ b/src/JiShe.CollectBus.Common/Helpers/JsonHelper.cs @@ -123,4 +123,32 @@ namespace JiShe.CollectBus.Common.Helpers 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/src/JiShe.CollectBus.Common/Helpers/SelectResult.cs b/src/JiShe.CollectBus.Common/Helpers/SelectResult.cs new file mode 100644 index 0000000..4ccaf99 --- /dev/null +++ b/src/JiShe.CollectBus.Common/Helpers/SelectResult.cs @@ -0,0 +1,35 @@ +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/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs index 026a95a..71cc741 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs @@ -16,16 +16,27 @@ namespace JiShe.CollectBus.IoTDBProvider /// /// /// + /// 构建表模型方式,1 根据实体《T》直接显示指定Tag,2根据实体《T》的名称指定表名 /// - Task InsertAsync(T entity) where T : IoTEntity; + Task InsertAsync(T entity, int buildTabletMode) where T : IoTEntity; /// /// 批量插入数据 /// /// /// + /// 构建表模型方式,1 根据实体《T》直接显示指定Tag,2根据实体《T》的名称指定表名 /// - Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity; + Task BatchInsertAsync(IEnumerable entities, int buildTabletMode) where T : IoTEntity; + + + /// + /// 删除数据 + /// + /// + /// + /// + Task DeleteAsync(QueryOptions options) where T : IoTEntity; /// /// 查询数据 diff --git a/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj b/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj index 717b0f6..137cee3 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj +++ b/src/JiShe.CollectBus.IoTDBProvider/JiShe.CollectBus.IoTDBProvider.csproj @@ -10,4 +10,7 @@ + + + diff --git a/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs b/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs index 019bd28..d04cd48 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs @@ -11,6 +11,11 @@ namespace JiShe.CollectBus.IoTDBProvider /// public class QueryOptions { + /// + /// 表名或标签名 + /// + public required string TableNameOrTagName { get; set; } + /// /// 分页 /// diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/DeviceMetadata.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/DeviceMetadata.cs index 8e84776..0d5b408 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/DeviceMetadata.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/DeviceMetadata.cs @@ -13,17 +13,17 @@ namespace JiShe.CollectBus.IoTDBProvider public class DeviceMetadata { /// - /// 测量值集合,用于构建Table的测量值,也就是field参数 + /// 测量值集合,用于构建Table的测量值,也就是columnNames参数 /// - public List Measurements { get; } = new(); + public List ColumnNames { get; } = new(); /// - /// 列类型集合,用于构建Table的列类型,也就是columnCategory参数 + /// 列类型集合,用于构建Table的列类型,也就是columnCategories参数 /// public List ColumnCategories { get; } = new(); /// - /// 值类型集合,用于构建Table的值类型,也就是dataType参数 + /// 值类型集合,用于构建Table的值类型,也就是dataTypes参数 /// public ListDataTypes { get; } = new(); } diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs index 449884a..39e07c0 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs @@ -1,5 +1,8 @@ using Apache.IoTDB; using Apache.IoTDB.DataStructure; +using JiShe.CollectBus.Common.Extensions; +using JiShe.CollectBus.Common.Helpers; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Collections.Concurrent; @@ -19,8 +22,9 @@ namespace JiShe.CollectBus.IoTDBProvider private readonly IoTDBOptions _options; private readonly TableSessionPool _sessionPool; private static readonly ConcurrentDictionary _metadataCache = new(); + private readonly ILogger _logger; - public IoTDBProvider(IOptions options) + public IoTDBProvider(IOptions options, ILogger logger) { _options = options.Value; @@ -32,58 +36,20 @@ namespace JiShe.CollectBus.IoTDBProvider .Build(); _sessionPool.Open(false).Wait(); + _logger = logger; } - /// - /// 获取设备元数据 - /// - /// - /// - private DeviceMetadata GetMetadata() where T : IoTEntity - { - return _metadataCache.GetOrAdd(typeof(T), type => - { - var metadata = new DeviceMetadata(); - foreach (var prop in type.GetProperties()) - { - //标签列 - var attrTAG = prop.GetCustomAttribute(); - if (attrTAG != null) - { - metadata.ColumnCategories.Add(ColumnCategory.TAG); - } - - //属性列 - var attrATTRIBUTE = prop.GetCustomAttribute(); - if (attrATTRIBUTE != null) - { - metadata.ColumnCategories.Add(ColumnCategory.ATTRIBUTE); - } - - //数据列 - var attrFIELD = prop.GetCustomAttribute(); - if (attrFIELD != null) - { - metadata.ColumnCategories.Add(ColumnCategory.FIELD); - metadata.Measurements.Add(prop.Name); - metadata.DataTypes.Add(GetDataTypeFromStr(prop.PropertyType.Name)); - } - } - return metadata; - }); - } - /// /// 插入数据 /// /// /// /// - public async Task InsertAsync(T entity) where T : IoTEntity + public async Task InsertAsync(T entity, int buildTabletMode) where T : IoTEntity { var metadata = GetMetadata(); - var tablet = BuildTablet(new[] { entity }, metadata); + var tablet = BuildTablet(new[] { entity }, metadata, buildTabletMode); await _sessionPool.InsertAsync(tablet); } @@ -93,58 +59,46 @@ namespace JiShe.CollectBus.IoTDBProvider /// /// /// - public async Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity + public async Task BatchInsertAsync(IEnumerable entities, int buildTabletMode) where T : IoTEntity { - var metadata = GetMetadata(); + var metadata = GetMetadata(); var batchSize = 1000; var batches = entities.Chunk(batchSize); foreach (var batch in batches) { - var tablet = BuildTablet(batch, metadata); + var tablet = BuildTablet(batch, metadata, buildTabletMode); await _sessionPool.InsertAsync(tablet); } } + /// - /// 构建表模型 + /// 删除数据 /// /// - /// - /// + /// /// - private Tablet BuildTablet(IEnumerable entities, DeviceMetadata metadata) where T : IoTEntity + public async Task DeleteAsync(QueryOptions options) where T : IoTEntity { - var deviceId = DevicePathBuilder.GetDeviceId(entities.First()); - var timestamps = new List(); - var values = new List>(); + var query = BuildDeleteSQL(options); + var sessionDataSet = await _sessionPool.ExecuteQueryStatementAsync(query); - foreach (var entity in entities) + if (!sessionDataSet.HasNext()) { - timestamps.Add(entity.Timestamps); - var rowValues = new List(); - foreach (var measurement in metadata.Measurements) - { - var value = typeof(T).GetProperty(measurement)?.GetValue(entity); - if(value == null) - { - throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,属性{measurement}值为空,不符合IoTDB设计标准,请赋值以后重新处理。"); - } - rowValues.Add(value); - } - values.Add(rowValues); + _logger.LogWarning($"{typeof(T).Name} 删除数据时,没有返回受影响记录数量。"); + return 0; } - return new Tablet( - deviceId, - metadata.Measurements, - metadata.DataTypes, - values, - timestamps - ); + //获取唯一结果行 + var row = sessionDataSet.Next(); + return row.Values[0]; } + + + /// /// 查询数据 /// @@ -153,7 +107,7 @@ namespace JiShe.CollectBus.IoTDBProvider /// public async Task> QueryAsync(QueryOptions options) where T : IoTEntity, new() { - var query = BuildQuery(options); + var query = BuildQuerySQL(options); var sessionDataSet = await _sessionPool.ExecuteQueryStatementAsync(query); var result = new PagedResult @@ -165,18 +119,75 @@ namespace JiShe.CollectBus.IoTDBProvider return result; } + /// + /// 构建表模型 + /// + /// + /// 表实体 + /// 设备元数据 + /// 构建表模型方式,1 根据实体《T》直接显示指定Tag,2根据实体《T》的名称指定表名 + /// + private Tablet BuildTablet(IEnumerable entities, DeviceMetadata metadata, int buildTabletMode) where T : IoTEntity + { + var timestamps = new List(); + var values = new List>(); + + foreach (var entity in entities) + { + timestamps.Add(entity.Timestamps); + var rowValues = new List(); + foreach (var measurement in metadata.ColumnNames) + { + var value = typeof(T).GetProperty(measurement)?.GetValue(entity); + if (value == null) + { + throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,属性{measurement}值为空,不符合IoTDB设计标准,请赋值以后重新处理。"); + } + rowValues.Add(value); + } + values.Add(rowValues); + } + + if (buildTabletMode == 1) + { + return new Tablet( + DevicePathBuilder.GetDeviceId(entities.First()), + metadata.ColumnNames, + metadata.ColumnCategories, + metadata.DataTypes, + values, + timestamps + ); + } + else if (buildTabletMode == 2) + { + return new Tablet( + DevicePathBuilder.GetTableName(), + metadata.ColumnNames, + metadata.ColumnCategories, + metadata.DataTypes, + values, + timestamps + ); + } + else + { + throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,buildTabletMode参数值不正确,请赋值以后重新处理。"); + } + } + /// /// 构建查询语句 /// /// /// /// - private string BuildQuery(QueryOptions options) where T : IoTEntity + private string BuildQuerySQL(QueryOptions options) where T : IoTEntity { var metadata = GetMetadata(); var sb = new StringBuilder("SELECT "); - sb.AppendJoin(", ", metadata.Measurements); - sb.Append($" FROM {DevicePathBuilder.GetTableName()}"); + sb.AppendJoin(", ", metadata.ColumnNames); + sb.Append($" FROM {options.TableNameOrTagName}"); if (options.Conditions.Any()) { @@ -188,6 +199,30 @@ namespace JiShe.CollectBus.IoTDBProvider return sb.ToString(); } + /// + /// 构建删除语句 + /// + /// + /// + /// + private string BuildDeleteSQL(QueryOptions options) where T : IoTEntity + { + var metadata = GetMetadata(); + var sb = new StringBuilder("DELETE "); + + sb.Append($" FROM {options.TableNameOrTagName}"); + + sb.AppendJoin(", ", metadata.ColumnNames); + + if (options.Conditions.Any()) + { + sb.Append(" WHERE "); + sb.AppendJoin(" AND ", options.Conditions.Select(TranslateCondition)); + } + + return sb.ToString(); + } + /// /// 将查询条件转换为SQL语句 /// @@ -213,7 +248,7 @@ namespace JiShe.CollectBus.IoTDBProvider /// private async Task GetTotalCount(QueryOptions options) where T : IoTEntity { - var countQuery = $"SELECT COUNT(*) FROM {DevicePathBuilder.GetTableName()}"; + var countQuery = $"SELECT COUNT(*) FROM {options.TableNameOrTagName}"; if (options.Conditions.Any()) { countQuery += " WHERE " + string.Join(" AND ", options.Conditions.Select(TranslateCondition)); @@ -246,7 +281,7 @@ namespace JiShe.CollectBus.IoTDBProvider }; - foreach (var measurement in metadata.Measurements) + foreach (var measurement in metadata.ColumnNames) { var value = record.Values; @@ -263,7 +298,7 @@ namespace JiShe.CollectBus.IoTDBProvider } return results; } - + /// /// 释放资源 /// @@ -272,23 +307,196 @@ namespace JiShe.CollectBus.IoTDBProvider _sessionPool?.Close().Wait(); } - private TSDataType GetDataTypeFromStr(string str) + /// + /// 获取设备元数据 + /// + /// + /// + private DeviceMetadata GetMetadata() where T : IoTEntity { - return str switch + return _metadataCache.GetOrAdd(typeof(T), type => { - "BOOLEAN" => TSDataType.BOOLEAN, - "INT32" => TSDataType.INT32, - "INT64" => TSDataType.INT64, - "FLOAT" => TSDataType.FLOAT, - "DOUBLE" => TSDataType.DOUBLE, - "TEXT" => TSDataType.TEXT, - "NULLTYPE" => TSDataType.NONE, - "TIMESTAMP" => TSDataType.TIMESTAMP, - "DATE" => TSDataType.DATE, - "BLOB" => TSDataType.BLOB, - "STRING" => TSDataType.STRING, - _ => TSDataType.STRING - }; + var columns = CollectColumnMetadata(type); + var metadata = BuildDeviceMetadata(columns); + return metadata; + }); + + //return _metadataCache.GetOrAdd(typeof(T), type => + //{ + // var metadata = new DeviceMetadata(); + // List> columns = new(); + // foreach (var prop in type.GetProperties()) + // { + // //标签列 + // var attrTAG = prop.GetCustomAttribute(); + // if (attrTAG != null) + // { + // columns.Add(Tuple.Create(prop.PropertyType.Name, ColumnCategory.TAG, GetDataTypeFromStr(prop.PropertyType.Name))); + // continue; + // } + + // //属性列 + // var attrATTRIBUTE = prop.GetCustomAttribute(); + // if (attrATTRIBUTE != null) + // { + // columns.Add(Tuple.Create(prop.PropertyType.Name, ColumnCategory.ATTRIBUTE, GetDataTypeFromStr(prop.PropertyType.Name))); + // continue; + // } + + // //数据列 + // var attrFIELD = prop.GetCustomAttribute(); + // if (attrFIELD != null) + // { + // columns.Add(Tuple.Create(prop.PropertyType.Name, ColumnCategory.FIELD, GetDataTypeFromStr(prop.PropertyType.Name))); + // } + // } + // var columnCategories = EnumExtensions.ToEnumDictionary(); + // foreach (var item in columnCategories) + // { + // if (item.Value == ColumnCategory.TAG) + // { + // metadata.ColumnNames.AddRange(columns.Where(d => d.Item2 == ColumnCategory.FIELD).Select(d => d.Item1).ToList()); + // metadata.ColumnCategories.AddRange(columns.Where(d => d.Item2 == ColumnCategory.TAG).Select(d => d.Item2).ToList()); + // metadata.DataTypes.AddRange(columns.Where(d => d.Item2 == ColumnCategory.TAG).Select(d => d.Item3).ToList()); + // } + // else if (item.Value == ColumnCategory.ATTRIBUTE) + // { + // metadata.ColumnNames.AddRange(columns.Where(d => d.Item2 == ColumnCategory.ATTRIBUTE).Select(d => d.Item1).ToList()); + // metadata.ColumnCategories.AddRange(columns.Where(d => d.Item2 == ColumnCategory.ATTRIBUTE).Select(d => d.Item2).ToList()); + // metadata.DataTypes.AddRange(columns.Where(d => d.Item2 == ColumnCategory.ATTRIBUTE).Select(d => d.Item3).ToList()); + // } + // else if (item.Value == ColumnCategory.FIELD) + // { + // metadata.ColumnNames.AddRange(columns.Where(d => d.Item2 == ColumnCategory.FIELD).Select(d => d.Item1).ToList()); + // metadata.ColumnCategories.AddRange(columns.Where(d => d.Item2 == ColumnCategory.FIELD).Select(d => d.Item2).ToList()); + // metadata.DataTypes.AddRange(columns.Where(d => d.Item2 == ColumnCategory.FIELD).Select(d => d.Item3).ToList()); + // } + // } + + // return metadata; + //}); } + + /// + /// 获取设备元数据的列 + /// + /// + /// + private List CollectColumnMetadata(Type type) + { + var columns = new List(); + + foreach (var prop in type.GetProperties()) + { + //按优先级顺序检查属性,避免重复反射 + ColumnInfo? column = prop.GetCustomAttribute() is not null ? new ColumnInfo( + name: prop.Name, //使用属性名 + category: ColumnCategory.TAG, + dataType: GetDataTypeFromTypeName(prop.PropertyType.Name) + ) : prop.GetCustomAttribute() is not null ? new ColumnInfo( + prop.Name, + ColumnCategory.ATTRIBUTE, + GetDataTypeFromTypeName(prop.PropertyType.Name) + ) : prop.GetCustomAttribute() is not null ? new ColumnInfo( + prop.Name, + ColumnCategory.FIELD, + GetDataTypeFromTypeName(prop.PropertyType.Name) + ) : null; + + if (column.HasValue) + { + columns.Add(column.Value); + } + } + return columns; + } + + /// + /// 构建设备元数据 + /// + /// + /// + private DeviceMetadata BuildDeviceMetadata(List columns) + { + var metadata = new DeviceMetadata(); + + //按业务逻辑顺序处理(TAG -> FIELD -> ATTRIBUTE) + 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); + + return metadata; + } + + /// + /// 处理不同列类型的逻辑 + /// + /// + /// + /// + 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 readonly struct ColumnInfo + { + public string Name { get; } + public ColumnCategory Category { get; } + public TSDataType DataType { get; } + + public ColumnInfo(string name, ColumnCategory category, TSDataType dataType) + { + Name = name; + Category = category; + DataType = dataType; + } + } + + /// + /// 根据类型名称获取对应的 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, + ["TIMESTAMP"] = TSDataType.TIMESTAMP, + ["DATE"] = TSDataType.DATE, + ["BLOB"] = TSDataType.BLOB, + ["STRING"] = TSDataType.STRING + }; } } From 50639f6fcf204c42c60be43f254418b9bfc7250d Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Thu, 3 Apr 2025 16:46:26 +0800 Subject: [PATCH 10/18] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Samples/SampleAppService.cs | 8 +- .../JiShe.CollectBus.Host.csproj | 6 ++ src/JiShe.CollectBus.Host/appsettings.json | 14 +--- .../Options/IoTDBOptions.cs | 10 +++ .../Provider/DevicePathBuilder.cs | 6 +- .../Provider/IoTDBProvider.cs | 75 +++++-------------- 6 files changed, 46 insertions(+), 73 deletions(-) diff --git a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs index 9c2a86f..4b62555 100644 --- a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs +++ b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs @@ -6,6 +6,7 @@ using JiShe.CollectBus.FreeSql; using JiShe.CollectBus.IoTDBProvider; using JiShe.CollectBus.IotSystems.PrepayModel; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace JiShe.CollectBus.Samples; @@ -20,11 +21,12 @@ public class SampleAppService : CollectBusAppService, ISampleAppService } - public async Task AddReadingAsync() + [HttpGet] + public async Task AddReadingAsync(int buildTabletMode = 1) { ElectricityMeter meter = new ElectricityMeter() { - SystemName = "Energy", + SystemName = "energy", DeviceId = "402440506", DeviceType = "Ammeter", Current = 10, @@ -32,7 +34,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService ProjectCode = "10059", Voltage = 10 }; - await _iotDBProvider.InsertAsync(meter,2); + await _iotDBProvider.InsertAsync(meter, buildTabletMode); } public Task GetAsync() diff --git a/src/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj b/src/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj index b70752b..7d9bc47 100644 --- a/src/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj +++ b/src/JiShe.CollectBus.Host/JiShe.CollectBus.Host.csproj @@ -58,6 +58,12 @@ + + + Always + + + Always diff --git a/src/JiShe.CollectBus.Host/appsettings.json b/src/JiShe.CollectBus.Host/appsettings.json index b506272..05c99de 100644 --- a/src/JiShe.CollectBus.Host/appsettings.json +++ b/src/JiShe.CollectBus.Host/appsettings.json @@ -73,18 +73,12 @@ "Version": "V1" } ], - "Cap": { - "RabbitMq": { - "HostName": "118.190.144.92", - "UserName": "collectbus", - "Password": "123456", - "Port": 5672 - } - }, "IoTDBOptions": { "UserName": "root", "Password": "root", - "ClusterList": [ "192.168.56.102:6667"], - "PoolSize": 10 + "ClusterList": [ "192.168.56.102:6667" ], + "PoolSize": 10, + "DataBaseName": "energy", + "OpenDebugMode": true } } \ No newline at end of file diff --git a/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs b/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs index 5cdd256..6a762b1 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs @@ -11,6 +11,11 @@ namespace JiShe.CollectBus.IoTDBProvider /// public class IoTDBOptions { + /// + /// 数据库名称 + /// + public string DataBaseName { get; set; } + /// /// 集群列表 /// @@ -28,5 +33,10 @@ namespace JiShe.CollectBus.IoTDBProvider /// 连接池大小 /// public int PoolSize { get; set; } = 3; + + /// + /// 是否开启调试模式,生产环境请关闭,因为底层的实现方式,可能会导致内存持续增长。 + /// + public bool OpenDebugMode { get; set;} } } diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs index 3166ca6..cfab5c2 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs @@ -12,14 +12,14 @@ namespace JiShe.CollectBus.IoTDBProvider public static class DevicePathBuilder { /// - /// 构建设备路径 + /// 构建设备路径,由于路径的层级约束规范不能是纯数字字符,所以需要做特殊处理。 /// /// /// /// public static string GetDeviceId(T entity) where T : IoTEntity { - return $"root.{entity.SystemName}.{entity.ProjectCode}.{entity.DeviceId}"; + return $"root.{entity.SystemName.ToLower()}.`{entity.ProjectCode}`.`{entity.DeviceId}`"; } @@ -32,7 +32,7 @@ namespace JiShe.CollectBus.IoTDBProvider public static string GetTableName() where T : IoTEntity { var type = typeof(T); - return $"{type.Name}"; + return $"{type.Name.ToLower()}"; } } diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs index 39e07c0..b911876 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs @@ -32,10 +32,18 @@ namespace JiShe.CollectBus.IoTDBProvider .SetNodeUrls(_options.ClusterList) .SetUsername(_options.UserName) .SetPassword(_options.Password) + .SetDatabase(_options.DataBaseName) .SetFetchSize(_options.PoolSize) .Build(); _sessionPool.Open(false).Wait(); + if (_options.OpenDebugMode) + { + _sessionPool.OpenDebugMode(builder => + { + builder.AddConsole(); + }); + } _logger = logger; } @@ -50,7 +58,11 @@ namespace JiShe.CollectBus.IoTDBProvider var metadata = GetMetadata(); var tablet = BuildTablet(new[] { entity }, metadata, buildTabletMode); - await _sessionPool.InsertAsync(tablet); + var result = await _sessionPool.InsertAsync(tablet); + if (result <=0) + { + _logger.LogWarning($"{typeof(T).Name}插入数据没有成功"); + } } /// @@ -69,7 +81,11 @@ namespace JiShe.CollectBus.IoTDBProvider foreach (var batch in batches) { var tablet = BuildTablet(batch, metadata, buildTabletMode); - await _sessionPool.InsertAsync(tablet); + var result = await _sessionPool.InsertAsync(tablet); + if (result <= 0) + { + _logger.LogWarning($"{typeof(T).Name} 批量插入数据第{batch}批次没有成功,共{batches}批次。"); + } } } @@ -320,61 +336,6 @@ namespace JiShe.CollectBus.IoTDBProvider var metadata = BuildDeviceMetadata(columns); return metadata; }); - - //return _metadataCache.GetOrAdd(typeof(T), type => - //{ - // var metadata = new DeviceMetadata(); - // List> columns = new(); - // foreach (var prop in type.GetProperties()) - // { - // //标签列 - // var attrTAG = prop.GetCustomAttribute(); - // if (attrTAG != null) - // { - // columns.Add(Tuple.Create(prop.PropertyType.Name, ColumnCategory.TAG, GetDataTypeFromStr(prop.PropertyType.Name))); - // continue; - // } - - // //属性列 - // var attrATTRIBUTE = prop.GetCustomAttribute(); - // if (attrATTRIBUTE != null) - // { - // columns.Add(Tuple.Create(prop.PropertyType.Name, ColumnCategory.ATTRIBUTE, GetDataTypeFromStr(prop.PropertyType.Name))); - // continue; - // } - - // //数据列 - // var attrFIELD = prop.GetCustomAttribute(); - // if (attrFIELD != null) - // { - // columns.Add(Tuple.Create(prop.PropertyType.Name, ColumnCategory.FIELD, GetDataTypeFromStr(prop.PropertyType.Name))); - // } - // } - // var columnCategories = EnumExtensions.ToEnumDictionary(); - // foreach (var item in columnCategories) - // { - // if (item.Value == ColumnCategory.TAG) - // { - // metadata.ColumnNames.AddRange(columns.Where(d => d.Item2 == ColumnCategory.FIELD).Select(d => d.Item1).ToList()); - // metadata.ColumnCategories.AddRange(columns.Where(d => d.Item2 == ColumnCategory.TAG).Select(d => d.Item2).ToList()); - // metadata.DataTypes.AddRange(columns.Where(d => d.Item2 == ColumnCategory.TAG).Select(d => d.Item3).ToList()); - // } - // else if (item.Value == ColumnCategory.ATTRIBUTE) - // { - // metadata.ColumnNames.AddRange(columns.Where(d => d.Item2 == ColumnCategory.ATTRIBUTE).Select(d => d.Item1).ToList()); - // metadata.ColumnCategories.AddRange(columns.Where(d => d.Item2 == ColumnCategory.ATTRIBUTE).Select(d => d.Item2).ToList()); - // metadata.DataTypes.AddRange(columns.Where(d => d.Item2 == ColumnCategory.ATTRIBUTE).Select(d => d.Item3).ToList()); - // } - // else if (item.Value == ColumnCategory.FIELD) - // { - // metadata.ColumnNames.AddRange(columns.Where(d => d.Item2 == ColumnCategory.FIELD).Select(d => d.Item1).ToList()); - // metadata.ColumnCategories.AddRange(columns.Where(d => d.Item2 == ColumnCategory.FIELD).Select(d => d.Item2).ToList()); - // metadata.DataTypes.AddRange(columns.Where(d => d.Item2 == ColumnCategory.FIELD).Select(d => d.Item3).ToList()); - // } - // } - - // return metadata; - //}); } /// From 5d0a2088aeb60019fab4fd5ec9f853b864225f44 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Mon, 7 Apr 2025 10:56:40 +0800 Subject: [PATCH 11/18] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Samples/SampleAppService.cs | 95 ++++++++++++++++++- src/JiShe.CollectBus.Host/appsettings.json | 4 +- .../Options/IoTDBOptions.cs | 9 +- .../Provider/IoTDBProvider.cs | 31 ++++-- 4 files changed, 128 insertions(+), 11 deletions(-) diff --git a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs index 4b62555..9b853fd 100644 --- a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs +++ b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs @@ -1,12 +1,16 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Apache.IoTDB.DataStructure; +using Apache.IoTDB; +using Confluent.Kafka; using JiShe.CollectBus.Ammeters; using JiShe.CollectBus.FreeSql; using JiShe.CollectBus.IoTDBProvider; using JiShe.CollectBus.IotSystems.PrepayModel; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; namespace JiShe.CollectBus.Samples; @@ -14,10 +18,12 @@ public class SampleAppService : CollectBusAppService, ISampleAppService { private readonly IIoTDBProvider _iotDBProvider; + private readonly IoTDBOptions _options; - public SampleAppService(IIoTDBProvider iotDBProvider) + public SampleAppService(IIoTDBProvider iotDBProvider, IOptions options) { _iotDBProvider = iotDBProvider; + _options = options.Value; } @@ -37,6 +43,93 @@ public class SampleAppService : CollectBusAppService, ISampleAppService await _iotDBProvider.InsertAsync(meter, buildTabletMode); } + + [HttpGet] + public async Task AddReading2Async() + { + var tableSessionPool = new TableSessionPool.Builder() + .SetNodeUrls(_options.ClusterList) + .SetUsername(_options.UserName) + .SetPassword(_options.Password) + .SetFetchSize(1024) + .Build(); + + await tableSessionPool.Open(false); + + + + await tableSessionPool.ExecuteNonQueryStatementAsync("CREATE DATABASE test1"); + await tableSessionPool.ExecuteNonQueryStatementAsync("CREATE DATABASE test2"); + + //await tableSessionPool.ExecuteNonQueryStatementAsync("use test2"); + + //// or use full qualified table name + //await tableSessionPool.ExecuteNonQueryStatementAsync( + // "create table test1.table1(region_id STRING TAG, plant_id STRING TAG, device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, humidity DOUBLE FIELD) with (TTL=3600000)"); + + //await tableSessionPool.ExecuteNonQueryStatementAsync( + // "create table table2(region_id STRING TAG, plant_id STRING TAG, color STRING ATTRIBUTE, temperature FLOAT FIELD, speed DOUBLE FIELD) with (TTL=6600000)"); + + //// show tables from current database + //var res = await tableSessionPool.ExecuteQueryStatementAsync("SHOW TABLES"); + //res.ShowTableNames(); + //while (res.HasNext()) Console.WriteLine(res.Next()); + //await res.Close(); + + //// show tables by specifying another database + //// using SHOW tables FROM + //res = await tableSessionPool.ExecuteQueryStatementAsync("SHOW TABLES FROM test1"); + //res.ShowTableNames(); + //while (res.HasNext()) Console.WriteLine(res.Next()); + //await res.Close(); + + var tableName = "testTable1"; + List columnNames = + new List { + "region_id", + "plant_id", + "device_id", + "model", + "temperature", + "humidity" }; + List dataTypes = + new List{ + TSDataType.STRING, + TSDataType.STRING, + TSDataType.STRING, + TSDataType.STRING, + TSDataType.FLOAT, + TSDataType.DOUBLE}; + List columnCategories = + new List{ + ColumnCategory.TAG, + ColumnCategory.TAG, + ColumnCategory.TAG, + ColumnCategory.ATTRIBUTE, + ColumnCategory.FIELD, + ColumnCategory.FIELD}; + var values = new List> { }; + var timestamps = new List { }; + for (long timestamp = 0; timestamp < 100; timestamp++) + { + timestamps.Add(timestamp); + values.Add(new List { "1", "5", "3", "A", 1.23F + timestamp, 111.1 + timestamp +}); + } + var tablet = new Tablet(tableName, columnNames, columnCategories, dataTypes, values, timestamps); + await tableSessionPool.InsertAsync(tablet); + + + //var res = await tableSessionPool.ExecuteQueryStatementAsync("select * from testTable1 " + // + "where region_id = '1' and plant_id in ('3', '5') and device_id = '3'"); + // res.ShowTableNames(); + // while (res.HasNext()) Console.WriteLine(res.Next()); + // await res.Close(); + + await tableSessionPool.Close(); + } + + public Task GetAsync() { return Task.FromResult( diff --git a/src/JiShe.CollectBus.Host/appsettings.json b/src/JiShe.CollectBus.Host/appsettings.json index 05c99de..83eeb5b 100644 --- a/src/JiShe.CollectBus.Host/appsettings.json +++ b/src/JiShe.CollectBus.Host/appsettings.json @@ -77,8 +77,8 @@ "UserName": "root", "Password": "root", "ClusterList": [ "192.168.56.102:6667" ], - "PoolSize": 10, - "DataBaseName": "energy", + "PoolSize": 2, + "DataBaseName": "root.energy2", "OpenDebugMode": true } } \ No newline at end of file diff --git a/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs b/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs index 6a762b1..e9be4b4 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs @@ -27,12 +27,17 @@ namespace JiShe.CollectBus.IoTDBProvider /// /// 密码 /// - public string Password { get; set; } + public string Password { get; set; } /// /// 连接池大小 /// - public int PoolSize { get; set; } = 3; + public int PoolSize { get; set; } = 2; + + /// + /// 查询时,每次查询的数据量,默认1024 + /// + public int FetchSize { get; set; } = 1024; /// /// 是否开启调试模式,生产环境请关闭,因为底层的实现方式,可能会导致内存持续增长。 diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs index b911876..eb83aca 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs @@ -2,6 +2,7 @@ using Apache.IoTDB.DataStructure; using JiShe.CollectBus.Common.Extensions; using JiShe.CollectBus.Common.Helpers; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; @@ -22,18 +23,24 @@ namespace JiShe.CollectBus.IoTDBProvider private readonly IoTDBOptions _options; private readonly TableSessionPool _sessionPool; private static readonly ConcurrentDictionary _metadataCache = new(); - private readonly ILogger _logger; + private readonly ILogger _logger; public IoTDBProvider(IOptions options, ILogger logger) { _options = options.Value; _sessionPool = new TableSessionPool.Builder() +#if DEBUG + .SetHost(_options.ClusterList.FirstOrDefault()?.Split(":")[0]) + .SetHost(_options.ClusterList.FirstOrDefault()?.Split(":")[1]) +#else .SetNodeUrls(_options.ClusterList) +#endif .SetUsername(_options.UserName) .SetPassword(_options.Password) + .SetFetchSize(_options.FetchSize) + .SetPoolSize(_options.PoolSize) .SetDatabase(_options.DataBaseName) - .SetFetchSize(_options.PoolSize) .Build(); _sessionPool.Open(false).Wait(); @@ -58,10 +65,23 @@ namespace JiShe.CollectBus.IoTDBProvider var metadata = GetMetadata(); var tablet = BuildTablet(new[] { entity }, metadata, buildTabletMode); - var result = await _sessionPool.InsertAsync(tablet); - if (result <=0) + int result = 0; + if (buildTabletMode == 1) { - _logger.LogWarning($"{typeof(T).Name}插入数据没有成功"); + result = await _sessionPool.InsertAsync(tablet); + } + else if(buildTabletMode == 2) + { + result = await _sessionPool.InsertAsync(tablet); + } + else + { + throw new Exception($"{nameof(InsertAsync)} 表模型{typeof(T).Name}记录入库时,buildTabletMode参数值不正确,请赋值以后重新处理。"); + } + + if (result <= 0) + { + _logger.LogError($"{typeof(T).Name}插入数据没有成功"); } } @@ -169,7 +189,6 @@ namespace JiShe.CollectBus.IoTDBProvider return new Tablet( DevicePathBuilder.GetDeviceId(entities.First()), metadata.ColumnNames, - metadata.ColumnCategories, metadata.DataTypes, values, timestamps From 7696b7379be5510416b7fe2f065e5a82cc234c54 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Mon, 7 Apr 2025 16:44:25 +0800 Subject: [PATCH 12/18] =?UTF-8?q?=E5=AE=9E=E7=8E=B0SessionPool=E7=81=B5?= =?UTF-8?q?=E6=B4=BB=E5=88=87=E6=8D=A2=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Samples/SampleAppService.cs | 106 ++------ src/JiShe.CollectBus.Host/appsettings.json | 7 +- .../CollectBusIoTDBModule.cs | 16 +- .../Context/IoTDBRuntimeContext.cs | 36 +++ .../Interface/IIoTDBProvider.cs | 14 +- .../Interface/IIoTDBSessionFactory.cs | 16 ++ .../Interface/IIoTDBSessionPool.cs | 35 +++ .../Options/IoTDBOptions.cs | 7 +- .../Options/QueryOptions.cs | 6 +- .../Provider/DevicePathBuilder.cs | 2 +- .../Provider/IoTDBProvider.cs | 231 ++++++++++-------- .../Provider/IoTDBSessionFactory.cs | 36 +++ .../Provider/IoTEntity.cs | 4 +- .../Provider/SessionPoolAdapter.cs | 74 ++++++ .../Provider/TableSessionPoolAdapter.cs | 72 ++++++ 15 files changed, 464 insertions(+), 198 deletions(-) create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Context/IoTDBRuntimeContext.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBSessionFactory.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBSessionPool.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBSessionFactory.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Provider/SessionPoolAdapter.cs create mode 100644 src/JiShe.CollectBus.IoTDBProvider/Provider/TableSessionPoolAdapter.cs diff --git a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs index 9b853fd..8990319 100644 --- a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs +++ b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs @@ -11,6 +11,7 @@ using JiShe.CollectBus.IotSystems.PrepayModel; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; +using JiShe.CollectBus.IoTDBProvider.Context; namespace JiShe.CollectBus.Samples; @@ -18,17 +19,20 @@ public class SampleAppService : CollectBusAppService, ISampleAppService { private readonly IIoTDBProvider _iotDBProvider; + private readonly IoTDBRuntimeContext _dbContext; private readonly IoTDBOptions _options; - public SampleAppService(IIoTDBProvider iotDBProvider, IOptions options) + public SampleAppService(IIoTDBProvider iotDBProvider, IOptions options, + IoTDBRuntimeContext dbContext) { _iotDBProvider = iotDBProvider; _options = options.Value; + _dbContext = dbContext; } [HttpGet] - public async Task AddReadingAsync(int buildTabletMode = 1) + public async Task UseSessionPool() { ElectricityMeter meter = new ElectricityMeter() { @@ -38,95 +42,31 @@ public class SampleAppService : CollectBusAppService, ISampleAppService Current = 10, MeterModel = "DDZY-1980", ProjectCode = "10059", - Voltage = 10 + Voltage = 10, + Timestamps = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), }; - await _iotDBProvider.InsertAsync(meter, buildTabletMode); + await _iotDBProvider.InsertAsync(meter); } [HttpGet] - public async Task AddReading2Async() + public async Task UseTableSessionPool() { - var tableSessionPool = new TableSessionPool.Builder() - .SetNodeUrls(_options.ClusterList) - .SetUsername(_options.UserName) - .SetPassword(_options.Password) - .SetFetchSize(1024) - .Build(); + //_dbContext.UseTableSessionPool = true; + _iotDBProvider.SwitchSessionPool(true); - await tableSessionPool.Open(false); - - - - await tableSessionPool.ExecuteNonQueryStatementAsync("CREATE DATABASE test1"); - await tableSessionPool.ExecuteNonQueryStatementAsync("CREATE DATABASE test2"); - - //await tableSessionPool.ExecuteNonQueryStatementAsync("use test2"); - - //// or use full qualified table name - //await tableSessionPool.ExecuteNonQueryStatementAsync( - // "create table test1.table1(region_id STRING TAG, plant_id STRING TAG, device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, humidity DOUBLE FIELD) with (TTL=3600000)"); - - //await tableSessionPool.ExecuteNonQueryStatementAsync( - // "create table table2(region_id STRING TAG, plant_id STRING TAG, color STRING ATTRIBUTE, temperature FLOAT FIELD, speed DOUBLE FIELD) with (TTL=6600000)"); - - //// show tables from current database - //var res = await tableSessionPool.ExecuteQueryStatementAsync("SHOW TABLES"); - //res.ShowTableNames(); - //while (res.HasNext()) Console.WriteLine(res.Next()); - //await res.Close(); - - //// show tables by specifying another database - //// using SHOW tables FROM - //res = await tableSessionPool.ExecuteQueryStatementAsync("SHOW TABLES FROM test1"); - //res.ShowTableNames(); - //while (res.HasNext()) Console.WriteLine(res.Next()); - //await res.Close(); - - var tableName = "testTable1"; - List columnNames = - new List { - "region_id", - "plant_id", - "device_id", - "model", - "temperature", - "humidity" }; - List dataTypes = - new List{ - TSDataType.STRING, - TSDataType.STRING, - TSDataType.STRING, - TSDataType.STRING, - TSDataType.FLOAT, - TSDataType.DOUBLE}; - List columnCategories = - new List{ - ColumnCategory.TAG, - ColumnCategory.TAG, - ColumnCategory.TAG, - ColumnCategory.ATTRIBUTE, - ColumnCategory.FIELD, - ColumnCategory.FIELD}; - var values = new List> { }; - var timestamps = new List { }; - for (long timestamp = 0; timestamp < 100; timestamp++) + ElectricityMeter meter = new ElectricityMeter() { - timestamps.Add(timestamp); - values.Add(new List { "1", "5", "3", "A", 1.23F + timestamp, 111.1 + timestamp -}); - } - var tablet = new Tablet(tableName, columnNames, columnCategories, dataTypes, values, timestamps); - await tableSessionPool.InsertAsync(tablet); - - - //var res = await tableSessionPool.ExecuteQueryStatementAsync("select * from testTable1 " - // + "where region_id = '1' and plant_id in ('3', '5') and device_id = '3'"); - // res.ShowTableNames(); - // while (res.HasNext()) Console.WriteLine(res.Next()); - // await res.Close(); - - await tableSessionPool.Close(); + SystemName = "energy", + DeviceId = "402440506", + DeviceType = "Ammeter", + Current = 10, + MeterModel = "DDZY-1980", + ProjectCode = "10059", + Voltage = 10, + Timestamps = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), + }; + await _iotDBProvider.InsertAsync(meter); } diff --git a/src/JiShe.CollectBus.Host/appsettings.json b/src/JiShe.CollectBus.Host/appsettings.json index 83eeb5b..3d51171 100644 --- a/src/JiShe.CollectBus.Host/appsettings.json +++ b/src/JiShe.CollectBus.Host/appsettings.json @@ -40,7 +40,7 @@ "EnergyDB": "server=118.190.144.92;database=db_energy;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False" }, "Redis": { - "Configuration": "118.190.144.92:6379,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", + "Configuration": "118.190.144.92:6380,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", "DefaultDB": "14", "HangfireDB": "15" }, @@ -78,7 +78,8 @@ "Password": "root", "ClusterList": [ "192.168.56.102:6667" ], "PoolSize": 2, - "DataBaseName": "root.energy2", - "OpenDebugMode": true + "DataBaseName": "energy", + "OpenDebugMode": true, + "UseTableSessionPoolByDefault": false } } \ No newline at end of file diff --git a/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs b/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs index 5a8902c..444ab4e 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs @@ -1,4 +1,7 @@ -using Microsoft.Extensions.DependencyInjection; +using JiShe.CollectBus.IoTDBProvider.Context; +using JiShe.CollectBus.IoTDBProvider.Interface; +using JiShe.CollectBus.IoTDBProvider.Provider; +using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Linq; @@ -13,7 +16,16 @@ namespace JiShe.CollectBus.IoTDBProvider public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.Configure(context.Services.GetConfiguration().GetSection(nameof(IoTDBOptions))); - context.Services.AddSingleton(); + + // 注册上下文为Scoped + context.Services.AddScoped(); + + // 注册Session工厂 + context.Services.AddSingleton(); + + // 注册Provider + context.Services.AddScoped(); + } } } diff --git a/src/JiShe.CollectBus.IoTDBProvider/Context/IoTDBRuntimeContext.cs b/src/JiShe.CollectBus.IoTDBProvider/Context/IoTDBRuntimeContext.cs new file mode 100644 index 0000000..41f48cb --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Context/IoTDBRuntimeContext.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider.Context +{ + /// + /// IoTDB SessionPool 运行时上下文 + /// + public class IoTDBRuntimeContext + { + private readonly bool _defaultValue; + + public IoTDBRuntimeContext(IOptions options) + { + _defaultValue = options.Value.UseTableSessionPoolByDefault; + UseTableSessionPool = _defaultValue; + } + + /// + /// 是否使用表模型存储, 默认false,使用tree模型存储 + /// + public bool UseTableSessionPool { get; set; } + + /// + /// 重置为默认值 + /// + public void ResetToDefault() + { + UseTableSessionPool = _defaultValue; + } + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs index 71cc741..b67f17d 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBProvider.cs @@ -7,27 +7,31 @@ using System.Threading.Tasks; namespace JiShe.CollectBus.IoTDBProvider { /// - /// IoTDB数据源 + /// IoTDB数据源,数据库能同时存多个时序模型,但数据是完全隔离的,不能跨时序模型查询,通过连接字符串配置 /// public interface IIoTDBProvider { + /// + /// 切换 SessionPool + /// + /// 是否使用表模型 + void SwitchSessionPool(bool useTableSession); + /// /// 插入数据 /// /// /// - /// 构建表模型方式,1 根据实体《T》直接显示指定Tag,2根据实体《T》的名称指定表名 /// - Task InsertAsync(T entity, int buildTabletMode) where T : IoTEntity; + Task InsertAsync(T entity) where T : IoTEntity; /// /// 批量插入数据 /// /// /// - /// 构建表模型方式,1 根据实体《T》直接显示指定Tag,2根据实体《T》的名称指定表名 /// - Task BatchInsertAsync(IEnumerable entities, int buildTabletMode) where T : IoTEntity; + Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity; /// diff --git a/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBSessionFactory.cs b/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBSessionFactory.cs new file mode 100644 index 0000000..b904ba0 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBSessionFactory.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider.Interface +{ + /// + /// Session 工厂接口 + /// + public interface IIoTDBSessionFactory + { + IIoTDBSessionPool GetSessionPool(bool useTableSession); + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBSessionPool.cs b/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBSessionPool.cs new file mode 100644 index 0000000..be2c2b7 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Interface/IIoTDBSessionPool.cs @@ -0,0 +1,35 @@ +using Apache.IoTDB.DataStructure; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider.Interface +{ + /// + /// Session 连接池 + /// + public interface IIoTDBSessionPool : IDisposable + { + /// + /// 打开连接池 + /// + /// + Task OpenAsync(); + + /// + /// 插入数据 + /// + /// + /// + Task InsertAsync(Tablet tablet); + + /// + /// 查询数据 + /// + /// + /// + Task ExecuteQueryStatementAsync(string sql); + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs b/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs index e9be4b4..62cbd92 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Options/IoTDBOptions.cs @@ -12,7 +12,7 @@ namespace JiShe.CollectBus.IoTDBProvider public class IoTDBOptions { /// - /// 数据库名称 + /// 数据库名称,表模型才有,树模型为空 /// public string DataBaseName { get; set; } @@ -43,5 +43,10 @@ namespace JiShe.CollectBus.IoTDBProvider /// 是否开启调试模式,生产环境请关闭,因为底层的实现方式,可能会导致内存持续增长。 /// public bool OpenDebugMode { get; set;} + + /// + /// 是否使用表模型存储, 默认false,使用tree模型存储 + /// + public bool UseTableSessionPoolByDefault { get; set; } = false; } } diff --git a/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs b/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs index d04cd48..3600f12 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Options/QueryOptions.cs @@ -12,18 +12,20 @@ namespace JiShe.CollectBus.IoTDBProvider public class QueryOptions { /// - /// 表名或标签名 + /// 表模型的表名称或者树模型的设备路径 /// - public required string TableNameOrTagName { get; set; } + public required string TableNameOrTreePath { get; set; } /// /// 分页 /// public int Page { get; set; } + /// /// 分页大小 /// public int PageSize { get; set; } + /// /// 查询条件 /// diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs index cfab5c2..e170577 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/DevicePathBuilder.cs @@ -17,7 +17,7 @@ namespace JiShe.CollectBus.IoTDBProvider /// /// /// - public static string GetDeviceId(T entity) where T : IoTEntity + public static string GetDevicePath(T entity) where T : IoTEntity { return $"root.{entity.SystemName.ToLower()}.`{entity.ProjectCode}`.`{entity.DeviceId}`"; } diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs index eb83aca..4046ba9 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs @@ -2,6 +2,9 @@ using Apache.IoTDB.DataStructure; using JiShe.CollectBus.Common.Extensions; using JiShe.CollectBus.Common.Helpers; +using JiShe.CollectBus.IoTDBProvider.Context; +using JiShe.CollectBus.IoTDBProvider.Interface; +using JiShe.CollectBus.IoTDBProvider.Provider; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -18,40 +21,54 @@ namespace JiShe.CollectBus.IoTDBProvider /// /// IoTDB数据源 /// - public class IoTDBProvider : IIoTDBProvider, IDisposable + public class IoTDBProvider : IIoTDBProvider { - private readonly IoTDBOptions _options; - private readonly TableSessionPool _sessionPool; + private IIoTDBSessionPool _currentSession; private static readonly ConcurrentDictionary _metadataCache = new(); - private readonly ILogger _logger; + private readonly ILogger _logger; + private readonly IIoTDBSessionFactory _sessionFactory; + private readonly IoTDBRuntimeContext _runtimeContext; - public IoTDBProvider(IOptions options, ILogger logger) + public IoTDBProvider( + ILogger logger, + IIoTDBSessionFactory sessionFactory, + IoTDBRuntimeContext runtimeContext) { - _options = options.Value; - - _sessionPool = new TableSessionPool.Builder() -#if DEBUG - .SetHost(_options.ClusterList.FirstOrDefault()?.Split(":")[0]) - .SetHost(_options.ClusterList.FirstOrDefault()?.Split(":")[1]) -#else - .SetNodeUrls(_options.ClusterList) -#endif - .SetUsername(_options.UserName) - .SetPassword(_options.Password) - .SetFetchSize(_options.FetchSize) - .SetPoolSize(_options.PoolSize) - .SetDatabase(_options.DataBaseName) - .Build(); - - _sessionPool.Open(false).Wait(); - if (_options.OpenDebugMode) - { - _sessionPool.OpenDebugMode(builder => - { - builder.AddConsole(); - }); - } _logger = logger; + _sessionFactory = sessionFactory; + _runtimeContext = runtimeContext; + + RefreshSessionPool(); + } + + private void RefreshSessionPool() + { + try + { + _currentSession?.Dispose(); + _currentSession = _sessionFactory.GetSessionPool(_runtimeContext.UseTableSessionPool); + _currentSession.OpenAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + catch (Exception ex) + { + //自动回退到备用SessionPool + _logger.LogError($"{nameof(RefreshSessionPool)} 切换SessionPool失败,回退到默认配置:{ex.Serialize()}"); + _runtimeContext.UseTableSessionPool = !_runtimeContext.UseTableSessionPool; + _currentSession = _sessionFactory.GetSessionPool(_runtimeContext.UseTableSessionPool); + } + } + + /// + /// 切换 SessionPool + /// + /// + public void SwitchSessionPool(bool useTableSession) + { + if (_runtimeContext.UseTableSessionPool != useTableSession) + { + _runtimeContext.UseTableSessionPool = useTableSession; + RefreshSessionPool(); + } } /// @@ -60,24 +77,13 @@ namespace JiShe.CollectBus.IoTDBProvider /// /// /// - public async Task InsertAsync(T entity, int buildTabletMode) where T : IoTEntity + public async Task InsertAsync(T entity) where T : IoTEntity { var metadata = GetMetadata(); - var tablet = BuildTablet(new[] { entity }, metadata, buildTabletMode); - int result = 0; - if (buildTabletMode == 1) - { - result = await _sessionPool.InsertAsync(tablet); - } - else if(buildTabletMode == 2) - { - result = await _sessionPool.InsertAsync(tablet); - } - else - { - throw new Exception($"{nameof(InsertAsync)} 表模型{typeof(T).Name}记录入库时,buildTabletMode参数值不正确,请赋值以后重新处理。"); - } + var tablet = BuildTablet(new[] { entity }, metadata); + + int result = await _currentSession.InsertAsync(tablet); if (result <= 0) { @@ -89,9 +95,8 @@ namespace JiShe.CollectBus.IoTDBProvider /// 批量插入数据 /// /// - /// /// - public async Task BatchInsertAsync(IEnumerable entities, int buildTabletMode) where T : IoTEntity + public async Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity { var metadata = GetMetadata(); @@ -100,8 +105,8 @@ namespace JiShe.CollectBus.IoTDBProvider foreach (var batch in batches) { - var tablet = BuildTablet(batch, metadata, buildTabletMode); - var result = await _sessionPool.InsertAsync(tablet); + var tablet = BuildTablet(batch, metadata); + var result = await _currentSession.InsertAsync(tablet); if (result <= 0) { _logger.LogWarning($"{typeof(T).Name} 批量插入数据第{batch}批次没有成功,共{batches}批次。"); @@ -119,7 +124,7 @@ namespace JiShe.CollectBus.IoTDBProvider public async Task DeleteAsync(QueryOptions options) where T : IoTEntity { var query = BuildDeleteSQL(options); - var sessionDataSet = await _sessionPool.ExecuteQueryStatementAsync(query); + var sessionDataSet = await _currentSession.ExecuteQueryStatementAsync(query); if (!sessionDataSet.HasNext()) { @@ -132,9 +137,6 @@ namespace JiShe.CollectBus.IoTDBProvider return row.Values[0]; } - - - /// /// 查询数据 /// @@ -144,7 +146,7 @@ namespace JiShe.CollectBus.IoTDBProvider public async Task> QueryAsync(QueryOptions options) where T : IoTEntity, new() { var query = BuildQuerySQL(options); - var sessionDataSet = await _sessionPool.ExecuteQueryStatementAsync(query); + var sessionDataSet = await _currentSession.ExecuteQueryStatementAsync(query); var result = new PagedResult { @@ -156,17 +158,17 @@ namespace JiShe.CollectBus.IoTDBProvider } /// - /// 构建表模型 + /// 构建Tablet /// /// /// 表实体 - /// 设备元数据 - /// 构建表模型方式,1 根据实体《T》直接显示指定Tag,2根据实体《T》的名称指定表名 + /// 设备元数据 /// - private Tablet BuildTablet(IEnumerable entities, DeviceMetadata metadata, int buildTabletMode) where T : IoTEntity + private Tablet BuildTablet(IEnumerable entities, DeviceMetadata metadata) where T : IoTEntity { var timestamps = new List(); var values = new List>(); + var devicePaths = new HashSet(); foreach (var entity in entities) { @@ -175,40 +177,70 @@ namespace JiShe.CollectBus.IoTDBProvider foreach (var measurement in metadata.ColumnNames) { var value = typeof(T).GetProperty(measurement)?.GetValue(entity); - if (value == null) - { - throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,属性{measurement}值为空,不符合IoTDB设计标准,请赋值以后重新处理。"); - } - rowValues.Add(value); + rowValues.Add(value ?? new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,属性{measurement}值为空,不符合IoTDB设计标准,请赋值以后重新处理。")); } values.Add(rowValues); + if (!_runtimeContext.UseTableSessionPool)//树模型 + { + devicePaths.Add(DevicePathBuilder.GetDevicePath(entity)); + } + else + { + devicePaths.Add(DevicePathBuilder.GetTableName()); + } } - if (buildTabletMode == 1) + if (devicePaths.Count > 1) { - return new Tablet( - DevicePathBuilder.GetDeviceId(entities.First()), - metadata.ColumnNames, - metadata.DataTypes, - values, - timestamps - ); - } - else if (buildTabletMode == 2) - { - return new Tablet( - DevicePathBuilder.GetTableName(), - metadata.ColumnNames, - metadata.ColumnCategories, - metadata.DataTypes, - values, - timestamps - ); - } - else - { - throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,buildTabletMode参数值不正确,请赋值以后重新处理。"); + throw new Exception($"{nameof(BuildTablet)} 构建Tablet《{typeof(T).Name}》时,批量插入的设备路径不一致。"); } + + return _runtimeContext.UseTableSessionPool + ? BuildTableSessionTablet(metadata, devicePaths.First(), values, timestamps) + : BuildSessionTablet(metadata, devicePaths.First(), values, timestamps); + } + + /// + /// 构建tree模型的Tablet + /// + /// + /// + /// + /// + /// + private Tablet BuildSessionTablet(DeviceMetadata metadata, string devicePath, + List> values, List timestamps) + { + return new Tablet( + devicePath, + metadata.ColumnNames, + metadata.DataTypes, + values, + timestamps + ); + } + + /// + /// 构建表模型的Tablet + /// + /// + /// + /// + /// + /// + private Tablet BuildTableSessionTablet(DeviceMetadata metadata, string devicePath, + List> values, List timestamps) + { + var tablet = new Tablet( + devicePath, + metadata.ColumnNames, + metadata.ColumnCategories, + metadata.DataTypes, + values, + timestamps + ); + + return tablet; } /// @@ -222,7 +254,7 @@ namespace JiShe.CollectBus.IoTDBProvider var metadata = GetMetadata(); var sb = new StringBuilder("SELECT "); sb.AppendJoin(", ", metadata.ColumnNames); - sb.Append($" FROM {options.TableNameOrTagName}"); + sb.Append($" FROM {options.TableNameOrTreePath}"); if (options.Conditions.Any()) { @@ -243,9 +275,18 @@ namespace JiShe.CollectBus.IoTDBProvider private string BuildDeleteSQL(QueryOptions options) where T : IoTEntity { var metadata = GetMetadata(); - var sb = new StringBuilder("DELETE "); + var sb = new StringBuilder(); - sb.Append($" FROM {options.TableNameOrTagName}"); + if (!_runtimeContext.UseTableSessionPool) + { + sb.Append("DELETE "); + } + else + { + sb.Append("DROP "); + } + + sb.Append($" FROM {options.TableNameOrTreePath}"); sb.AppendJoin(", ", metadata.ColumnNames); @@ -283,13 +324,13 @@ namespace JiShe.CollectBus.IoTDBProvider /// private async Task GetTotalCount(QueryOptions options) where T : IoTEntity { - var countQuery = $"SELECT COUNT(*) FROM {options.TableNameOrTagName}"; + var countQuery = $"SELECT COUNT(*) FROM {options.TableNameOrTreePath}"; if (options.Conditions.Any()) { countQuery += " WHERE " + string.Join(" AND ", options.Conditions.Select(TranslateCondition)); } - var result = await _sessionPool.ExecuteQueryStatementAsync(countQuery); + var result = await _currentSession.ExecuteQueryStatementAsync(countQuery); return result.HasNext() ? Convert.ToInt32(result.Next().Values[0]) : 0; } @@ -334,14 +375,6 @@ namespace JiShe.CollectBus.IoTDBProvider return results; } - /// - /// 释放资源 - /// - public void Dispose() - { - _sessionPool?.Close().Wait(); - } - /// /// 获取设备元数据 /// @@ -453,7 +486,7 @@ namespace JiShe.CollectBus.IoTDBProvider private TSDataType GetDataTypeFromTypeName(string typeName) { if (string.IsNullOrWhiteSpace(typeName)) - return TSDataType.STRING; + return TSDataType.STRING; return DataTypeMap.TryGetValue(typeName.Trim(), out var dataType) ? dataType diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBSessionFactory.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBSessionFactory.cs new file mode 100644 index 0000000..a9b462a --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBSessionFactory.cs @@ -0,0 +1,36 @@ +using JiShe.CollectBus.IoTDBProvider.Interface; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.IoTDBProvider.Provider +{ + + /// + /// 实现带缓存的Session工厂 + /// + public class IoTDBSessionFactory : IIoTDBSessionFactory + { + private readonly IoTDBOptions _options; + private readonly ConcurrentDictionary _pools = new(); + + public IoTDBSessionFactory(IOptions options) + { + _options = options.Value; + } + + public IIoTDBSessionPool GetSessionPool(bool useTableSession) + { + return _pools.GetOrAdd(useTableSession, key => + { + return key + ? new TableSessionPoolAdapter(_options) + : new SessionPoolAdapter(_options); + }); + } + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs index 45964fa..d9bf4fa 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTEntity.cs @@ -30,8 +30,8 @@ public string DeviceId { get; set; } /// - /// 当前时间戳,单位秒 + /// 当前时间戳,单位毫秒 /// - public long Timestamps { get; set; } = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + public long Timestamps { get; set; } = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); } } diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/SessionPoolAdapter.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/SessionPoolAdapter.cs new file mode 100644 index 0000000..81a69a9 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/SessionPoolAdapter.cs @@ -0,0 +1,74 @@ +using Apache.IoTDB.DataStructure; +using Apache.IoTDB; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using JiShe.CollectBus.IoTDBProvider.Interface; +using Microsoft.Extensions.Logging; + +namespace JiShe.CollectBus.IoTDBProvider.Provider +{ + /// + /// 树模型连接池 + /// + public class SessionPoolAdapter : IIoTDBSessionPool + { + private readonly SessionPool _sessionPool; + private readonly IoTDBOptions _options; + + public SessionPoolAdapter(IoTDBOptions options) + { + _options = options; + _sessionPool = new SessionPool.Builder() + .SetNodeUrl(options.ClusterList) + .SetUsername(options.UserName) + .SetPassword(options.Password) + .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 InsertAsync(Tablet tablet) + { + return await _sessionPool.InsertAlignedTabletAsync(tablet); + } + + /// + /// 查询数据 + /// + /// + /// + public async Task ExecuteQueryStatementAsync(string sql) + { + return await _sessionPool.ExecuteQueryStatementAsync(sql); + } + + public void Dispose() + { + _sessionPool?.Close().ConfigureAwait(false).GetAwaiter().GetResult(); + } + } +} diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/TableSessionPoolAdapter.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/TableSessionPoolAdapter.cs new file mode 100644 index 0000000..3676199 --- /dev/null +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/TableSessionPoolAdapter.cs @@ -0,0 +1,72 @@ +using Apache.IoTDB.DataStructure; +using Apache.IoTDB; +using JiShe.CollectBus.IoTDBProvider.Interface; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace JiShe.CollectBus.IoTDBProvider.Provider +{ + /// + /// 表模型Session连接池 + /// + public class TableSessionPoolAdapter : IIoTDBSessionPool + { + private readonly TableSessionPool _sessionPool; + private readonly IoTDBOptions _options; + + public TableSessionPoolAdapter(IoTDBOptions options) + { + _options = options; + _sessionPool = new TableSessionPool.Builder() + .SetNodeUrls(options.ClusterList) + .SetUsername(options.UserName) + .SetPassword(options.Password) + .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 InsertAsync(Tablet tablet) + { + return await _sessionPool.InsertAsync(tablet); + } + + /// + /// 查询数据 + /// + /// + /// + public async Task ExecuteQueryStatementAsync(string sql) + { + return await _sessionPool.ExecuteQueryStatementAsync(sql); + } + + public void Dispose() + { + _sessionPool?.Close().ConfigureAwait(false).GetAwaiter().GetResult(); + } + } +} From 1b4c4dd8ff43e1379feacb7330caf7a7aa556ea3 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Mon, 7 Apr 2025 17:35:37 +0800 Subject: [PATCH 13/18] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CollectBusApplicationModule.cs | 9 ++- .../CollectBusDomainModule.cs | 2 +- .../CollectBusHostModule.Configure.cs | 64 +++++++++++++------ 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/JiShe.CollectBus.Application/CollectBusApplicationModule.cs b/src/JiShe.CollectBus.Application/CollectBusApplicationModule.cs index 085ab8e..4230150 100644 --- a/src/JiShe.CollectBus.Application/CollectBusApplicationModule.cs +++ b/src/JiShe.CollectBus.Application/CollectBusApplicationModule.cs @@ -16,6 +16,13 @@ using Volo.Abp.BackgroundWorkers.Hangfire; using JiShe.CollectBus.MongoDB; using JiShe.CollectBus.ScheduledMeterReading; using JiShe.CollectBus.IoTDBProvider; +using Confluent.Kafka.Admin; +using Confluent.Kafka; +using Microsoft.Extensions.Options; +using JiShe.CollectBus.Protocol.Contracts; +using System.Collections.Generic; +using Thrift; +using Microsoft.Extensions.Configuration; namespace JiShe.CollectBus; @@ -60,7 +67,7 @@ public class CollectBusApplicationModule : AbpModule var dbContext = context.ServiceProvider.GetRequiredService(); //默认初始化表计信息 - dbContext.InitAmmeterCacheData().ConfigureAwait(false).GetAwaiter().GetResult(); + dbContext.InitAmmeterCacheData().ConfigureAwait(false).GetAwaiter().GetResult(); } } diff --git a/src/JiShe.CollectBus.Domain/CollectBusDomainModule.cs b/src/JiShe.CollectBus.Domain/CollectBusDomainModule.cs index 64519c7..24945de 100644 --- a/src/JiShe.CollectBus.Domain/CollectBusDomainModule.cs +++ b/src/JiShe.CollectBus.Domain/CollectBusDomainModule.cs @@ -12,7 +12,7 @@ namespace JiShe.CollectBus; [DependsOn( typeof(CollectBusDomainSharedModule), typeof(AbpAuditLoggingDomainModule), - typeof(AbpCachingModule), + typeof(AbpCachingModule), typeof(AbpBackgroundJobsDomainModule) )] public class CollectBusDomainModule : AbpModule diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs index 40f498e..80145ab 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs @@ -23,6 +23,7 @@ using JiShe.CollectBus.IotSystems.MessageReceiveds; using JiShe.CollectBus.IotSystems.MessageIssueds; using Confluent.Kafka; using MassTransit.SqlTransport.Topology; +using Confluent.Kafka.Admin; namespace JiShe.CollectBus.Host @@ -286,29 +287,51 @@ namespace JiShe.CollectBus.Host /// The configuration. public void ConfigureMassTransit(ServiceConfigurationContext context, IConfiguration configuration) { + + + var adminClient = new AdminClientBuilder(new AdminClientConfig + { + BootstrapServers = configuration.GetConnectionString("Kafka") + }).Build(); + + try + { + var topics = ProtocolConstExtensions.GetAllTopicNames(); + List topicSpecifications = new List(); + foreach (var item in topics) + { + topicSpecifications.Add(new TopicSpecification + { + Name = item, + NumPartitions = 3, + ReplicationFactor = 1 + }); + } + adminClient.CreateTopicsAsync(topicSpecifications).ConfigureAwait(false).GetAwaiter().GetResult(); + } + catch (CreateTopicsException e) + { + if (e.Results[0].Error.Code != ErrorCode.TopicAlreadyExists) + { + throw; + } + } + + var consumerConfig = new ConsumerConfig { GroupId = ProtocolConst.SubscriberGroup }; var producerConfig = new ProducerConfig(); - context.Services -// .ConfigureKafkaTestOptions(options => -// { - -//#if DEBUG -// options.CleanTopicsOnStart = true;// 测试时,每次启动都删除topic,生产环境不需要 -//#endif -// options.CreateTopicsIfNotExists = true; -// options.TopicNames = ProtocolConstExtensions.GetAllTopicNames(); -// }) + context.Services .AddMassTransit(x => { x.UsingInMemory((context, cfg) => cfg.ConfigureEndpoints(context)); - //x.AddConfigureEndpointsCallback((c, name, cfg) => - //{ - // cfg.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30))); - // cfg.UseMessageRetry(r => r.Immediate(5)); - // cfg.UseInMemoryOutbox(c); - //}); + x.AddConfigureEndpointsCallback((c, name, cfg) => + { + cfg.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30))); + cfg.UseMessageRetry(r => r.Immediate(5)); + cfg.UseInMemoryOutbox(c); + }); x.AddRider(rider => { @@ -322,14 +345,15 @@ namespace JiShe.CollectBus.Host .SetTimeLimit(s: 1) .SetTimeLimitStart(BatchTimeLimitStart.FromLast) .SetConcurrencyLimit(10)); - }); - + }); rider.AddConsumer(); + rider.AddProducer(ProtocolConst.SubscriberReceivedLoginEventName); + rider.AddProducer(ProtocolConst.SubscriberReceivedHeartbeatEventName); + rider.UsingKafka((c, cfg) => { - List hosts = new List() { "121.42.242.91:29092", "121.42.242.91:39092", "121.42.242.91:49092" }; - cfg.Host(hosts); + cfg.Host(configuration.GetConnectionString("Kafka")); cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedHeartbeatEventName, consumerConfig, configurator => { From 0dd95828d4320c916254079bcb1588fbd9775011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=9B=8A?= Date: Mon, 7 Apr 2025 21:34:05 +0800 Subject: [PATCH 14/18] =?UTF-8?q?=E8=BF=98=E5=8E=9FCAP=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BasicScheduledMeterReadingService.cs | 26 +++++++++---------- ...nergySystemScheduledMeterReadingService.cs | 2 +- .../CollectBusHostModule.cs | 4 +-- .../Abstracts/BaseProtocolPlugin.cs | 12 ++++----- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs index f65e51f..b4d3a02 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs @@ -39,12 +39,12 @@ namespace JiShe.CollectBus.ScheduledMeterReading public abstract class BasicScheduledMeterReadingService : CollectBusAppService, IScheduledMeterReadingService { private readonly ILogger _logger; - private readonly IPublishEndpoint _producerBus; + private readonly ICapPublisher _producerBus; private readonly IMeterReadingRecordRepository _meterReadingRecordsRepository; public BasicScheduledMeterReadingService( ILogger logger, - IPublishEndpoint producerBus, + ICapPublisher producerBus, IMeterReadingRecordRepository meterReadingRecordsRepository) { _producerBus = producerBus; @@ -298,8 +298,8 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; - //_ = _producerBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500), ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); - _= _producerBus.Publish(tempMsg); + _ = _producerBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500), ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + //_= _producerBus.Publish(tempMsg); meterTaskInfosList.Add(ammerterItem.Value); @@ -365,9 +365,9 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; - //_= _producerBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500), ProtocolConst.AmmeterSubscriberWorkerFiveMinuteIssuedEventName, tempMsg); + _= _producerBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500), ProtocolConst.AmmeterSubscriberWorkerFiveMinuteIssuedEventName, tempMsg); - _ = _producerBus.Publish(tempMsg); + //_ = _producerBus.Publish(tempMsg); meterTaskInfosList.Add(ammerterItem.Value); } @@ -437,9 +437,9 @@ namespace JiShe.CollectBus.ScheduledMeterReading TimeDensity = timeDensity.ToString(), }; - //_ = _producerBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500) ,ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, tempMsg); + _ = _producerBus.PublishDelayAsync(TimeSpan.FromMicroseconds(500) ,ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName, tempMsg); - _ = _producerBus.Publish(tempMsg); + //_ = _producerBus.Publish(tempMsg); meterTaskInfosList.Add(ammerterItem.Value); } @@ -827,9 +827,9 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; - //await _producerBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + await _producerBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); - _ = _producerBus.Publish(tempMsg); + //_ = _producerBus.Publish(tempMsg); meterTaskInfosList.Add(ammerterItem.Value); @@ -896,9 +896,9 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusAddress = ammerterItem.Value.FocusAddress, TimeDensity = timeDensity.ToString(), }; - //await _producerBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); + await _producerBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); - _ = _producerBus.Publish(tempMsg); + //_ = _producerBus.Publish(tempMsg); meterTaskInfosList.Add(ammerterItem.Value); @@ -966,7 +966,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading }; //await _producerBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerOneMinuteIssuedEventName, tempMsg); - _ = _producerBus.Publish(tempMsg); + //_ = _producerBus.Publish(tempMsg); meterTaskInfosList.Add(ammerterItem.Value); diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs index 78276e9..75931c3 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs @@ -29,7 +29,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading { public EnergySystemScheduledMeterReadingService(ILogger logger, - IPublishEndpoint producerBus, IMeterReadingRecordRepository meterReadingRecordsRepository) : base(logger, producerBus, meterReadingRecordsRepository) + ICapPublisher producerBus, IMeterReadingRecordRepository meterReadingRecordsRepository) : base(logger, producerBus, meterReadingRecordsRepository) { } diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.cs index 263be23..6f24b1e 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.cs @@ -43,8 +43,8 @@ namespace JiShe.CollectBus.Host ConfigureNetwork(context, configuration); ConfigureJwtAuthentication(context, configuration); ConfigureHangfire(context); - //ConfigureCap(context, configuration); - ConfigureMassTransit(context, configuration); + ConfigureCap(context, configuration); + //ConfigureMassTransit(context, configuration); ConfigureAuditLog(context); ConfigureCustom(context, configuration); } diff --git a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs index 2fbe138..28cf6c2 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs @@ -17,7 +17,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts { public abstract class BaseProtocolPlugin : IProtocolPlugin { - private readonly IPublishEndpoint _producerBus; + private readonly ICapPublisher _producerBus; private readonly ILogger _logger; private readonly IRepository _protocolInfoRepository; @@ -37,7 +37,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts _logger = serviceProvider.GetRequiredService>(); _protocolInfoRepository = serviceProvider.GetRequiredService>(); - _producerBus = serviceProvider.GetRequiredService(); + _producerBus = serviceProvider.GetRequiredService(); } public abstract ProtocolInfo Info { get; } @@ -87,8 +87,8 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts }; var bytes = Build3761SendData.BuildSendCommandBytes(reqParam); - //await _producerBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); - await _producerBus.Publish(new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); + await _producerBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); + //await _producerBus.Publish(new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); } /// @@ -126,9 +126,9 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts Fn = 1 }; var bytes = Build3761SendData.BuildSendCommandBytes(reqParam); - //await _producerBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); + await _producerBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); - await _producerBus.Publish(new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); + //await _producerBus.Publish(new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); } } From 323ad75f8aa64aae0964fd206c5d64729e7fae81 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Mon, 7 Apr 2025 21:50:50 +0800 Subject: [PATCH 15/18] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Plugins/TcpMonitor.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs index 7083387..8f37fb4 100644 --- a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs +++ b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs @@ -24,7 +24,7 @@ namespace JiShe.CollectBus.Plugins { public partial class TcpMonitor : PluginBase, ITransientDependency, ITcpReceivedPlugin, ITcpConnectingPlugin, ITcpConnectedPlugin, ITcpClosedPlugin { - private readonly IPublishEndpoint _producerBus; + private readonly ICapPublisher _producerBus; private readonly ILogger _logger; private readonly IRepository _deviceRepository; private readonly IDistributedCache _ammeterInfoCache; @@ -36,7 +36,7 @@ namespace JiShe.CollectBus.Plugins /// /// /// - public TcpMonitor(IPublishEndpoint producerBus, + public TcpMonitor(ICapPublisher producerBus, ILogger logger, IRepository deviceRepository, IDistributedCache ammeterInfoCache) @@ -160,9 +160,9 @@ namespace JiShe.CollectBus.Plugins DeviceNo = deviceNo, MessageId = NewId.NextGuid().ToString() }; - //await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedLoginEventName, messageReceivedLoginEvent); + await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedLoginEventName, messageReceivedLoginEvent); - await _producerBus.Publish( messageReceivedLoginEvent); + //await _producerBus.Publish( messageReceivedLoginEvent); } private async Task OnTcpHeartbeatReceived(ITcpSessionClient client, string messageHexString, string deviceNo) @@ -199,23 +199,13 @@ namespace JiShe.CollectBus.Plugins DeviceNo = deviceNo, MessageId = NewId.NextGuid().ToString() }; - //await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedHeartbeatEventName, messageReceivedHeartbeatEvent); - await _producerBus.Publish(messageReceivedHeartbeatEvent); + await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedHeartbeatEventName, messageReceivedHeartbeatEvent); + //await _producerBus.Publish(messageReceivedHeartbeatEvent); } private async Task OnTcpNormalReceived(ITcpSessionClient client, string messageHexString, string deviceNo) { - await _producerBus.Publish(new MessageReceived - { - ClientId = client.Id, - ClientIp = client.IP, - ClientPort = client.Port, - MessageHexString = messageHexString, - DeviceNo = deviceNo, - MessageId = NewId.NextGuid().ToString() - }); - - //await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedEventName, new MessageReceived + //await _producerBus.Publish(new MessageReceived //{ // ClientId = client.Id, // ClientIp = client.IP, @@ -224,6 +214,16 @@ namespace JiShe.CollectBus.Plugins // DeviceNo = deviceNo, // MessageId = NewId.NextGuid().ToString() //}); + + await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedEventName, new MessageReceived + { + ClientId = client.Id, + ClientIp = client.IP, + ClientPort = client.Port, + MessageHexString = messageHexString, + DeviceNo = deviceNo, + MessageId = NewId.NextGuid().ToString() + }); } } } From 39ae8a7f38ff13fb2c4b648224586b7fad9aa65f Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Tue, 8 Apr 2025 17:44:42 +0800 Subject: [PATCH 16/18] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IWorkerSubscriberAppService.cs | 2 - .../DataMigration/DataMigrationService.cs | 40 ++++++++--------- .../Samples/SampleAppService.cs | 22 +++++++-- .../BasicScheduledMeterReadingService.cs | 36 ++++++++++----- ...nergySystemScheduledMeterReadingService.cs | 3 +- .../Subscribers/SubscriberAppService.cs | 3 +- .../Subscribers/WorkerSubscriberAppService.cs | 23 +--------- .../Ammeters/ElectricityMeter.cs | 12 +++++ ...ScheduledMeterReadingIssuedEventMessage.cs | 5 +++ .../MeterReadingRecords.cs | 36 +++++---------- src/JiShe.CollectBus.Host/appsettings.json | 2 +- .../Provider/IoTDBProvider.cs | 43 ++++++++++++++++-- .../MeterReadingRecordRepository.cs | 19 ++++++-- .../ProtocolConst.cs | 45 ++++++++++++++----- 14 files changed, 186 insertions(+), 105 deletions(-) diff --git a/src/JiShe.CollectBus.Application.Contracts/Subscribers/IWorkerSubscriberAppService.cs b/src/JiShe.CollectBus.Application.Contracts/Subscribers/IWorkerSubscriberAppService.cs index 6d13b13..64dddf1 100644 --- a/src/JiShe.CollectBus.Application.Contracts/Subscribers/IWorkerSubscriberAppService.cs +++ b/src/JiShe.CollectBus.Application.Contracts/Subscribers/IWorkerSubscriberAppService.cs @@ -15,8 +15,6 @@ namespace JiShe.CollectBus.Subscribers #region 电表消息采集 - Task> AmmeterScheduledMeterOneMinuteReadingIssuedEventQuery(); - /// /// 1分钟采集电表数据下行消息消费订阅 /// diff --git a/src/JiShe.CollectBus.Application/DataMigration/DataMigrationService.cs b/src/JiShe.CollectBus.Application/DataMigration/DataMigrationService.cs index 4aa3a7e..f1f0c9b 100644 --- a/src/JiShe.CollectBus.Application/DataMigration/DataMigrationService.cs +++ b/src/JiShe.CollectBus.Application/DataMigration/DataMigrationService.cs @@ -66,21 +66,21 @@ namespace JiShe.CollectBus.DataMigration /// 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(); + //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; - } + // if (batchRecords == null || batchRecords.Length == 0) + // { + // writer.Complete(); + // break; + // } - await writer.WriteAsync(batchRecords); - } + // await writer.WriteAsync(batchRecords); + //} } /// @@ -111,14 +111,14 @@ namespace JiShe.CollectBus.DataMigration //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; - } + //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); + //await _meterReadingRecordsRepository.UpdateManyAsync(batch); } writer.Complete(); } diff --git a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs index 8990319..d2b2b47 100644 --- a/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs +++ b/src/JiShe.CollectBus.Application/Samples/SampleAppService.cs @@ -12,28 +12,41 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using JiShe.CollectBus.IoTDBProvider.Context; +using Microsoft.Extensions.Logging; namespace JiShe.CollectBus.Samples; public class SampleAppService : CollectBusAppService, ISampleAppService { - + private readonly ILogger _logger; private readonly IIoTDBProvider _iotDBProvider; private readonly IoTDBRuntimeContext _dbContext; private readonly IoTDBOptions _options; public SampleAppService(IIoTDBProvider iotDBProvider, IOptions options, - IoTDBRuntimeContext dbContext) + IoTDBRuntimeContext dbContext, ILogger logger) { _iotDBProvider = iotDBProvider; _options = options.Value; _dbContext = dbContext; + _logger = logger; } [HttpGet] - public async Task UseSessionPool() + public async Task UseSessionPool(long timestamps) { + string? messageHexString = null; + if (timestamps == 0) + { + timestamps = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + _logger.LogError($"timestamps_{timestamps}"); + } + else + { + messageHexString = messageHexString + timestamps; + } + ElectricityMeter meter = new ElectricityMeter() { SystemName = "energy", @@ -43,7 +56,8 @@ public class SampleAppService : CollectBusAppService, ISampleAppService MeterModel = "DDZY-1980", ProjectCode = "10059", Voltage = 10, - Timestamps = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), + IssuedMessageHexString = messageHexString, + Timestamps = timestamps, }; await _iotDBProvider.InsertAsync(meter); } diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs index b4d3a02..e9d0df6 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/BasicScheduledMeterReadingService.cs @@ -6,6 +6,7 @@ using System.Net; using System.Threading.Tasks; using DotNetCore.CAP; using FreeSql; +using FreeSql.Internal.CommonProvider; using JiShe.CollectBus.Ammeters; using JiShe.CollectBus.Common; using JiShe.CollectBus.Common.BuildSendDatas; @@ -15,6 +16,7 @@ using JiShe.CollectBus.Common.Extensions; using JiShe.CollectBus.Common.Helpers; using JiShe.CollectBus.Common.Models; using JiShe.CollectBus.GatherItem; +using JiShe.CollectBus.IoTDBProvider; using JiShe.CollectBus.IotSystems.Devices; using JiShe.CollectBus.IotSystems.MessageIssueds; using JiShe.CollectBus.IotSystems.MessageReceiveds; @@ -39,17 +41,20 @@ namespace JiShe.CollectBus.ScheduledMeterReading public abstract class BasicScheduledMeterReadingService : CollectBusAppService, IScheduledMeterReadingService { private readonly ILogger _logger; - private readonly ICapPublisher _producerBus; - private readonly IMeterReadingRecordRepository _meterReadingRecordsRepository; + private readonly ICapPublisher _producerBus; + private readonly IIoTDBProvider _dbProvider; + private readonly IMeterReadingRecordRepository _meterReadingRecordRepository; public BasicScheduledMeterReadingService( ILogger logger, - ICapPublisher producerBus, - IMeterReadingRecordRepository meterReadingRecordsRepository) + ICapPublisher producerBus, + IMeterReadingRecordRepository meterReadingRecordRepository, + IIoTDBProvider dbProvider) { _producerBus = producerBus; _logger = logger; - _meterReadingRecordsRepository = meterReadingRecordsRepository; + _dbProvider = dbProvider; + _meterReadingRecordRepository = meterReadingRecordRepository; } /// @@ -268,6 +273,8 @@ namespace JiShe.CollectBus.ScheduledMeterReading { //获取缓存中的电表信息 int timeDensity = 5; + var currentTime = DateTime.Now; + var redisKeyList = $"{string.Format(RedisConst.CacheTelemetryPacketInfoKey, SystemType, MeterTypeEnum.Ammeter, timeDensity)}*"; var oneMinutekeyList = await FreeRedisProvider.Instance.KeysAsync(redisKeyList); if (oneMinutekeyList == null || oneMinutekeyList.Length <= 0) @@ -307,7 +314,10 @@ namespace JiShe.CollectBus.ScheduledMeterReading } if (meterTaskInfosList != null && meterTaskInfosList.Count > 0) { - await _meterReadingRecordsRepository.InsertManyAsync(meterTaskInfosList); + //_dbProvider.SwitchSessionPool(true); + //await _dbProvider.InsertAsync(meterTaskInfosList); + + await _meterReadingRecordRepository.InsertManyAsync(meterTaskInfosList,currentTime); } //删除任务数据 @@ -335,6 +345,8 @@ namespace JiShe.CollectBus.ScheduledMeterReading { //获取缓存中的电表信息 int timeDensity = 5; + var currentTime = DateTime.Now; + var redisKeyList = $"{string.Format(RedisConst.CacheTelemetryPacketInfoKey, SystemType, MeterTypeEnum.Ammeter, timeDensity)}*"; var fiveMinutekeyList = await FreeRedisProvider.Instance.KeysAsync(redisKeyList); if (fiveMinutekeyList == null || fiveMinutekeyList.Length <= 0) @@ -374,7 +386,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading } if (meterTaskInfosList != null && meterTaskInfosList.Count > 0) { - await _meterReadingRecordsRepository.InsertManyAsync(meterTaskInfosList); + await _meterReadingRecordRepository.InsertManyAsync(meterTaskInfosList,currentTime); } //删除任务数据 @@ -446,7 +458,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading } if (meterTaskInfosList != null && meterTaskInfosList.Count > 0) { - await _meterReadingRecordsRepository.InsertManyAsync(meterTaskInfosList); + await _meterReadingRecordRepository.InsertManyAsync(meterTaskInfosList,currentDateTime); } //删除任务数据 @@ -706,6 +718,8 @@ namespace JiShe.CollectBus.ScheduledMeterReading FocusID = ammeter.FocusID, AFN = aFN, Fn = fn, + ItemCode = tempItem, + ManualOrNot = false, Pn = ammeter.MeteringCode, IssuedMessageId = GuidGenerator.Create().ToString(), IssuedMessageHexString = Convert.ToHexString(dataInfos), @@ -837,7 +851,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading } if (meterTaskInfosList != null && meterTaskInfosList.Count > 0) { - await _meterReadingRecordsRepository.InsertManyAsync(meterTaskInfosList); + await _meterReadingRecordRepository.InsertManyAsync(meterTaskInfosList); } //删除任务数据 @@ -906,7 +920,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading } if (meterTaskInfosList != null && meterTaskInfosList.Count > 0) { - await _meterReadingRecordsRepository.InsertManyAsync(meterTaskInfosList); + await _meterReadingRecordRepository.InsertManyAsync(meterTaskInfosList); } //删除任务数据 @@ -974,7 +988,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading } if (meterTaskInfosList != null && meterTaskInfosList.Count > 0) { - await _meterReadingRecordsRepository.InsertManyAsync(meterTaskInfosList); + await _meterReadingRecordRepository.InsertManyAsync(meterTaskInfosList); } //删除任务数据 diff --git a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs index 75931c3..b1c8b18 100644 --- a/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs +++ b/src/JiShe.CollectBus.Application/ScheduledMeterReading/EnergySystemScheduledMeterReadingService.cs @@ -6,6 +6,7 @@ using JiShe.CollectBus.Ammeters; using JiShe.CollectBus.Common.Consts; using JiShe.CollectBus.FreeSql; using JiShe.CollectBus.GatherItem; +using JiShe.CollectBus.IoTDBProvider; using JiShe.CollectBus.IotSystems.Devices; using JiShe.CollectBus.IotSystems.MessageIssueds; using JiShe.CollectBus.IotSystems.MeterReadingRecords; @@ -29,7 +30,7 @@ namespace JiShe.CollectBus.ScheduledMeterReading { public EnergySystemScheduledMeterReadingService(ILogger logger, - ICapPublisher producerBus, IMeterReadingRecordRepository meterReadingRecordsRepository) : base(logger, producerBus, meterReadingRecordsRepository) + ICapPublisher producerBus, IIoTDBProvider dbProvider, IMeterReadingRecordRepository meterReadingRecordRepository) : base(logger, producerBus, meterReadingRecordRepository, dbProvider) { } diff --git a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs index 242b216..aa86101 100644 --- a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs +++ b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs @@ -130,9 +130,10 @@ namespace JiShe.CollectBus.Subscribers Pn = 0, FocusAddress = "", MeterAddress = "", - DataResult = tb3761FN.Text, }); + //todo 将解析结果插入IoTDB,时标从 + //await _messageReceivedEventRepository.InsertAsync(receivedMessage); } } diff --git a/src/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs b/src/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs index 98df22b..82672f6 100644 --- a/src/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs +++ b/src/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs @@ -54,24 +54,7 @@ namespace JiShe.CollectBus.Subscribers } - #region 电表消息采集 - - /// - /// 一分钟定时抄读任务消息消费订阅 - /// - /// - [HttpGet] - [Route("ammeter/oneminute/issued-eventQuery")] - public async Task> AmmeterScheduledMeterOneMinuteReadingIssuedEventQuery() - { - var currentDateTime = DateTime.Now; - - var list = await _meterReadingRecordsRepository.ParallelQueryAsync(currentDateTime.AddMinutes(-20), currentDateTime.AddMinutes(10)); - - return list; - - //return null; - } + #region 电表消息采集 /// /// 一分钟定时抄读任务消息消费订阅 @@ -146,9 +129,7 @@ namespace JiShe.CollectBus.Subscribers _logger.LogError("【15分钟采集电表数据下行消息消费队列开始处理】协议不存在!"); } else - { - // var dd = await _meterReadingRecordsRepository.FirstOrDefaultAsync(d=>d.ManualOrNot== true); - + { var device = await _deviceRepository.FirstOrDefaultAsync(a => a.Number == receivedMessage.FocusAddress); if (device != null) { diff --git a/src/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs b/src/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs index d335fca..987013c 100644 --- a/src/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs +++ b/src/JiShe.CollectBus.Domain/Ammeters/ElectricityMeter.cs @@ -12,6 +12,18 @@ namespace JiShe.CollectBus.Ammeters [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; } diff --git a/src/JiShe.CollectBus.Domain/IotSystems/MessageIssueds/ScheduledMeterReadingIssuedEventMessage.cs b/src/JiShe.CollectBus.Domain/IotSystems/MessageIssueds/ScheduledMeterReadingIssuedEventMessage.cs index 49fce87..41ba7ea 100644 --- a/src/JiShe.CollectBus.Domain/IotSystems/MessageIssueds/ScheduledMeterReadingIssuedEventMessage.cs +++ b/src/JiShe.CollectBus.Domain/IotSystems/MessageIssueds/ScheduledMeterReadingIssuedEventMessage.cs @@ -29,5 +29,10 @@ namespace JiShe.CollectBus.IotSystems.MessageIssueds /// public string MessageId { get; set; } + /// + /// 最后一次消息Id,用于在消费消息时检查上一个任务是否处理完。 + /// + public string LastMessageId { get; set; } + } } diff --git a/src/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingRecords.cs b/src/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingRecords.cs index a75c032..153acdf 100644 --- a/src/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingRecords.cs +++ b/src/JiShe.CollectBus.Domain/IotSystems/MeterReadingRecords/MeterReadingRecords.cs @@ -13,8 +13,7 @@ namespace JiShe.CollectBus.IotSystems.MeterReadingRecords /// 抄读数据记录 /// public class MeterReadingRecords : AggregateRoot - { - + { /// /// 是否手动操作 /// @@ -34,7 +33,7 @@ namespace JiShe.CollectBus.IotSystems.MeterReadingRecords /// 下发消息Id /// public string IssuedMessageId { get; set; } - + /// /// 集中器ID /// @@ -85,11 +84,15 @@ namespace JiShe.CollectBus.IotSystems.MeterReadingRecords /// public int Pn { get; set; } + /// + /// 采集项编码 + /// + public string ItemCode { get; set;} /// - /// 是否下发成功 + /// 是否超时 /// - public bool WasSuccessful { get; set; } + public bool IsTimeout { get; set; } = false; /// /// 创建时间 @@ -109,28 +112,13 @@ namespace JiShe.CollectBus.IotSystems.MeterReadingRecords /// /// 上报消息Id /// - public string ReceivedMessageId { get; set; } + public string ReceivedMessageId { get; set; } /// - /// 数据迁移状态 + /// 上报报文解析备注,异常情况下才有 /// - public RecordsDataMigrationStatusEnum MigrationStatus { get; set; } - - /// - /// 数据结果,最终的解析报文结果值 - /// - public string DataResult { get; set; } - - /// - /// 数据时间,如冻结时间、事件发生事件等 - /// - public DateTime? DataGenerationTimestamp { get; set; } - - /// - /// 数据迁移时间 - /// - public DateTime? MigrationTime { get; set; } - + public string ReceivedRemark { get; set; } + public void CreateDataId(Guid Id) { this.Id = Id; diff --git a/src/JiShe.CollectBus.Host/appsettings.json b/src/JiShe.CollectBus.Host/appsettings.json index 3d51171..9694e44 100644 --- a/src/JiShe.CollectBus.Host/appsettings.json +++ b/src/JiShe.CollectBus.Host/appsettings.json @@ -40,7 +40,7 @@ "EnergyDB": "server=118.190.144.92;database=db_energy;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False" }, "Redis": { - "Configuration": "118.190.144.92:6380,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", + "Configuration": "120.24.52.151:6380,password=1q2w3e!@#,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true", "DefaultDB": "14", "HangfireDB": "15" }, diff --git a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs index 4046ba9..6ad2caa 100644 --- a/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs +++ b/src/JiShe.CollectBus.IoTDBProvider/Provider/IoTDBProvider.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; +using static Thrift.Protocol.Utilities.TJSONProtocolConstants; namespace JiShe.CollectBus.IoTDBProvider { @@ -30,7 +31,7 @@ namespace JiShe.CollectBus.IoTDBProvider private readonly IoTDBRuntimeContext _runtimeContext; public IoTDBProvider( - ILogger logger, + ILogger logger, IIoTDBSessionFactory sessionFactory, IoTDBRuntimeContext runtimeContext) { @@ -176,8 +177,21 @@ namespace JiShe.CollectBus.IoTDBProvider var rowValues = new List(); foreach (var measurement in metadata.ColumnNames) { - var value = typeof(T).GetProperty(measurement)?.GetValue(entity); - rowValues.Add(value ?? new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,属性{measurement}值为空,不符合IoTDB设计标准,请赋值以后重新处理。")); + PropertyInfo propertyInfo = typeof(T).GetProperty(measurement); + if (propertyInfo==null) + { + throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,没有找到{measurement}属性,属于异常情况。"); + } + var value = propertyInfo.GetValue(entity); + if (value != null) + { + rowValues.Add(value); + } + else + { + DataTypeValueMap.TryGetValue(propertyInfo.PropertyType.Name, out object defaultValue); + rowValues.Add(defaultValue); + } } values.Add(rowValues); if (!_runtimeContext.UseTableSessionPool)//树模型 @@ -286,7 +300,7 @@ namespace JiShe.CollectBus.IoTDBProvider sb.Append("DROP "); } - sb.Append($" FROM {options.TableNameOrTreePath}"); + sb.Append($" FROM {options.TableNameOrTreePath}"); sb.AppendJoin(", ", metadata.ColumnNames); @@ -509,7 +523,28 @@ namespace JiShe.CollectBus.IoTDBProvider ["TIMESTAMP"] = TSDataType.TIMESTAMP, ["DATE"] = TSDataType.DATE, ["BLOB"] = TSDataType.BLOB, + ["DECIMAL"] = TSDataType.STRING, ["STRING"] = TSDataType.STRING }; + + /// + /// 根据类型名称获取 IoTDB 数据默认值 + /// + private readonly IReadOnlyDictionary DataTypeValueMap = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["BOOLEAN"] = false, + ["INT32"] = 0, + ["INT64"] = 0, + ["FLOAT"] = 0.0f, + ["DOUBLE"] = 0.0d, + ["TEXT"] = string.Empty, + ["NULLTYPE"] = null, + ["TIMESTAMP"] = null, + ["DATE"] = null, + ["BLOB"] = null, + ["DECIMAL"] = "0.0", + ["STRING"] = string.Empty + }; } } diff --git a/src/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs b/src/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs index 5a73b8d..0a5ab7a 100644 --- a/src/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs +++ b/src/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs @@ -109,6 +109,21 @@ namespace JiShe.CollectBus.Repository.MeterReadingRecord return entity; } + + /// + /// 单个获取 + /// + /// + /// + /// + /// + public async Task FirOrDefaultAsync(MeterReadingRecords entity, DateTime? dateTime) + { + var collection = await GetShardedCollection(dateTime); + await collection.findon + throw new NotImplementedException(); + } + /// /// 多集合数据查询 /// @@ -156,9 +171,5 @@ namespace JiShe.CollectBus.Repository.MeterReadingRecord return database.GetCollection(collectionName); } - public Task FirOrDefaultAsync(MeterReadingRecords entity, DateTime? dateTime) - { - throw new NotImplementedException(); - } } } diff --git a/src/JiShe.CollectBus.Protocol.Contracts/ProtocolConst.cs b/src/JiShe.CollectBus.Protocol.Contracts/ProtocolConst.cs index 844a50b..66e6ede 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/ProtocolConst.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/ProtocolConst.cs @@ -18,50 +18,71 @@ namespace JiShe.CollectBus.Protocol.Contracts /// /// 1分钟采集电表数据下行消息主题 /// - public const string AmmeterSubscriberWorkerOneMinuteIssuedEventName = "issued.one.ammeter.event"; + public const string AmmeterSubscriberWorkerOneMinuteIssuedEventName = "issued.auto.one.ammeter.event"; /// /// 5分钟采集电表数据下行消息主题 /// - public const string AmmeterSubscriberWorkerFiveMinuteIssuedEventName = "issued.five.ammeter.event"; + public const string AmmeterSubscriberWorkerFiveMinuteIssuedEventName = "issued.auto.five.ammeter.event"; /// /// 15分钟采集电表数据下行消息主题 /// - public const string AmmeterSubscriberWorkerFifteenMinuteIssuedEventName = "issued.fifteen.ammeter.event"; + public const string AmmeterSubscriberWorkerFifteenMinuteIssuedEventName = "issued.auto.fifteen.ammeter.event"; /// - /// 其他采集数据下行消息主题,日冻结,月冻结、集中器版本号、定时阀控等 + /// 其他采集数据下行消息主题,日冻结,月冻结、集中器版本号控等 /// - public const string AmmeterSubscriberWorkerOtherIssuedEventName = "issued.other.ammeter.event"; + public const string AmmeterSubscriberWorkerOtherIssuedEventName = "issued.auto.other.ammeter.event"; + + /// + /// 电表自动阀控 + /// + public const string AmmeterSubscriberWorkerAutoValveControlIssuedEventName = "issued.auto.control.ammeter.event"; /// /// 电表手动阀控 /// - public const string AmmeterSubscriberWorkerManualValveControlIssuedEventName = "issued.control.ammeter.event"; + public const string AmmeterSubscriberWorkerManualValveControlIssuedEventName = "issued.manual.control.ammeter.event"; + + /// + /// 电表手动抄读 + /// + public const string AmmeterSubscriberWorkerManualValveReadingIssuedEventName = "issued.manual.reading.ammeter.event"; + #endregion #region 水表消息主题 /// /// 1分钟采集水表数据下行消息主题 /// - public const string WatermeterSubscriberWorkerOneMinuteIssuedEventName = "issued.one.watermeter.event"; + public const string WatermeterSubscriberWorkerOneMinuteIssuedEventName = "issued.auto.one.watermeter.event"; /// /// 5分钟采集水表数据下行消息主题 /// - public const string WatermeterSubscriberWorkerFiveMinuteIssuedEventName = "issued.five.watermeter.event"; + public const string WatermeterSubscriberWorkerFiveMinuteIssuedEventName = "issued.auto.five.watermeter.event"; /// /// 15分钟采集水表数据下行消息主题 /// - public const string WatermeterSubscriberWorkerFifteenMinuteIssuedEventName = "issued.fifteen.watermeter.event"; + public const string WatermeterSubscriberWorkerFifteenMinuteIssuedEventName = "issued.auto.fifteen.watermeter.event"; /// - /// 其他采集数据下行消息主题,日冻结,月冻结、集中器版本号、定时阀控等 + /// 其他采集数据下行消息主题,日冻结,月冻结、集中器版本号等 /// - public const string WatermeterSubscriberWorkerOtherIssuedEventName = "issued.other.watermeter.event"; + public const string WatermeterSubscriberWorkerOtherIssuedEventName = "issued.auto.other.watermeter.event"; + + /// + /// 水表自动阀控 + /// + public const string WatermeterSubscriberWorkerAutoValveControlIssuedEventName = "issued.auto.control.watermeter.event"; /// /// 水表手动阀控 /// - public const string WatermeterSubscriberWorkerManualValveControlIssuedEventName = "issued.control.watermeter.event"; + public const string WatermeterSubscriberWorkerManualValveControlIssuedEventName = "issued.manual.control.watermeter.event"; + + /// + /// 水表手动抄读 + /// + public const string WatermeterSubscriberWorkerManualValveReadingIssuedEventName = "issued.manual.reading.watermeter.event"; #endregion From b42388e7dfd86b6b4786223e16edbfb2a2332771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=9B=8A?= Date: Tue, 8 Apr 2025 23:15:46 +0800 Subject: [PATCH 17/18] =?UTF-8?q?=E6=A2=B3=E7=90=86Kafka=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E5=8F=8A=E8=AE=BE=E5=A4=87Hash=E5=88=86?= =?UTF-8?q?=E7=BB=84=E6=96=B9=E6=B3=95=E5=B0=81=E8=A3=85=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Subscribers/ISubscriberAppService.cs | 3 +- .../EnergySystem/EnergySystemAppService.cs | 34 +++++++------- .../Plugins/TcpMonitor.cs | 22 +++++++-- .../Subscribers/SubscriberAppService.cs | 47 ++++++++++++++----- .../Extensions/EnumExtensions.cs | 15 ++++++ .../Helpers/CommonHelper.cs | 34 ++++++++++++++ .../CollectBusHostModule.Configure.cs | 21 +++++---- .../MeterReadingRecordRepository.cs | 2 +- .../Abstracts/BaseProtocolPlugin.cs | 4 +- .../Extensions/ProtocolConstExtensions.cs | 37 +++++++++++++-- .../ProtocolConst.cs | 31 ++++++++++-- 11 files changed, 193 insertions(+), 57 deletions(-) diff --git a/src/JiShe.CollectBus.Application.Contracts/Subscribers/ISubscriberAppService.cs b/src/JiShe.CollectBus.Application.Contracts/Subscribers/ISubscriberAppService.cs index 22c79e6..f9bc706 100644 --- a/src/JiShe.CollectBus.Application.Contracts/Subscribers/ISubscriberAppService.cs +++ b/src/JiShe.CollectBus.Application.Contracts/Subscribers/ISubscriberAppService.cs @@ -7,7 +7,8 @@ namespace JiShe.CollectBus.Subscribers { public interface ISubscriberAppService : IApplicationService { - Task IssuedEvent(IssuedEventMessage issuedEventMessage); + Task LoginIssuedEvent(IssuedEventMessage issuedEventMessage); + Task HeartbeatIssuedEvent(IssuedEventMessage issuedEventMessage); Task ReceivedEvent(MessageReceived receivedMessage); Task ReceivedHeartbeatEvent(MessageReceivedHeartbeat receivedHeartbeatMessage); Task ReceivedLoginEvent(MessageReceivedLogin receivedLoginMessage); diff --git a/src/JiShe.CollectBus.Application/EnergySystem/EnergySystemAppService.cs b/src/JiShe.CollectBus.Application/EnergySystem/EnergySystemAppService.cs index 07db264..0542ede 100644 --- a/src/JiShe.CollectBus.Application/EnergySystem/EnergySystemAppService.cs +++ b/src/JiShe.CollectBus.Application/EnergySystem/EnergySystemAppService.cs @@ -71,7 +71,7 @@ namespace JiShe.CollectBus.EnergySystem return result; - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -108,7 +108,7 @@ namespace JiShe.CollectBus.EnergySystem foreach (var bytes in bytesList) { - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -149,7 +149,7 @@ namespace JiShe.CollectBus.EnergySystem }).ToList(); var bytes = Build3761SendData.BuildAmmeterParameterSetSendCmd(address, meterParameters); - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -178,7 +178,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.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -261,7 +261,7 @@ namespace JiShe.CollectBus.EnergySystem if (bytes != null) { - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -320,7 +320,7 @@ namespace JiShe.CollectBus.EnergySystem var bytes = Build3761SendData.BuildCommunicationParametersSetSendCmd(address, masterIP, materPort, backupIP, backupPort, input.Data.APN); - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -347,7 +347,7 @@ namespace JiShe.CollectBus.EnergySystem var address = $"{input.AreaCode}{input.Address}"; var bytes = Build3761SendData.BuildTerminalCalendarClockSendCmd(address); - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -375,7 +375,7 @@ namespace JiShe.CollectBus.EnergySystem bool isManual = !input.AreaCode.Equals("5110");//低功耗集中器不是长连接,在连接的那一刻再发送 var bytes = Build3761SendData.BuildConrCheckTimeSendCmd(address,DateTime.Now, isManual); - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -402,7 +402,7 @@ namespace JiShe.CollectBus.EnergySystem var address = $"{input.AreaCode}{input.Address}"; var bytes = Build3761SendData.BuildConrRebootSendCmd(address); - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -430,7 +430,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.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -479,7 +479,7 @@ namespace JiShe.CollectBus.EnergySystem foreach (var bytes in bytesList) { - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -548,7 +548,7 @@ namespace JiShe.CollectBus.EnergySystem foreach (var bytes in bytesList) { - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -577,7 +577,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.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -605,7 +605,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.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -631,7 +631,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.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -658,7 +658,7 @@ namespace JiShe.CollectBus.EnergySystem { var address = $"{data.AreaCode}{data.Address}"; var bytes = Build3761SendData.BuildTerminalVersionInfoReadingSendCmd(address); - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, @@ -713,7 +713,7 @@ namespace JiShe.CollectBus.EnergySystem foreach (var bytes in bytesList) { - await _capBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage + await _capBus.PublishAsync(ProtocolConst.AmmeterSubscriberWorkerManualValveReadingIssuedEventName, new IssuedEventMessage { //ClientId = messageReceived.ClientId, DeviceNo = address, diff --git a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs index 8f37fb4..7224cdb 100644 --- a/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs +++ b/src/JiShe.CollectBus.Application/Plugins/TcpMonitor.cs @@ -19,6 +19,7 @@ using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; +using static FreeSql.Internal.GlobalFilter; namespace JiShe.CollectBus.Plugins { @@ -78,7 +79,7 @@ namespace JiShe.CollectBus.Plugins } else { - await OnTcpNormalReceived(tcpSessionClient, messageHexString, aTuple.Item1); + await OnTcpNormalReceived(tcpSessionClient, messageHexString, aTuple.Item1,aFn.ToString()!.PadLeft(2,'0')); } } else @@ -160,7 +161,7 @@ namespace JiShe.CollectBus.Plugins DeviceNo = deviceNo, MessageId = NewId.NextGuid().ToString() }; - await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedLoginEventName, messageReceivedLoginEvent); + await _producerBus.PublishAsync(ProtocolConst.SubscriberLoginReceivedEventName, messageReceivedLoginEvent); //await _producerBus.Publish( messageReceivedLoginEvent); } @@ -199,11 +200,19 @@ namespace JiShe.CollectBus.Plugins DeviceNo = deviceNo, MessageId = NewId.NextGuid().ToString() }; - await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedHeartbeatEventName, messageReceivedHeartbeatEvent); + await _producerBus.PublishAsync(ProtocolConst.SubscriberHeartbeatReceivedEventName, messageReceivedHeartbeatEvent); //await _producerBus.Publish(messageReceivedHeartbeatEvent); } - private async Task OnTcpNormalReceived(ITcpSessionClient client, string messageHexString, string deviceNo) + /// + /// 正常帧处理,将不同的AFN进行分发 + /// + /// + /// + /// + /// + /// + private async Task OnTcpNormalReceived(ITcpSessionClient client, string messageHexString, string deviceNo,string aFn) { //await _producerBus.Publish(new MessageReceived //{ @@ -215,7 +224,10 @@ namespace JiShe.CollectBus.Plugins // MessageId = NewId.NextGuid().ToString() //}); - await _producerBus.PublishAsync(ProtocolConst.SubscriberReceivedEventName, new MessageReceived + + string topicName = string.Format(ProtocolConst.AFNTopicNameFormat, aFn); + + await _producerBus.PublishAsync(topicName, new MessageReceived { ClientId = client.Id, ClientIp = client.IP, diff --git a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs index aa86101..a801454 100644 --- a/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs +++ b/src/JiShe.CollectBus.Application/Subscribers/SubscriberAppService.cs @@ -60,9 +60,38 @@ namespace JiShe.CollectBus.Subscribers _meterReadingRecordsRepository = meterReadingRecordsRepository; } - [CapSubscribe(ProtocolConst.SubscriberIssuedEventName)] - public async Task IssuedEvent(IssuedEventMessage issuedEventMessage) + [CapSubscribe(ProtocolConst.SubscriberLoginIssuedEventName)] + public async Task LoginIssuedEvent(IssuedEventMessage issuedEventMessage) { + switch (issuedEventMessage.Type) + { + case IssuedEventType.Heartbeat: + break; + case IssuedEventType.Login: + _logger.LogWarning($"集中器地址{issuedEventMessage.ClientId} 登录回复下发内容:{issuedEventMessage.Serialize()}"); + var loginEntity = await _messageReceivedLoginEventRepository.GetAsync(a => a.MessageId == issuedEventMessage.MessageId); + loginEntity.AckTime = Clock.Now; + loginEntity.IsAck = true; + await _messageReceivedLoginEventRepository.UpdateAsync(loginEntity); + break; + case IssuedEventType.Data: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + //var device = await _deviceRepository.FindAsync(a => a.Number == issuedEventMessage.DeviceNo); + //if (device != null) + //{ + // await _tcpService.SendAsync(device.ClientId, issuedEventMessage.Message); + //} + + await _tcpService.SendAsync(issuedEventMessage.ClientId, issuedEventMessage.Message); + } + + [CapSubscribe(ProtocolConst.SubscriberHeartbeatIssuedEventName)] + public async Task HeartbeatIssuedEvent(IssuedEventMessage issuedEventMessage) + { switch (issuedEventMessage.Type) { case IssuedEventType.Heartbeat: @@ -72,13 +101,6 @@ namespace JiShe.CollectBus.Subscribers heartbeatEntity.IsAck = true; await _messageReceivedHeartbeatEventRepository.UpdateAsync(heartbeatEntity); break; - case IssuedEventType.Login: - _logger.LogWarning($"集中器地址{issuedEventMessage.ClientId} 登录回复下发内容:{issuedEventMessage.Serialize()}"); - var loginEntity = await _messageReceivedLoginEventRepository.GetAsync(a => a.MessageId == issuedEventMessage.MessageId); - loginEntity.AckTime = Clock.Now; - loginEntity.IsAck = true; - await _messageReceivedLoginEventRepository.UpdateAsync(loginEntity); - break; case IssuedEventType.Data: break; default: @@ -130,15 +152,14 @@ namespace JiShe.CollectBus.Subscribers Pn = 0, FocusAddress = "", MeterAddress = "", + //DataResult = tb3761FN.Text, }); - //todo 将解析结果插入IoTDB,时标从 - //await _messageReceivedEventRepository.InsertAsync(receivedMessage); } } - [CapSubscribe(ProtocolConst.SubscriberReceivedHeartbeatEventName)] + [CapSubscribe(ProtocolConst.SubscriberHeartbeatReceivedEventName)] public async Task ReceivedHeartbeatEvent(MessageReceivedHeartbeat receivedHeartbeatMessage) { var protocolPlugin = _serviceProvider.GetKeyedService("StandardProtocolPlugin"); @@ -153,7 +174,7 @@ namespace JiShe.CollectBus.Subscribers } } - [CapSubscribe(ProtocolConst.SubscriberReceivedLoginEventName)] + [CapSubscribe(ProtocolConst.SubscriberLoginReceivedEventName)] public async Task ReceivedLoginEvent(MessageReceivedLogin receivedLoginMessage) { var protocolPlugin = _serviceProvider.GetKeyedService("StandardProtocolPlugin"); diff --git a/src/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs b/src/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs index 222984e..f54a3a4 100644 --- a/src/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs +++ b/src/JiShe.CollectBus.Common/Extensions/EnumExtensions.cs @@ -49,6 +49,21 @@ namespace JiShe.CollectBus.Common.Extensions ); } + /// + /// 将枚举转换为字典 + /// + /// + /// + public static Dictionary ToNameValueDictionary() where TEnum : Enum + { + return Enum.GetValues(typeof(TEnum)) + .Cast() + .ToDictionary( + e => e.ToString(), + e => Convert.ToInt32(e) + ); + } + /// /// 将枚举转换为字典 /// diff --git a/src/JiShe.CollectBus.Common/Helpers/CommonHelper.cs b/src/JiShe.CollectBus.Common/Helpers/CommonHelper.cs index fa38d1d..8c980c5 100644 --- a/src/JiShe.CollectBus.Common/Helpers/CommonHelper.cs +++ b/src/JiShe.CollectBus.Common/Helpers/CommonHelper.cs @@ -760,5 +760,39 @@ namespace JiShe.CollectBus.Common.Helpers return fontValue; } + + /// + /// 获取设备Id哈希值 + /// + /// + /// + /// + public static int GetDeviceHashCode(string deviceId, int TotalShards = 100) + { + // 计算哈希分组ID + return Math.Abs(deviceId.GetHashCode() % TotalShards); + } + + /// + /// 获取设备Id哈希分组 + /// + /// + /// + public static Dictionary> GetDeviceHashGroup(List deviceList) + { + Dictionary> keyValuePairs = new Dictionary>(); + foreach (var deviceId in deviceList) + { + var hashCode = GetDeviceHashCode(deviceId); + + if (!keyValuePairs.ContainsKey(hashCode.ToString())) + { + keyValuePairs.Add(hashCode.ToString(), new List()); + } + + keyValuePairs[hashCode.ToString()].Add(deviceId); + } + return keyValuePairs; + } } } diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs index 80145ab..32c39b5 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs @@ -257,6 +257,9 @@ namespace JiShe.CollectBus.Host /// The configuration. public void ConfigureCap(ServiceConfigurationContext context, IConfiguration configuration) { + List topics = ProtocolConstExtensions.GetAllTopicNamesByIssued(); + topics.AddRange(ProtocolConstExtensions.GetAllTopicNamesByReceived()); + context.Services.AddCap(x => { x.DefaultGroupName = ProtocolConst.SubscriberGroup; @@ -282,9 +285,7 @@ namespace JiShe.CollectBus.Host /// The configuration. /// /// Configures the mass transit. - /// - /// The context. - /// The configuration. + /// public void ConfigureMassTransit(ServiceConfigurationContext context, IConfiguration configuration) { @@ -296,7 +297,9 @@ namespace JiShe.CollectBus.Host try { - var topics = ProtocolConstExtensions.GetAllTopicNames(); + List topics = ProtocolConstExtensions.GetAllTopicNamesByIssued(); + topics.AddRange(ProtocolConstExtensions.GetAllTopicNamesByReceived()); + List topicSpecifications = new List(); foreach (var item in topics) { @@ -348,20 +351,20 @@ namespace JiShe.CollectBus.Host }); rider.AddConsumer(); - rider.AddProducer(ProtocolConst.SubscriberReceivedLoginEventName); - rider.AddProducer(ProtocolConst.SubscriberReceivedHeartbeatEventName); + rider.AddProducer(ProtocolConst.SubscriberLoginReceivedEventName); + rider.AddProducer(ProtocolConst.SubscriberHeartbeatReceivedEventName); rider.UsingKafka((c, cfg) => { cfg.Host(configuration.GetConnectionString("Kafka")); - cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedHeartbeatEventName, consumerConfig, configurator => + cfg.TopicEndpoint(ProtocolConst.SubscriberHeartbeatReceivedEventName, consumerConfig, configurator => { configurator.AutoOffsetReset = AutoOffsetReset.Earliest; configurator.ConfigureConsumer(c); }); - cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedLoginEventName, consumerConfig, configurator => + cfg.TopicEndpoint(ProtocolConst.SubscriberLoginReceivedEventName, consumerConfig, configurator => { configurator.ConfigureConsumer(c); configurator.AutoOffsetReset = AutoOffsetReset.Earliest; @@ -373,7 +376,7 @@ namespace JiShe.CollectBus.Host configurator.AutoOffsetReset = AutoOffsetReset.Earliest; }); - cfg.TopicEndpoint(ProtocolConst.SubscriberIssuedEventName, consumerConfig, configurator => + cfg.TopicEndpoint(ProtocolConst.SubscriberReceivedEventName, consumerConfig, configurator => { configurator.ConfigureConsumer(c); configurator.AutoOffsetReset = AutoOffsetReset.Earliest; diff --git a/src/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs b/src/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs index 0a5ab7a..7e34031 100644 --- a/src/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs +++ b/src/JiShe.CollectBus.MongoDB/Repository/MeterReadingRecord/MeterReadingRecordRepository.cs @@ -120,7 +120,7 @@ namespace JiShe.CollectBus.Repository.MeterReadingRecord public async Task FirOrDefaultAsync(MeterReadingRecords entity, DateTime? dateTime) { var collection = await GetShardedCollection(dateTime); - await collection.findon + //await collection.findon throw new NotImplementedException(); } diff --git a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs index 28cf6c2..762a2ca 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/Abstracts/BaseProtocolPlugin.cs @@ -87,7 +87,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts }; var bytes = Build3761SendData.BuildSendCommandBytes(reqParam); - await _producerBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); + await _producerBus.PublishAsync(ProtocolConst.SubscriberLoginIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); //await _producerBus.Publish(new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Login, MessageId = messageReceived.MessageId }); } @@ -126,7 +126,7 @@ namespace JiShe.CollectBus.Protocol.Contracts.Abstracts Fn = 1 }; var bytes = Build3761SendData.BuildSendCommandBytes(reqParam); - await _producerBus.PublishAsync(ProtocolConst.SubscriberIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); + await _producerBus.PublishAsync(ProtocolConst.SubscriberHeartbeatIssuedEventName, new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); //await _producerBus.Publish(new IssuedEventMessage { ClientId = messageReceived.ClientId, DeviceNo = messageReceived.DeviceNo, Message = bytes, Type = IssuedEventType.Heartbeat, MessageId = messageReceived.MessageId }); } diff --git a/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs b/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs index 9455144..8c097e2 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs @@ -1,4 +1,6 @@ -using System; +using JiShe.CollectBus.Common.Enums; +using JiShe.CollectBus.Common.Extensions; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -10,10 +12,10 @@ namespace JiShe.CollectBus.Protocol.Contracts public class ProtocolConstExtensions { /// - /// 自动获取 ProtocolConst 类中所有 Kafka 主题名称 + /// 自动获取 ProtocolConst 类中所有下行 Kafka 主题名称 /// (通过反射筛选 public const string 且字段名以 "EventName" 结尾的常量) /// - public static List GetAllTopicNames() + public static List GetAllTopicNamesByIssued() { return typeof(ProtocolConst) .GetFields(BindingFlags.Public | BindingFlags.Static) @@ -21,9 +23,36 @@ namespace JiShe.CollectBus.Protocol.Contracts f.IsLiteral && !f.IsInitOnly && f.FieldType == typeof(string) && - f.Name.EndsWith("EventName")) // 通过命名规则过滤主题字段 + f.Name.EndsWith("IssuedEventName")) // 通过命名规则过滤主题字段 .Select(f => (string)f.GetRawConstantValue()!) .ToList(); } + + /// + /// 自动获取 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(); + foreach (var item in afnList) + { + topicList.Add(string.Format(ProtocolConst.AFNTopicNameFormat, item.Value.ToString().PadLeft(2, '0'))); + } + + return topicList; + } } } diff --git a/src/JiShe.CollectBus.Protocol.Contracts/ProtocolConst.cs b/src/JiShe.CollectBus.Protocol.Contracts/ProtocolConst.cs index 66e6ede..796023a 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/ProtocolConst.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/ProtocolConst.cs @@ -9,10 +9,28 @@ namespace JiShe.CollectBus.Protocol.Contracts public class ProtocolConst { public const string SubscriberGroup = "jishe.collectbus"; - public const string SubscriberIssuedEventName = "issued.event"; + /// + /// 心跳下行消息主题 + /// + public const string SubscriberHeartbeatIssuedEventName = "issued.heartbeat.event"; + /// + /// 登录下行消息主题 + /// + public const string SubscriberLoginIssuedEventName = "issued.login.event"; + + /// + /// 上行消息主题 + /// public const string SubscriberReceivedEventName = "received.event"; - public const string SubscriberReceivedHeartbeatEventName = "received.heartbeat.event"; - public const string SubscriberReceivedLoginEventName = "received.login.event"; + + /// + /// 心跳上行消息主题 + /// + public const string SubscriberHeartbeatReceivedEventName = "received.heartbeat.event"; + /// + /// 登录上行消息主题 + /// + public const string SubscriberLoginReceivedEventName = "received.login.event"; #region 电表消息主题 /// @@ -47,7 +65,7 @@ namespace JiShe.CollectBus.Protocol.Contracts /// 电表手动抄读 /// public const string AmmeterSubscriberWorkerManualValveReadingIssuedEventName = "issued.manual.reading.ammeter.event"; - + #endregion #region 水表消息主题 @@ -85,7 +103,10 @@ namespace JiShe.CollectBus.Protocol.Contracts public const string WatermeterSubscriberWorkerManualValveReadingIssuedEventName = "issued.manual.reading.watermeter.event"; #endregion - + /// + /// AFN上行主题格式 + /// + public const string AFNTopicNameFormat = "received.afn{0}.event"; } } From beb11c76f534c1bd0e951f52c9565584095df3a3 Mon Sep 17 00:00:00 2001 From: ChenYi <296215406@outlook.com> Date: Wed, 9 Apr 2025 14:31:48 +0800 Subject: [PATCH 18/18] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Subscribers/WorkerSubscriberAppService.cs | 22 ++--- .../CollectBusHostModule.Configure.cs | 81 ++++++++++--------- .../CollectBusHostModule.cs | 1 + src/JiShe.CollectBus.Host/appsettings.json | 3 +- .../Extensions/ProtocolConstExtensions.cs | 11 ++- 5 files changed, 65 insertions(+), 53 deletions(-) diff --git a/src/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs b/src/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs index 82672f6..4428fe4 100644 --- a/src/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs +++ b/src/JiShe.CollectBus.Application/Subscribers/WorkerSubscriberAppService.cs @@ -148,7 +148,7 @@ namespace JiShe.CollectBus.Subscribers #region 水表消息采集 /// - /// 一分钟定时抄读任务消息消费订阅 + /// 1分钟采集水表数据下行消息消费订阅 /// /// /// @@ -157,11 +157,11 @@ namespace JiShe.CollectBus.Subscribers [CapSubscribe(ProtocolConst.WatermeterSubscriberWorkerOneMinuteIssuedEventName)] public async Task WatermeterScheduledMeterOneMinuteReadingIssuedEvent(ScheduledMeterReadingIssuedEventMessage receivedMessage) { - _logger.LogInformation("1分钟采集电表数据下行消息消费队列开始处理"); + _logger.LogInformation("1分钟采集水表数据下行消息消费队列开始处理"); var protocolPlugin = _serviceProvider.GetKeyedService("StandardProtocolPlugin"); if (protocolPlugin == null) { - _logger.LogError("【1分钟采集电表数据下行消息消费队列开始处理】协议不存在!"); + _logger.LogError("【1分钟采集水表数据下行消息消费队列开始处理】协议不存在!"); } else { @@ -175,20 +175,20 @@ namespace JiShe.CollectBus.Subscribers } /// - /// 5分钟采集电表数据下行消息消费订阅 + /// 5分钟采集水表数据下行消息消费订阅 /// /// /// [HttpPost] [Route("watermeter/fiveminute/issued-event")] - [CapSubscribe(ProtocolConst.AmmeterSubscriberWorkerFiveMinuteIssuedEventName)] + [CapSubscribe(ProtocolConst.WatermeterSubscriberWorkerFiveMinuteIssuedEventName)] public async Task WatermeterScheduledMeterFiveMinuteReadingIssuedEvent(ScheduledMeterReadingIssuedEventMessage receivedMessage) { - _logger.LogInformation("5分钟采集电表数据下行消息消费队列开始处理"); + _logger.LogInformation("5分钟采集水表数据下行消息消费队列开始处理"); var protocolPlugin = _serviceProvider.GetKeyedService("StandardProtocolPlugin"); if (protocolPlugin == null) { - _logger.LogError("【5分钟采集电表数据下行消息消费队列开始处理】协议不存在!"); + _logger.LogError("【5分钟采集水表数据下行消息消费队列开始处理】协议不存在!"); } else { @@ -202,20 +202,20 @@ namespace JiShe.CollectBus.Subscribers } /// - /// 15分钟采集电表数据下行消息消费订阅 + /// 15分钟采集水表数据下行消息消费订阅 /// /// /// [HttpPost] [Route("watermeter/fifteenminute/issued-event")] - [CapSubscribe(ProtocolConst.AmmeterSubscriberWorkerFifteenMinuteIssuedEventName)] + [CapSubscribe(ProtocolConst.WatermeterSubscriberWorkerFifteenMinuteIssuedEventName)] public async Task WatermeterScheduledMeterFifteenMinuteReadingIssuedEvent(ScheduledMeterReadingIssuedEventMessage receivedMessage) { - _logger.LogInformation("15分钟采集电表数据下行消息消费队列开始处理"); + _logger.LogInformation("15分钟采集水表数据下行消息消费队列开始处理"); var protocolPlugin = _serviceProvider.GetKeyedService("StandardProtocolPlugin"); if (protocolPlugin == null) { - _logger.LogError("【15分钟采集电表数据下行消息消费队列开始处理】协议不存在!"); + _logger.LogError("【15分钟采集水表数据下行消息消费队列开始处理】协议不存在!"); } else { diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs index 32c39b5..96e1e18 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.Configure.cs @@ -256,10 +256,7 @@ namespace JiShe.CollectBus.Host /// The context. /// The configuration. public void ConfigureCap(ServiceConfigurationContext context, IConfiguration configuration) - { - List topics = ProtocolConstExtensions.GetAllTopicNamesByIssued(); - topics.AddRange(ProtocolConstExtensions.GetAllTopicNamesByReceived()); - + { context.Services.AddCap(x => { x.DefaultGroupName = ProtocolConst.SubscriberGroup; @@ -287,40 +284,7 @@ namespace JiShe.CollectBus.Host /// Configures the mass transit. /// public void ConfigureMassTransit(ServiceConfigurationContext context, IConfiguration configuration) - { - - - var adminClient = new AdminClientBuilder(new AdminClientConfig - { - BootstrapServers = configuration.GetConnectionString("Kafka") - }).Build(); - - try - { - List topics = ProtocolConstExtensions.GetAllTopicNamesByIssued(); - topics.AddRange(ProtocolConstExtensions.GetAllTopicNamesByReceived()); - - List topicSpecifications = new List(); - foreach (var item in topics) - { - topicSpecifications.Add(new TopicSpecification - { - Name = item, - NumPartitions = 3, - ReplicationFactor = 1 - }); - } - adminClient.CreateTopicsAsync(topicSpecifications).ConfigureAwait(false).GetAwaiter().GetResult(); - } - catch (CreateTopicsException e) - { - if (e.Results[0].Error.Code != ErrorCode.TopicAlreadyExists) - { - throw; - } - } - - + { var consumerConfig = new ConsumerConfig { GroupId = ProtocolConst.SubscriberGroup }; var producerConfig = new ProducerConfig(); @@ -391,5 +355,46 @@ namespace JiShe.CollectBus.Host }); }); } + + /// + /// 配置Kafka主题 + /// + /// + /// + public void ConfigureKafkaTopic(ServiceConfigurationContext context, IConfiguration configuration) + { + var adminClient = new AdminClientBuilder(new AdminClientConfig + { + BootstrapServers = configuration.GetConnectionString("Kafka") + }).Build(); + + try + { + string serverTagName = configuration.GetSection("ServerTagName").Value!; + + List topics = ProtocolConstExtensions.GetAllTopicNamesByIssued(serverTagName); + topics.AddRange(ProtocolConstExtensions.GetAllTopicNamesByReceived(serverTagName)); + + List topicSpecifications = new List(); + foreach (var item in topics) + { + topicSpecifications.Add(new TopicSpecification + { + Name = item, + NumPartitions = 3, + ReplicationFactor = 1 + }); + } + adminClient.CreateTopicsAsync(topicSpecifications).ConfigureAwait(false).GetAwaiter().GetResult(); + } + catch (CreateTopicsException e) + { + if (e.Results[0].Error.Code != ErrorCode.TopicAlreadyExists) + { + throw; + } + } + + } } } \ No newline at end of file diff --git a/src/JiShe.CollectBus.Host/CollectBusHostModule.cs b/src/JiShe.CollectBus.Host/CollectBusHostModule.cs index 6f24b1e..f18fcdb 100644 --- a/src/JiShe.CollectBus.Host/CollectBusHostModule.cs +++ b/src/JiShe.CollectBus.Host/CollectBusHostModule.cs @@ -45,6 +45,7 @@ namespace JiShe.CollectBus.Host ConfigureHangfire(context); ConfigureCap(context, configuration); //ConfigureMassTransit(context, configuration); + ConfigureKafkaTopic(context, configuration); ConfigureAuditLog(context); ConfigureCustom(context, configuration); } diff --git a/src/JiShe.CollectBus.Host/appsettings.json b/src/JiShe.CollectBus.Host/appsettings.json index 9694e44..2ef7ded 100644 --- a/src/JiShe.CollectBus.Host/appsettings.json +++ b/src/JiShe.CollectBus.Host/appsettings.json @@ -81,5 +81,6 @@ "DataBaseName": "energy", "OpenDebugMode": true, "UseTableSessionPoolByDefault": false - } + }, + "ServerTagName": "JiSheCollectBus" } \ No newline at end of file diff --git a/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs b/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs index 8c097e2..67abc75 100644 --- a/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs +++ b/src/JiShe.CollectBus.Protocol.Contracts/Extensions/ProtocolConstExtensions.cs @@ -15,24 +15,27 @@ namespace JiShe.CollectBus.Protocol.Contracts /// 自动获取 ProtocolConst 类中所有下行 Kafka 主题名称 /// (通过反射筛选 public const string 且字段名以 "EventName" 结尾的常量) /// - public static List GetAllTopicNamesByIssued() + public static List GetAllTopicNamesByIssued(string serverTagName) { - return typeof(ProtocolConst) + 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 => $"{serverTagName}.{(string)f.GetRawConstantValue()!}") .Select(f => (string)f.GetRawConstantValue()!) .ToList(); + + return topics; } /// /// 自动获取 ProtocolConst 类中所有下行 Kafka 主题名称 /// (通过反射筛选 public const string 且字段名以 "EventName" 结尾的常量) /// - public static List GetAllTopicNamesByReceived() + public static List GetAllTopicNamesByReceived(string serverTagName) { //固定的上报主题 var topicList = typeof(ProtocolConst) @@ -52,6 +55,8 @@ namespace JiShe.CollectBus.Protocol.Contracts topicList.Add(string.Format(ProtocolConst.AFNTopicNameFormat, item.Value.ToString().PadLeft(2, '0'))); } + //return topicList.Select(f => $"{serverTagName}.{f}").ToList(); + return topicList; } }