From 24e0bed979c9196d6f78b21be2e790603b885a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A4=D1=80=D0=BE=D0=BB=D0=BE=D0=B2?= Date: Fri, 9 Apr 2021 17:59:07 +0500 Subject: [PATCH] =?UTF-8?q?=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B8?= =?UTF-8?q?=D0=BB=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BF=D0=BE=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D0=B0?= =?UTF-8?q?=D1=81=20=D0=B2=20signalR,=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BA=D1=83=20=D0=B8=D0=B7=20=D0=B2=D0=B5=D0=B1=20=D0=B0?= =?UTF-8?q?=D0=BF=D0=B8.=20=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D0=BB=20=D0=BA=D0=B5=D1=88.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Data/TelemetryInfoDto.cs | 1 + AsbCloudApp/Services/ITelemetryService.cs | 1 + AsbCloudDb/Model/AsbCloudDbContext.cs | 29 +++++++++++- AsbCloudDb/Model/DataSaubBase.cs | 4 +- AsbCloudDb/Model/Telemetry.cs | 13 ++--- .../Services/Cache/CacheDb.cs | 18 ++++--- .../Services/TelemetryService.cs | 47 ++++++++++++------- .../Services/WellService.cs | 9 +--- AsbCloudWebApi/Controllers/DataController.cs | 3 +- .../Controllers/TelemetryController.cs | 9 +++- .../ITelemetryHubClient.cs | 4 +- .../{WebSocket => SignalR}/TelemetryHub.cs | 9 ++-- AsbCloudWebApi/Startup.cs | 2 +- 13 files changed, 99 insertions(+), 50 deletions(-) rename AsbCloudWebApi/{WebSocket => SignalR}/ITelemetryHubClient.cs (65%) rename AsbCloudWebApi/{WebSocket => SignalR}/TelemetryHub.cs (59%) diff --git a/AsbCloudApp/Data/TelemetryInfoDto.cs b/AsbCloudApp/Data/TelemetryInfoDto.cs index aa971481..c0dec52f 100644 --- a/AsbCloudApp/Data/TelemetryInfoDto.cs +++ b/AsbCloudApp/Data/TelemetryInfoDto.cs @@ -6,6 +6,7 @@ namespace AsbCloudApp.Data { public class TelemetryInfoDto { + public DateTime Date { get; set; } public string Caption { get; set; } public string Cluster { get; set; } public string Deposit { get; set; } diff --git a/AsbCloudApp/Services/ITelemetryService.cs b/AsbCloudApp/Services/ITelemetryService.cs index c19c5bf0..1b421367 100644 --- a/AsbCloudApp/Services/ITelemetryService.cs +++ b/AsbCloudApp/Services/ITelemetryService.cs @@ -9,6 +9,7 @@ namespace AsbCloudApp.Services { public interface ITelemetryService { + int? GetWellIdByUid(string uid); void UpdateData(string uid, TelemetryDataDto data); void UpdateInfo(string uid, TelemetryInfoDto info); } diff --git a/AsbCloudDb/Model/AsbCloudDbContext.cs b/AsbCloudDb/Model/AsbCloudDbContext.cs index cb38a40d..e91b608d 100644 --- a/AsbCloudDb/Model/AsbCloudDbContext.cs +++ b/AsbCloudDb/Model/AsbCloudDbContext.cs @@ -136,12 +136,37 @@ namespace AsbCloudDb.Model .WithOne(p => p.Well) .HasForeignKey(d => d.IdTelemetry) .HasConstraintName("t_well_t_telemetry_id_fk"); + + }); - OnModelCreatingPartial(modelBuilder); + FillData(modelBuilder); } - partial void OnModelCreatingPartial(ModelBuilder modelBuilder); + private void FillData(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasData(new List { + new Deposit{Id = 1, Caption = "месторождение" }, + }); + }); + + modelBuilder.Entity(entity => + { + entity.HasData(new List { + new Cluster{Id = 1, Caption = "месторождение", IdDeposit = 1 }, + }); + }); + + modelBuilder.Entity(entity => + { + entity.HasData(new List { + new Well{Id = 1, IdCluster = 1, IdCustomer = 1, Caption = "скв 1" }, + new Well{Id = 2, IdCluster = 1, IdCustomer = 1, Caption = "скв 2" }, + }); + }); + } public IQueryable GetWellsByCustomer(int idCustomer) { diff --git a/AsbCloudDb/Model/DataSaubBase.cs b/AsbCloudDb/Model/DataSaubBase.cs index dd11cd66..a7585815 100644 --- a/AsbCloudDb/Model/DataSaubBase.cs +++ b/AsbCloudDb/Model/DataSaubBase.cs @@ -2,13 +2,14 @@ using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; #nullable disable namespace AsbCloudDb.Model { [Table("t_data_saub_base"), Comment("набор основных данных по SAUB")] - public partial class DataSaubBase: IId + public partial class DataSaubBase : IId { [Key] [Column("id")] @@ -68,6 +69,7 @@ namespace AsbCloudDb.Model [Column("flow_delta_limit_max"), Comment("Расход. Аварийный макс.")] public double? FlowDeltaLimitMax { get; set; } + [JsonIgnore] [ForeignKey(nameof(IdTelemetry))] [InverseProperty(nameof(Model.Telemetry.DataSaubBases))] public virtual Telemetry Telemetry { get; set; } diff --git a/AsbCloudDb/Model/Telemetry.cs b/AsbCloudDb/Model/Telemetry.cs index d9f3d06b..ac39dafe 100644 --- a/AsbCloudDb/Model/Telemetry.cs +++ b/AsbCloudDb/Model/Telemetry.cs @@ -29,18 +29,15 @@ namespace AsbCloudDb.Model [Column("info", TypeName = "jsonb"), Comment("Информация с панели о скважине")] public string Info { get; set; } - [Column("data", TypeName = "json"), Comment("последние пришедшие данные со скважины в сыром виде")] - public string Data { get; set; } - - [Column("last_data_saub", TypeName = "json"), Comment("последние пришедшие данные со скважины в виде json класса DataSaubBase")] - public string LastDataSaub { get; set; } - [Column("version"), Comment("Версия ПО панели отправляющей телеметрию")] [StringLength(64)] public string Version { get; set; } - //[Column("time_zone", TypeName = "json"), Comment("Временная зона панели САУБ.")] - //public TimeZoneInfo TimeZone { get; set; } + [Column("last_data_saub", TypeName = "jsonb"), Comment("Информация с панели о скважине")] + public DataSaubBase LastDataSaub { get; set; } + + [Column("time_zone", TypeName = "json"), Comment("Временная зона панели САУБ.")] + public TimeZoneInfo TimeZone { get; set; } [InverseProperty(nameof(Model.Well.Telemetry))] public virtual Well Well { get; set; } diff --git a/AsbCloudInfrastructure/Services/Cache/CacheDb.cs b/AsbCloudInfrastructure/Services/Cache/CacheDb.cs index aca8b345..babe3363 100644 --- a/AsbCloudInfrastructure/Services/Cache/CacheDb.cs +++ b/AsbCloudInfrastructure/Services/Cache/CacheDb.cs @@ -2,24 +2,30 @@ using Microsoft.EntityFrameworkCore.Internal; using System; using System.Collections.Generic; +using System.Collections.Concurrent; using System.Text; namespace AsbCloudInfrastructure.Services.Cache { public class CacheDb - { - private Dictionary> cache = new Dictionary>(); + { + private ConcurrentDictionary> cache = new ConcurrentDictionary>(); public ICacheTable GetCachedTable(DbContext context) where TEntity : class { - var entityType = typeof(TEntity); - if (!cache.ContainsKey(entityType)) - cache[entityType] = new List(8); + var entityTypeName = typeof(TEntity).FullName; - var tableCache = new CacheTable(context, (List)cache[entityType]); + if (!cache.ContainsKey(entityTypeName)) + cache[entityTypeName] = new List(8); + + var tableCache = new CacheTable(context, (List)cache[entityTypeName]); return tableCache; } + + public void DropAll()=> cache.Clear(); + + public void Drop() => cache.Remove(typeof(TEntity).FullName, out _); } } diff --git a/AsbCloudInfrastructure/Services/TelemetryService.cs b/AsbCloudInfrastructure/Services/TelemetryService.cs index 3e1d7f5d..388c2aa9 100644 --- a/AsbCloudInfrastructure/Services/TelemetryService.cs +++ b/AsbCloudInfrastructure/Services/TelemetryService.cs @@ -17,55 +17,68 @@ namespace AsbCloudInfrastructure.Services private readonly IAsbCloudDbContext db; private readonly IMapper mapper; private readonly ICacheTable cacheTelemetry; + private readonly ICacheTable cacheWell; public TelemetryService(IAsbCloudDbContext db, CacheDb cacheDb, MapperConfiguration mapperConfiguration) { this.db = db; mapper = mapperConfiguration.CreateMapper(); cacheTelemetry = cacheDb.GetCachedTable((AsbCloudDbContext)db); + cacheWell = cacheDb.GetCachedTable((AsbCloudDbContext)db); } + public int? GetWellIdByUid(string uid) + { + var tele = cacheTelemetry.FirstOrDefault(t => t.RemoteUid == uid, RefreshMode.IfResultEmpty); + if (tele is null) + return null; + + return cacheWell.FirstOrDefault(w => w?.IdTelemetry == tele.Id)?.Id; + } + + private Telemetry GetOrCreateTelemetry(string uid) + => cacheTelemetry.FirstOrDefault(t => t.RemoteUid == uid, RefreshMode.IfResultEmpty) + ?? cacheTelemetry.Insert(new Telemetry{ RemoteUid = uid, }); + public void UpdateData(string uid, TelemetryDataDto data) { - var telemetry = cacheTelemetry.FirstOrDefault(t => t.RemoteUid == uid, RefreshMode.IfResultEmpty); + var telemetry = GetOrCreateTelemetry(uid); - if(telemetry is null) - { - var newTelemetry = new Telemetry - { - RemoteUid = uid, - Version = data.HmiVersion, - Data = JsonSerializer.Serialize(data), - LastDataSaub = JsonSerializer.Serialize(data.DataSaub), - }; - telemetry = cacheTelemetry.Insert(newTelemetry); - } - - if (data.DataSaub != default) + if ((data.DataSaub != default) && (data.DataSaub.Count() > 0)) { + DataSaubBase dataSaub = default; foreach (var item in data.DataSaub) { - var dataSaub = mapper.Map(item); + dataSaub = mapper.Map(item); dataSaub.IdTelemetry = telemetry.Id; db.DataSaubBases.Add(dataSaub); } + if(dataSaub != default) + telemetry.LastDataSaub = dataSaub; + db.SaveChanges(); - } + } } public void UpdateInfo(string uid, TelemetryInfoDto info) { var telemetry = cacheTelemetry.FirstOrDefault(t => t.RemoteUid == uid); + var infoJson = JsonSerializer.Serialize(info); + + //TODO: update telemetry timezone + if (telemetry is null) { var newTelemetry = new Telemetry { RemoteUid = uid, - Info = JsonSerializer.Serialize(info), + Info = infoJson, }; telemetry = cacheTelemetry.Insert(newTelemetry); } + else + cacheTelemetry.Update(t => t.RemoteUid == uid, t => t.Info = infoJson); } } } diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index 4867e58d..383416bd 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -34,13 +34,8 @@ namespace AsbCloudInfrastructure.Services Deposit = well.Cluster.Deposit.Caption, }; - var dataJson = well.Telemetry?.LastDataSaub; - - if (string.IsNullOrEmpty(dataJson)) - return wellDto; - - var data = System.Text.Json.JsonSerializer.Deserialize(dataJson); - wellDto.LastData = mapperConfiguration.CreateMapper().Map(data); + if (well.Telemetry?.LastDataSaub != default) + wellDto.LastData = mapperConfiguration.CreateMapper().Map(well.Telemetry.LastDataSaub); return wellDto; } diff --git a/AsbCloudWebApi/Controllers/DataController.cs b/AsbCloudWebApi/Controllers/DataController.cs index 6e1166c2..1edd7973 100644 --- a/AsbCloudWebApi/Controllers/DataController.cs +++ b/AsbCloudWebApi/Controllers/DataController.cs @@ -25,7 +25,8 @@ namespace AsbCloudWebApi.Controllers } /// - /// Возвращает данные САУБ по скважине + /// Возвращает данные САУБ по скважине. + /// По умолчанию за последние 10 минут. /// /// /// diff --git a/AsbCloudWebApi/Controllers/TelemetryController.cs b/AsbCloudWebApi/Controllers/TelemetryController.cs index f65bf2b0..8bf57acb 100644 --- a/AsbCloudWebApi/Controllers/TelemetryController.cs +++ b/AsbCloudWebApi/Controllers/TelemetryController.cs @@ -1,6 +1,6 @@ using AsbCloudApp.Data; using AsbCloudApp.Services; -using AsbCloudWebApi.WebSocket; +using AsbCloudWebApi.SignalR; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; @@ -52,7 +52,12 @@ namespace AsbCloudWebApi.Controllers public IActionResult Data(string uid, [FromBody] TelemetryDataDto data) { telemetryService.UpdateData(uid, data); - //telemetryHubContext.Clients.Group(""). + + var wellId = telemetryService.GetWellIdByUid(uid); + + if (wellId != null && data.DataSaub?.Count > 0) + Task.Run(() => telemetryHubContext.Clients.Group($"well_{wellId}").SendAsync(nameof(ITelemetryHubClient.ReceiveDataSaub), data.DataSaub) ); + return Ok(); } diff --git a/AsbCloudWebApi/WebSocket/ITelemetryHubClient.cs b/AsbCloudWebApi/SignalR/ITelemetryHubClient.cs similarity index 65% rename from AsbCloudWebApi/WebSocket/ITelemetryHubClient.cs rename to AsbCloudWebApi/SignalR/ITelemetryHubClient.cs index 086f4fa7..83e1f4ee 100644 --- a/AsbCloudWebApi/WebSocket/ITelemetryHubClient.cs +++ b/AsbCloudWebApi/SignalR/ITelemetryHubClient.cs @@ -4,10 +4,10 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace AsbCloudWebApi.WebSocket +namespace AsbCloudWebApi.SignalR { public interface ITelemetryHubClient { - Task ReceiveDataSaub(DataSaubBaseDto data); + Task ReceiveDataSaub(IEnumerable data); } } diff --git a/AsbCloudWebApi/WebSocket/TelemetryHub.cs b/AsbCloudWebApi/SignalR/TelemetryHub.cs similarity index 59% rename from AsbCloudWebApi/WebSocket/TelemetryHub.cs rename to AsbCloudWebApi/SignalR/TelemetryHub.cs index d09eed4a..59ae127d 100644 --- a/AsbCloudWebApi/WebSocket/TelemetryHub.cs +++ b/AsbCloudWebApi/SignalR/TelemetryHub.cs @@ -1,10 +1,13 @@ using AsbCloudApp.Data; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; +using System.Collections.Generic; using System.Threading.Tasks; -namespace AsbCloudWebApi.WebSocket +namespace AsbCloudWebApi.SignalR { + // SignalR manual: + // https://docs.microsoft.com/ru-ru/aspnet/core/signalr/introduction?view=aspnetcore-5.0 [Authorize] public class TelemetryHub : Hub @@ -15,8 +18,8 @@ namespace AsbCloudWebApi.WebSocket public Task RemoveFromGroup(string groupName) => Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName); - public Task SendDataSaub(int wellId, DataSaubBaseDto data) - => Clients.All.ReceiveDataSaub(data); + public Task SendDataSaub(string groupName, IEnumerable data) + => Clients.Group(groupName).ReceiveDataSaub(data); } } \ No newline at end of file diff --git a/AsbCloudWebApi/Startup.cs b/AsbCloudWebApi/Startup.cs index 9b8bc99e..a4d78058 100644 --- a/AsbCloudWebApi/Startup.cs +++ b/AsbCloudWebApi/Startup.cs @@ -1,5 +1,5 @@ using AsbCloudInfrastructure; -using AsbCloudWebApi.WebSocket; +using AsbCloudWebApi.SignalR; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration;