diff --git a/AsbCloudApp/Data/WellMapInfoDto.cs b/AsbCloudApp/Data/WellMapInfoDto.cs index 242ecb6c..8b0bdb58 100644 --- a/AsbCloudApp/Data/WellMapInfoDto.cs +++ b/AsbCloudApp/Data/WellMapInfoDto.cs @@ -50,6 +50,11 @@ namespace AsbCloudApp.Data /// 2 - завершена /// public int IdState { get; set; } + + /// + /// Название текущей секции + /// + public string? Section { get; set; } /// /// Коэф-т использования автоподачи долота (суммарный ротор + слайд) @@ -89,6 +94,31 @@ namespace AsbCloudApp.Data /// public PlanFactDto ROP { get; set; } = null!; + /// + /// Нагрузка на долота, Т + /// + public PlanFactDto AxialLoad { get; set; } = null!; + + /// + /// Обороты ротора + /// + public PlanFactDto TopDriveSpeed { get; set; } = null!; + + /// + /// Момент ротора кн/м + /// + public PlanFactDto TopDriveTorque { get; set; } = null!; + + /// + /// Перепад давления + /// + public PlanFactDto Pressure { get; set; } = null!; + + /// + /// Действующее задание давления, атм + /// + public double? PressureSp { get; set; } + /// /// Плановая и текущая глубина /// diff --git a/AsbCloudApp/IntegrationEvents/Interfaces/IIntegrationEvent.cs b/AsbCloudApp/IntegrationEvents/Interfaces/IIntegrationEvent.cs new file mode 100644 index 00000000..a80dfdee --- /dev/null +++ b/AsbCloudApp/IntegrationEvents/Interfaces/IIntegrationEvent.cs @@ -0,0 +1,6 @@ +namespace AsbCloudApp.IntegrationEvents.Interfaces; + +/// +/// Интерфейс маркер для доменных событий +/// +public interface IIntegrationEvent { } \ No newline at end of file diff --git a/AsbCloudApp/IntegrationEvents/Interfaces/IIntegrationEventHandler.cs b/AsbCloudApp/IntegrationEvents/Interfaces/IIntegrationEventHandler.cs new file mode 100644 index 00000000..18695f07 --- /dev/null +++ b/AsbCloudApp/IntegrationEvents/Interfaces/IIntegrationEventHandler.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudApp.IntegrationEvents.Interfaces; + +/// +/// Обработчик событий +/// +/// +public interface IIntegrationEventHandler where T: IIntegrationEvent +{ + /// + /// Метод обработки события + /// + /// + /// + /// + Task HandleAsync(T integrationEvent, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/AsbCloudApp/IntegrationEvents/UpdateWellInfoEvent.cs b/AsbCloudApp/IntegrationEvents/UpdateWellInfoEvent.cs new file mode 100644 index 00000000..4ae75a19 --- /dev/null +++ b/AsbCloudApp/IntegrationEvents/UpdateWellInfoEvent.cs @@ -0,0 +1,9 @@ +using AsbCloudApp.IntegrationEvents.Interfaces; + +namespace AsbCloudApp.IntegrationEvents; + +/// +/// Обновление показателей бурения +/// +/// +public record UpdateWellInfoEvent(int IdWell) : IIntegrationEvent; \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs index 1faa769f..a971116e 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs @@ -230,11 +230,15 @@ namespace AsbCloudInfrastructure.Services.Subsystems var beginUTC = gtDate.HasValue ? gtDate.Value.ToUtcDateTimeOffset(hoursOffset) - : DateTime.Today.AddDays(-1).ToUtcDateTimeOffset(hoursOffset); + : db.SubsystemOperationTimes.Min(s => s.DateStart) + .DateTime + .ToUtcDateTimeOffset(hoursOffset); var endUTC = ltDate.HasValue ? ltDate.Value.ToUtcDateTimeOffset(hoursOffset) - : DateTime.Today.ToUtcDateTimeOffset(hoursOffset); + : db.SubsystemOperationTimes.Max(s => s.DateEnd) + .DateTime + .ToUtcDateTimeOffset(hoursOffset); var telemetryIds = wells .Where(w => w.IdTelemetry is not null) diff --git a/AsbCloudInfrastructure/Services/WellInfoService.cs b/AsbCloudInfrastructure/Services/WellInfoService.cs index 7066ef22..efcfd54d 100644 --- a/AsbCloudInfrastructure/Services/WellInfoService.cs +++ b/AsbCloudInfrastructure/Services/WellInfoService.cs @@ -6,7 +6,6 @@ using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudApp.Services.Subsystems; -using AsbCloudDb.Model; using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Services.SAUB; using Mapster; @@ -16,6 +15,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using AsbCloudApp.IntegrationEvents; +using AsbCloudApp.IntegrationEvents.Interfaces; namespace AsbCloudInfrastructure.Services { @@ -36,7 +37,7 @@ namespace AsbCloudInfrastructure.Services private readonly IWitsRecordRepository witsRecord1Repository; private readonly IGtrRepository gtrRepository; private static IEnumerable WellMapInfo = Enumerable.Empty(); - + public WellInfoService( TelemetryDataCache telemetryDataSaubCache, TelemetryDataCache telemetryDataSpinCache, @@ -63,23 +64,18 @@ namespace AsbCloudInfrastructure.Services private static async Task WorkAction(string workName, IServiceProvider serviceProvider, CancellationToken token) { - var db = serviceProvider.GetRequiredService(); var wellService = serviceProvider.GetRequiredService(); var operationsStatService = serviceProvider.GetRequiredService(); var processMapRepository = serviceProvider.GetRequiredService(); var subsystemOperationTimeService = serviceProvider.GetRequiredService(); var telemetryDataSaubCache = serviceProvider.GetRequiredService>(); + var messageHub = serviceProvider.GetRequiredService>(); - var activeWells = await wellService.GetAsync(new() {IdState = 1}, token); + var wells = await wellService.GetAllAsync(token); - IEnumerable activeWellsIds = activeWells - .Select(w => w.Id); + var wellsIds = wells.Select(w => w.Id); - var idTelemetries = activeWells - .Where(w => w.IdTelemetry != null) - .Select(t => t.IdTelemetry); - - var processMapRequests = activeWellsIds.Select(id => new ProcessMapRequest { IdWell = id }); + var processMapRequests = wellsIds.Select(id => new ProcessMapRequest { IdWell = id }); var processMaps = await processMapRepository.GetProcessMapAsync(processMapRequests, token); var wellDepthByProcessMap = processMaps @@ -90,20 +86,23 @@ namespace AsbCloudInfrastructure.Services 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(); wellMapInfo.IdState = well.IdState; double? currentDepth = null; + TelemetryDataSaubDto? lastSaubTelemetry = null; + if (well.IdTelemetry.HasValue) { wellMapInfo.IdTelemetry = well.IdTelemetry.Value; - var lastSaubTelemetry = telemetryDataSaubCache.GetLastOrDefault(well.IdTelemetry.Value); + lastSaubTelemetry = telemetryDataSaubCache.GetLastOrDefault(well.IdTelemetry.Value); if(lastSaubTelemetry is not null) { currentDepth = lastSaubTelemetry.WellDepth; @@ -119,27 +118,54 @@ namespace AsbCloudInfrastructure.Services .OrderBy(p => p.DepthEnd); int? idSection = wellLastFactSection?.Id; - ProcessMapPlanDto? welllProcessMap = null; + ProcessMapPlanDto? wellProcessMap = null; if (idSection.HasValue) { - welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.IdWellSectionType == idSection); + wellProcessMap = wellProcessMaps.FirstOrDefault(p => p.IdWellSectionType == idSection); } else if(currentDepth.HasValue) { - welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.DepthStart <= currentDepth.Value && p.DepthEnd >= currentDepth.Value); - idSection ??= welllProcessMap?.IdWellSectionType; + wellProcessMap = wellProcessMaps.FirstOrDefault(p => p.DepthStart <= currentDepth.Value && p.DepthEnd >= currentDepth.Value); } double? planTotalDepth = null; planTotalDepth = wellDepthByProcessMap.FirstOrDefault(p => p.Id == well.Id)?.DepthEnd; planTotalDepth ??= wellOperationsStat?.Total.Plan?.WellDepthEnd; + wellMapInfo.Section = wellLastFactSection?.Caption; + wellMapInfo.FirstFactOperationDateStart = wellOperationsStat?.Total.Fact?.Start ?? wellOperationsStat?.Total.Plan?.Start; 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() { Plan = planTotalDepth, @@ -148,7 +174,7 @@ namespace AsbCloudInfrastructure.Services wellMapInfo.ROP = new() { - Plan = welllProcessMap?.RopPlan, + Plan = wellProcessMap?.RopPlan, Fact = wellOperationsStat?.Total.Fact?.Rop, }; @@ -167,6 +193,11 @@ namespace AsbCloudInfrastructure.Services return wellMapInfo; }).ToArray(); + + var updateWellInfoEventTasks = wellsIds.Select(idWell => + messageHub.HandleAsync(new UpdateWellInfoEvent(idWell), token)); + + await Task.WhenAll(updateWellInfoEventTasks); } private WellMapInfoWithTelemetryStat Convert(WellMapInfoWithComanies wellInfo) diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index c47dd204..4973baf0 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -92,7 +92,7 @@ namespace AsbCloudInfrastructure.Services Longitude = gCluster.Key.Longitude ?? gDeposit.Key.Longitude, 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(); dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude; dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude; diff --git a/AsbCloudWebApi/Controllers/WellOperationController.cs b/AsbCloudWebApi/Controllers/WellOperationController.cs index d2a619c4..13abc59a 100644 --- a/AsbCloudWebApi/Controllers/WellOperationController.cs +++ b/AsbCloudWebApi/Controllers/WellOperationController.cs @@ -32,7 +32,7 @@ namespace AsbCloudWebApi.Controllers IWellOperationImportService wellOperationImportService, IUserRepository userRepository) { - this.operationRepository = operationService; + this.operationRepository = operationRepository; this.wellService = wellService; this.wellOperationImportService = wellOperationImportService; this.userRepository = userRepository; @@ -218,7 +218,7 @@ namespace AsbCloudWebApi.Controllers var result = await operationRepository.InsertRangeAsync(values, token) .ConfigureAwait(false); - + return Ok(result); } diff --git a/AsbCloudWebApi/DependencyInjection.cs b/AsbCloudWebApi/DependencyInjection.cs index 7477f745..09922854 100644 --- a/AsbCloudWebApi/DependencyInjection.cs +++ b/AsbCloudWebApi/DependencyInjection.cs @@ -3,7 +3,6 @@ using AsbCloudApp.Repositories; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; @@ -12,8 +11,12 @@ using System.Collections.Generic; using System.IO; using System.Reflection; using System.Threading.Tasks; +using AsbCloudApp.IntegrationEvents; +using AsbCloudApp.IntegrationEvents.Interfaces; using AsbCloudApp.Services.Notifications; +using AsbCloudWebApi.SignalR; using AsbCloudWebApi.SignalR.Services; +using Microsoft.AspNetCore.Mvc; using Microsoft.OpenApi.Any; namespace AsbCloudWebApi @@ -140,5 +143,8 @@ namespace AsbCloudWebApi { services.AddTransient(); } + + public static void AddIntegrationEvents(this IServiceCollection services) => services + .AddTransient, WellInfoHub>(); } } diff --git a/AsbCloudWebApi/SignalR/WellInfoHub.cs b/AsbCloudWebApi/SignalR/WellInfoHub.cs new file mode 100644 index 00000000..677b4f64 --- /dev/null +++ b/AsbCloudWebApi/SignalR/WellInfoHub.cs @@ -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 +{ + private readonly IHubContext hubContext; + private readonly IWellService wellService; + private readonly WellInfoService wellInfoService; + + public WellInfoHub(IHubContext 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); + } +} \ No newline at end of file diff --git a/AsbCloudWebApi/Startup.cs b/AsbCloudWebApi/Startup.cs index 0fb3ba87..f04a53d1 100644 --- a/AsbCloudWebApi/Startup.cs +++ b/AsbCloudWebApi/Startup.cs @@ -45,6 +45,8 @@ namespace AsbCloudWebApi services.AddNotificationTransportServices(); + services.AddIntegrationEvents(); + services.AddJWTAuthentication(); services.AddSignalR() @@ -151,6 +153,7 @@ namespace AsbCloudWebApi app.UseEndpoints(endpoints => { endpoints.MapControllers(); + endpoints.MapHub("/hubs/wellInfo"); endpoints.MapHub("/hubs/notifications"); endpoints.MapHub("/hubs/telemetry"); endpoints.MapHub("/hubs/reports"); diff --git a/SignalRTestClient/Program.cs b/SignalRTestClient/Program.cs index 487f1253..9ae72e04 100644 --- a/SignalRTestClient/Program.cs +++ b/SignalRTestClient/Program.cs @@ -10,7 +10,7 @@ internal class Program { var connectionBuilder = new HubConnectionBuilder(); var connection = connectionBuilder - .WithUrl("http://localhost:5000/hubs/notifications", connectionOptions => { + .WithUrl("http://localhost:5000/hubs/limitingParameters", connectionOptions => { connectionOptions.AccessTokenProvider = AccessTokenProvider; }) .WithAutomaticReconnect() @@ -26,9 +26,9 @@ internal class Program connection.StartAsync().Wait(); //Console.WriteLine("OnConnected"); - //connection.SendCoreAsync("OnConnected", new object[] { }, CancellationToken.None).Wait(); + connection.SendCoreAsync("OnConnectedAsync", new object[] { 1 }, CancellationToken.None).Wait(); - var subsction = connection.On("receiveNotifications", (str1) => { + var subsction = connection.On("well_info_update", (str1) => { Console.WriteLine(str1); } );