merge dev to feature/email_notifications

This commit is contained in:
ngfrolov 2023-09-15 10:45:54 +05:00
commit c05fc0f400
Signed by: ng.frolov
GPG Key ID: E99907A0357B29A7
12 changed files with 190 additions and 29 deletions

View File

@ -51,6 +51,11 @@ namespace AsbCloudApp.Data
/// </summary> /// </summary>
public int IdState { get; set; } public int IdState { get; set; }
/// <summary>
/// Название текущей секции
/// </summary>
public string? Section { get; set; }
/// <summary> /// <summary>
/// Коэф-т использования автоподачи долота (суммарный ротор + слайд) /// Коэф-т использования автоподачи долота (суммарный ротор + слайд)
/// </summary> /// </summary>
@ -89,6 +94,31 @@ namespace AsbCloudApp.Data
/// </summary> /// </summary>
public PlanFactDto<double?> ROP { get; set; } = null!; public PlanFactDto<double?> ROP { get; set; } = null!;
/// <summary>
/// Нагрузка на долота, Т
/// </summary>
public PlanFactDto<double?> AxialLoad { get; set; } = null!;
/// <summary>
/// Обороты ротора
/// </summary>
public PlanFactDto<double?> TopDriveSpeed { get; set; } = null!;
/// <summary>
/// Момент ротора кн/м
/// </summary>
public PlanFactDto<double?> TopDriveTorque { get; set; } = null!;
/// <summary>
/// Перепад давления
/// </summary>
public PlanFactDto<double?> Pressure { get; set; } = null!;
/// <summary>
/// Действующее задание давления, атм
/// </summary>
public double? PressureSp { get; set; }
/// <summary> /// <summary>
/// Плановая и текущая глубина /// Плановая и текущая глубина
/// </summary> /// </summary>

View File

@ -0,0 +1,6 @@
namespace AsbCloudApp.IntegrationEvents.Interfaces;
/// <summary>
/// Интерфейс маркер для доменных событий
/// </summary>
public interface IIntegrationEvent { }

View File

@ -0,0 +1,19 @@
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.IntegrationEvents.Interfaces;
/// <summary>
/// Обработчик событий
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IIntegrationEventHandler<in T> where T: IIntegrationEvent
{
/// <summary>
/// Метод обработки события
/// </summary>
/// <param name="integrationEvent"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task HandleAsync(T integrationEvent, CancellationToken cancellationToken);
}

View File

@ -0,0 +1,9 @@
using AsbCloudApp.IntegrationEvents.Interfaces;
namespace AsbCloudApp.IntegrationEvents;
/// <summary>
/// Обновление показателей бурения
/// </summary>
/// <param name="IdWell"></param>
public record UpdateWellInfoEvent(int IdWell) : IIntegrationEvent;

View File

@ -230,11 +230,15 @@ namespace AsbCloudInfrastructure.Services.Subsystems
var beginUTC = gtDate.HasValue var beginUTC = gtDate.HasValue
? gtDate.Value.ToUtcDateTimeOffset(hoursOffset) ? gtDate.Value.ToUtcDateTimeOffset(hoursOffset)
: DateTime.Today.AddDays(-1).ToUtcDateTimeOffset(hoursOffset); : db.SubsystemOperationTimes.Min(s => s.DateStart)
.DateTime
.ToUtcDateTimeOffset(hoursOffset);
var endUTC = ltDate.HasValue var endUTC = ltDate.HasValue
? ltDate.Value.ToUtcDateTimeOffset(hoursOffset) ? ltDate.Value.ToUtcDateTimeOffset(hoursOffset)
: DateTime.Today.ToUtcDateTimeOffset(hoursOffset); : db.SubsystemOperationTimes.Max(s => s.DateEnd)
.DateTime
.ToUtcDateTimeOffset(hoursOffset);
var telemetryIds = wells var telemetryIds = wells
.Where(w => w.IdTelemetry is not null) .Where(w => w.IdTelemetry is not null)

View File

@ -6,7 +6,6 @@ using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudApp.Services.Subsystems; using AsbCloudApp.Services.Subsystems;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Services.SAUB; using AsbCloudInfrastructure.Services.SAUB;
using Mapster; using Mapster;
@ -16,6 +15,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.IntegrationEvents;
using AsbCloudApp.IntegrationEvents.Interfaces;
namespace AsbCloudInfrastructure.Services namespace AsbCloudInfrastructure.Services
{ {
@ -63,23 +64,18 @@ namespace AsbCloudInfrastructure.Services
private static async Task WorkAction(string workName, IServiceProvider serviceProvider, CancellationToken token) private static async Task WorkAction(string workName, IServiceProvider serviceProvider, CancellationToken token)
{ {
var db = serviceProvider.GetRequiredService<IAsbCloudDbContext>();
var wellService = serviceProvider.GetRequiredService<IWellService>(); var wellService = serviceProvider.GetRequiredService<IWellService>();
var operationsStatService = serviceProvider.GetRequiredService<IOperationsStatService>(); var operationsStatService = serviceProvider.GetRequiredService<IOperationsStatService>();
var processMapRepository = serviceProvider.GetRequiredService<IProcessMapPlanRepository>(); var processMapRepository = serviceProvider.GetRequiredService<IProcessMapPlanRepository>();
var subsystemOperationTimeService = serviceProvider.GetRequiredService<ISubsystemOperationTimeService>(); var subsystemOperationTimeService = serviceProvider.GetRequiredService<ISubsystemOperationTimeService>();
var telemetryDataSaubCache = serviceProvider.GetRequiredService<TelemetryDataCache<TelemetryDataSaubDto>>(); var telemetryDataSaubCache = serviceProvider.GetRequiredService<TelemetryDataCache<TelemetryDataSaubDto>>();
var messageHub = serviceProvider.GetRequiredService<IIntegrationEventHandler<UpdateWellInfoEvent>>();
var activeWells = await wellService.GetAsync(new() {IdState = 1}, token); var wells = await wellService.GetAllAsync(token);
IEnumerable<int> activeWellsIds = activeWells var wellsIds = wells.Select(w => w.Id);
.Select(w => w.Id);
var idTelemetries = activeWells var processMapRequests = wellsIds.Select(id => new ProcessMapRequest { IdWell = id });
.Where(w => w.IdTelemetry != null)
.Select(t => t.IdTelemetry);
var processMapRequests = activeWellsIds.Select(id => new ProcessMapRequest { IdWell = id });
var processMaps = await processMapRepository.GetProcessMapAsync(processMapRequests, token); var processMaps = await processMapRepository.GetProcessMapAsync(processMapRequests, token);
var wellDepthByProcessMap = processMaps var wellDepthByProcessMap = processMaps
@ -90,20 +86,23 @@ namespace AsbCloudInfrastructure.Services
DepthEnd = g.Max(p => p.DepthEnd) DepthEnd = g.Max(p => p.DepthEnd)
}); });
var operationsStat = await operationsStatService.GetWellsStatAsync(activeWellsIds, token); var operationsStat = await operationsStatService.GetWellsStatAsync(wellsIds, token);
var subsystemStat = await subsystemOperationTimeService.GetStatByActiveWells(activeWellsIds, token); var subsystemStat = await subsystemOperationTimeService
.GetStatByActiveWells(wellsIds, token);
WellMapInfo = activeWells.Select(well => { WellMapInfo = wells.Select(well => {
var wellMapInfo = well.Adapt<WellMapInfoWithComanies>(); var wellMapInfo = well.Adapt<WellMapInfoWithComanies>();
wellMapInfo.IdState = well.IdState; wellMapInfo.IdState = well.IdState;
double? currentDepth = null; double? currentDepth = null;
TelemetryDataSaubDto? lastSaubTelemetry = null;
if (well.IdTelemetry.HasValue) if (well.IdTelemetry.HasValue)
{ {
wellMapInfo.IdTelemetry = well.IdTelemetry.Value; wellMapInfo.IdTelemetry = well.IdTelemetry.Value;
var lastSaubTelemetry = telemetryDataSaubCache.GetLastOrDefault(well.IdTelemetry.Value); lastSaubTelemetry = telemetryDataSaubCache.GetLastOrDefault(well.IdTelemetry.Value);
if(lastSaubTelemetry is not null) if(lastSaubTelemetry is not null)
{ {
currentDepth = lastSaubTelemetry.WellDepth; currentDepth = lastSaubTelemetry.WellDepth;
@ -119,27 +118,54 @@ namespace AsbCloudInfrastructure.Services
.OrderBy(p => p.DepthEnd); .OrderBy(p => p.DepthEnd);
int? idSection = wellLastFactSection?.Id; int? idSection = wellLastFactSection?.Id;
ProcessMapPlanDto? welllProcessMap = null; ProcessMapPlanDto? wellProcessMap = null;
if (idSection.HasValue) if (idSection.HasValue)
{ {
welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.IdWellSectionType == idSection); wellProcessMap = wellProcessMaps.FirstOrDefault(p => p.IdWellSectionType == idSection);
} }
else if(currentDepth.HasValue) else if(currentDepth.HasValue)
{ {
welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.DepthStart <= currentDepth.Value && p.DepthEnd >= currentDepth.Value); wellProcessMap = wellProcessMaps.FirstOrDefault(p => p.DepthStart <= currentDepth.Value && p.DepthEnd >= currentDepth.Value);
idSection ??= welllProcessMap?.IdWellSectionType;
} }
double? planTotalDepth = null; double? planTotalDepth = null;
planTotalDepth = wellDepthByProcessMap.FirstOrDefault(p => p.Id == well.Id)?.DepthEnd; planTotalDepth = wellDepthByProcessMap.FirstOrDefault(p => p.Id == well.Id)?.DepthEnd;
planTotalDepth ??= wellOperationsStat?.Total.Plan?.WellDepthEnd; planTotalDepth ??= wellOperationsStat?.Total.Plan?.WellDepthEnd;
wellMapInfo.Section = wellLastFactSection?.Caption;
wellMapInfo.FirstFactOperationDateStart = wellOperationsStat?.Total.Fact?.Start wellMapInfo.FirstFactOperationDateStart = wellOperationsStat?.Total.Fact?.Start
?? wellOperationsStat?.Total.Plan?.Start; ?? wellOperationsStat?.Total.Plan?.Start;
wellMapInfo.LastPredictOperationDateEnd = wellOperationsStat?.Total.Plan?.End; wellMapInfo.LastPredictOperationDateEnd = wellOperationsStat?.Total.Plan?.End;
wellMapInfo.AxialLoad = new()
{
Plan = wellProcessMap?.AxialLoad.Plan,
Fact = lastSaubTelemetry?.AxialLoad
};
wellMapInfo.TopDriveSpeed = new()
{
Plan = wellProcessMap?.TopDriveSpeed.Plan,
Fact = lastSaubTelemetry?.RotorSpeed
};
wellMapInfo.TopDriveTorque = new()
{
Plan = wellProcessMap?.TopDriveTorque.Plan,
Fact = lastSaubTelemetry?.RotorTorque
};
wellMapInfo.Pressure = new()
{
Plan = wellProcessMap?.Pressure.Plan,
Fact = lastSaubTelemetry?.Pressure
};
wellMapInfo.PressureSp = lastSaubTelemetry?.PressureSp;
wellMapInfo.WellDepth = new() wellMapInfo.WellDepth = new()
{ {
Plan = planTotalDepth, Plan = planTotalDepth,
@ -148,7 +174,7 @@ namespace AsbCloudInfrastructure.Services
wellMapInfo.ROP = new() wellMapInfo.ROP = new()
{ {
Plan = welllProcessMap?.RopPlan, Plan = wellProcessMap?.RopPlan,
Fact = wellOperationsStat?.Total.Fact?.Rop, Fact = wellOperationsStat?.Total.Fact?.Rop,
}; };
@ -167,6 +193,11 @@ namespace AsbCloudInfrastructure.Services
return wellMapInfo; return wellMapInfo;
}).ToArray(); }).ToArray();
var updateWellInfoEventTasks = wellsIds.Select(idWell =>
messageHub.HandleAsync(new UpdateWellInfoEvent(idWell), token));
await Task.WhenAll(updateWellInfoEventTasks);
} }
private WellMapInfoWithTelemetryStat Convert(WellMapInfoWithComanies wellInfo) private WellMapInfoWithTelemetryStat Convert(WellMapInfoWithComanies wellInfo)

View File

@ -92,7 +92,7 @@ namespace AsbCloudInfrastructure.Services
Longitude = gCluster.Key.Longitude ?? gDeposit.Key.Longitude, Longitude = gCluster.Key.Longitude ?? gDeposit.Key.Longitude,
Wells = gCluster.Select(well => Wells = gCluster.Select(well =>
{ {
var dto = wellInfoService.FirstOrDefault(w => w.Id == well.Id); var dto = wellInfoService.FirstOrDefault(w => w.Id == well.Id && well.IdState == 1);
dto ??= well.Adapt<WellMapInfoWithTelemetryStat>(); dto ??= well.Adapt<WellMapInfoWithTelemetryStat>();
dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude; dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude;
dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude; dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude;

View File

@ -32,7 +32,7 @@ namespace AsbCloudWebApi.Controllers
IWellOperationImportService wellOperationImportService, IWellOperationImportService wellOperationImportService,
IUserRepository userRepository) IUserRepository userRepository)
{ {
this.operationRepository = operationService; this.operationRepository = operationRepository;
this.wellService = wellService; this.wellService = wellService;
this.wellOperationImportService = wellOperationImportService; this.wellOperationImportService = wellOperationImportService;
this.userRepository = userRepository; this.userRepository = userRepository;

View File

@ -3,7 +3,6 @@ using AsbCloudApp.Repositories;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services; using AsbCloudInfrastructure.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
@ -12,9 +11,13 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsbCloudApp.IntegrationEvents;
using AsbCloudApp.IntegrationEvents.Interfaces;
using AsbCloudApp.Services.Notifications; using AsbCloudApp.Services.Notifications;
using AsbCloudInfrastructure.Services.Email; using AsbCloudInfrastructure.Services.Email;
using AsbCloudWebApi.SignalR;
using AsbCloudWebApi.SignalR.Services; using AsbCloudWebApi.SignalR.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Any;
namespace AsbCloudWebApi namespace AsbCloudWebApi
@ -144,5 +147,8 @@ namespace AsbCloudWebApi
services.AddTransient<NotificationPublisher>(); services.AddTransient<NotificationPublisher>();
} }
public static void AddIntegrationEvents(this IServiceCollection services) => services
.AddTransient<IIntegrationEventHandler<UpdateWellInfoEvent>, WellInfoHub>();
} }
} }

View File

@ -0,0 +1,53 @@
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.IntegrationEvents;
using AsbCloudApp.IntegrationEvents.Interfaces;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services;
using Microsoft.AspNetCore.SignalR;
namespace AsbCloudWebApi.SignalR;
public class WellInfoHub : BaseHub, IIntegrationEventHandler<UpdateWellInfoEvent>
{
private readonly IHubContext<WellInfoHub> hubContext;
private readonly IWellService wellService;
private readonly WellInfoService wellInfoService;
public WellInfoHub(IHubContext<WellInfoHub> hubContext,
IWellService wellService,
WellInfoService wellInfoService)
{
this.hubContext = hubContext;
this.wellService = wellService;
this.wellInfoService = wellInfoService;
}
public override async Task AddToGroup(string groupName)
{
var idWell = int.Parse(groupName.Split('_')[2]);
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await HandleAsync(new UpdateWellInfoEvent(idWell), CancellationToken.None);
}
public async Task HandleAsync(UpdateWellInfoEvent integrationEvent, CancellationToken cancellationToken)
{
const string method = "update_well_info";
var well = await wellService.GetOrDefaultAsync(integrationEvent.IdWell, cancellationToken);
if(well is null)
return;
var wellInfo = wellInfoService.FirstOrDefault(w => w.Id == well.Id);
await hubContext.Clients.Group($"well_info_{integrationEvent.IdWell}")
.SendAsync(method, new
{
Well = well,
WellInfo = wellInfo
}, cancellationToken);
}
}

View File

@ -45,6 +45,8 @@ namespace AsbCloudWebApi
services.AddNotificationTransportServices(); services.AddNotificationTransportServices();
services.AddIntegrationEvents();
services.AddJWTAuthentication(); services.AddJWTAuthentication();
services.AddSignalR() services.AddSignalR()
@ -151,6 +153,7 @@ namespace AsbCloudWebApi
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers(); endpoints.MapControllers();
endpoints.MapHub<WellInfoHub>("/hubs/wellInfo");
endpoints.MapHub<NotificationHub>("/hubs/notifications"); endpoints.MapHub<NotificationHub>("/hubs/notifications");
endpoints.MapHub<TelemetryHub>("/hubs/telemetry"); endpoints.MapHub<TelemetryHub>("/hubs/telemetry");
endpoints.MapHub<ReportsHub>("/hubs/reports"); endpoints.MapHub<ReportsHub>("/hubs/reports");

View File

@ -10,7 +10,7 @@ internal class Program
{ {
var connectionBuilder = new HubConnectionBuilder(); var connectionBuilder = new HubConnectionBuilder();
var connection = connectionBuilder var connection = connectionBuilder
.WithUrl("http://localhost:5000/hubs/notifications", connectionOptions => { .WithUrl("http://localhost:5000/hubs/limitingParameters", connectionOptions => {
connectionOptions.AccessTokenProvider = AccessTokenProvider; connectionOptions.AccessTokenProvider = AccessTokenProvider;
}) })
.WithAutomaticReconnect() .WithAutomaticReconnect()
@ -26,9 +26,9 @@ internal class Program
connection.StartAsync().Wait(); connection.StartAsync().Wait();
//Console.WriteLine("OnConnected"); //Console.WriteLine("OnConnected");
//connection.SendCoreAsync("OnConnected", new object[] { }, CancellationToken.None).Wait(); connection.SendCoreAsync("OnConnectedAsync", new object[] { 1 }, CancellationToken.None).Wait();
var subsction = connection.On<object>("receiveNotifications", (str1) => { var subsction = connection.On<object>("well_info_update", (str1) => {
Console.WriteLine(str1); Console.WriteLine(str1);
} ); } );