настроил разделение по группас в signalR, отправку из веб апи. доработал кеш.

This commit is contained in:
Фролов 2021-04-09 17:59:07 +05:00
parent 5e80746333
commit 24e0bed979
13 changed files with 99 additions and 50 deletions

View File

@ -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; }

View File

@ -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);
}

View File

@ -136,12 +136,37 @@ namespace AsbCloudDb.Model
.WithOne(p => p.Well)
.HasForeignKey<Well>(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<Deposit>(entity =>
{
entity.HasData(new List<Deposit> {
new Deposit{Id = 1, Caption = "месторождение" },
});
});
modelBuilder.Entity<Cluster>(entity =>
{
entity.HasData(new List<Cluster> {
new Cluster{Id = 1, Caption = "месторождение", IdDeposit = 1 },
});
});
modelBuilder.Entity<Well>(entity =>
{
entity.HasData(new List<Well> {
new Well{Id = 1, IdCluster = 1, IdCustomer = 1, Caption = "скв 1" },
new Well{Id = 2, IdCluster = 1, IdCustomer = 1, Caption = "скв 2" },
});
});
}
public IQueryable<Well> GetWellsByCustomer(int idCustomer)
{

View File

@ -2,6 +2,7 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
#nullable disable
@ -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; }

View File

@ -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; }

View File

@ -2,6 +2,7 @@
using Microsoft.EntityFrameworkCore.Internal;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Text;
namespace AsbCloudInfrastructure.Services.Cache
@ -9,17 +10,22 @@ namespace AsbCloudInfrastructure.Services.Cache
public class CacheDb
{
private Dictionary<Type, IEnumerable<object>> cache = new Dictionary<Type, IEnumerable<object>>();
private ConcurrentDictionary<string, IEnumerable<object>> cache = new ConcurrentDictionary<string, IEnumerable<object>>();
public ICacheTable<TEntity> GetCachedTable<TEntity>(DbContext context)
where TEntity : class
{
var entityType = typeof(TEntity);
if (!cache.ContainsKey(entityType))
cache[entityType] = new List<TEntity>(8);
var entityTypeName = typeof(TEntity).FullName;
var tableCache = new CacheTable<TEntity>(context, (List<TEntity>)cache[entityType]);
if (!cache.ContainsKey(entityTypeName))
cache[entityTypeName] = new List<TEntity>(8);
var tableCache = new CacheTable<TEntity>(context, (List<TEntity>)cache[entityTypeName]);
return tableCache;
}
public void DropAll()=> cache.Clear();
public void Drop<TEntity>() => cache.Remove(typeof(TEntity).FullName, out _);
}
}

View File

@ -17,39 +17,46 @@ namespace AsbCloudInfrastructure.Services
private readonly IAsbCloudDbContext db;
private readonly IMapper mapper;
private readonly ICacheTable<Telemetry> cacheTelemetry;
private readonly ICacheTable<Well> cacheWell;
public TelemetryService(IAsbCloudDbContext db, CacheDb cacheDb, MapperConfiguration mapperConfiguration)
{
this.db = db;
mapper = mapperConfiguration.CreateMapper();
cacheTelemetry = cacheDb.GetCachedTable<Telemetry>((AsbCloudDbContext)db);
cacheWell = cacheDb.GetCachedTable<Well>((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<DataSaubBase>(item);
dataSaub = mapper.Map<DataSaubBase>(item);
dataSaub.IdTelemetry = telemetry.Id;
db.DataSaubBases.Add(dataSaub);
}
if(dataSaub != default)
telemetry.LastDataSaub = dataSaub;
db.SaveChanges();
}
}
@ -57,15 +64,21 @@ namespace AsbCloudInfrastructure.Services
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);
}
}
}

View File

@ -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<DataSaubBase>(dataJson);
wellDto.LastData = mapperConfiguration.CreateMapper().Map<DataSaubBaseDto>(data);
if (well.Telemetry?.LastDataSaub != default)
wellDto.LastData = mapperConfiguration.CreateMapper().Map<DataSaubBaseDto>(well.Telemetry.LastDataSaub);
return wellDto;
}

View File

@ -25,7 +25,8 @@ namespace AsbCloudWebApi.Controllers
}
/// <summary>
/// Возвращает данные САУБ по скважине
/// Возвращает данные САУБ по скважине.
/// По умолчанию за последние 10 минут.
/// </summary>
/// <param name="wellId"></param>
/// <param name=""></param>

View File

@ -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();
}

View File

@ -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<DataSaubBaseDto> data);
}
}

View File

@ -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<ITelemetryHubClient>
@ -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<DataSaubBaseDto> data)
=> Clients.Group(groupName).ReceiveDataSaub(data);
}
}

View File

@ -1,5 +1,5 @@
using AsbCloudInfrastructure;
using AsbCloudWebApi.WebSocket;
using AsbCloudWebApi.SignalR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;