diff --git a/AsbCloudApp/Data/GTR/GtrWitsDto.cs b/AsbCloudApp/Data/GTR/GtrWitsDto.cs new file mode 100644 index 00000000..a8ecb939 --- /dev/null +++ b/AsbCloudApp/Data/GTR/GtrWitsDto.cs @@ -0,0 +1,214 @@ +namespace AsbCloudApp.Data.GTR; + +public class GtrWitsDto +{ + /// + /// Вес на крюке + /// + public float? HKLA { get; set; } + + /// + /// Высота крюка + /// + public float? BLKPOS { get; set; } + + /// + /// Нагрузка на долото + /// + public float? WOBA { get; set; } + + /// + /// Момент на роторе/ВСП + /// + public float? TORQA { get; set; } + + /// + /// Давление на входе (на стояке) + /// + public float? SPPA { get; set; } + + /// + /// Обороты ротора/ВСП + /// + public float? RPMA { get; set; } + + /// + /// Механическая скорость + /// + public float? ROPA { get; set; } + + /// + /// Скорость инструмента вверх + /// + public float? RSUX { get; set; } + + /// + /// Скорость инструмента вниз + /// + public float? RSDX { get; set; } + + /// + /// Расход на входе + /// + public float? MFIA { get; set; } + + /// + /// Расход на выходе + /// + public float? MFOA { get; set; } + + /// + /// Температура на входе + /// + public float? MTIA { get; set; } + + /// + /// Температура на выходе + /// + public float? MTOA { get; set; } + + /// + /// Ходы насоса №1 + /// + public float? SPM1 { get; set; } + + /// + /// Ходы насоса №2 + /// + public float? SPM2 { get; set; } + + /// + /// Ходы насоса №3 + /// + public float? SPM3 { get; set; } + + /// + /// Общий объем бурового раствора на поверхности + /// + public float? TVOLACT { get; set; } + + /// + /// Объем бурового раствора в доливной емкости №1 + /// + public float? TTVOL1 { get; set; } + + /// + /// Объем бурового раствора в доливной емкости №2 + /// + public float? TTVOL2 { get; set; } + + /// + /// Объем бурового раствора в емкости №1 + /// + public float? TVOL01 { get; set; } + + /// + /// Объем бурового раствора в емкости №2 + /// + public float? TVOL02 { get; set; } + + /// + /// Объем бурового раствора в емкости №3 + /// + public float? TVOL03 { get; set; } + + /// + /// Объем бурового раствора в емкости №4 + /// + public float? TVOL04 { get; set; } + + /// + /// Объем бурового раствора в емкости №5 + /// + public float? TVOL05 { get; set; } + + /// + /// Объем бурового раствора в емкости №6 + /// + public float? TVOL06 { get; set; } + + /// + /// Объем бурового раствора в емкости №7 + /// + public float? TVOL07 { get; set; } + + /// + /// Объем бурового раствора в емкости №8 + /// + public float? TVOL08 { get; set; } + + /// + /// Объем бурового раствора в емкости №9 + /// + public float? TVOL09 { get; set; } + + /// + /// Объем бурового раствора в емкости №10 + /// + public float? TVOL10 { get; set; } + + /// + /// Объем бурового раствора в емкости №11 + /// + public float? TVOL11 { get; set; } + + /// + /// Объем бурового раствора в емкости №12 + /// + public float? TVOL12 { get; set; } + + /// + /// Объем бурового раствора в емкости №13 + /// + public float? TVOL13 { get; set; } + + /// + /// Объем бурового раствора в емкости №14 + /// + public float? TVOL14 { get; set; } + + /// + /// Плотность (удельный вес) бурового раствора на выходе + /// + public float? MDOA { get; set; } + + /// + /// Плотность (удельный вес) бурового раствора на входе + /// + public float? MDIA { get; set; } + + /// + /// Процентное содержание метана + /// + public float? METHANE { get; set; } + + /// + /// Процентное содержание этана + /// + public float? ETHANE { get; set; } + + /// + /// Процентное содержание пропана + /// + public float? PROPANE { get; set; } + + /// + /// Процентное содержание бутана + /// + public float? IBUTANE { get; set; } + + /// + /// Процентное содержание пентана + /// + public float? NBUTANE { get; set; } + + /// + /// Процентное содержание углеводородов + /// + public float? HydrocarbonPercentage => METHANE + ETHANE + PROPANE + IBUTANE + NBUTANE; + + /// + /// Процентное содержание газов + /// + public float? GASA { get; set; } +} \ No newline at end of file diff --git a/AsbCloudApp/Repositories/IGtrRepository.cs b/AsbCloudApp/Repositories/IGtrRepository.cs index 0d597dde..1aadfb96 100644 --- a/AsbCloudApp/Repositories/IGtrRepository.cs +++ b/AsbCloudApp/Repositories/IGtrRepository.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using AsbCloudApp.Requests; namespace AsbCloudApp.Repositories { @@ -19,6 +20,14 @@ namespace AsbCloudApp.Repositories /// /// Task SaveDataAsync(int idTelemetry, IEnumerable dtos, CancellationToken token); + + /// + /// получить данные ГТИ + /// + /// + /// + /// + Task> GetAsync(GtrRequest request, CancellationToken token); /// /// получить данные для клиента @@ -29,6 +38,7 @@ namespace AsbCloudApp.Repositories /// кол-во элементов до которых эти данные прореживаются /// /// + [Obsolete] Task> GetAsync(int idWell, DateTime? dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); @@ -39,6 +49,7 @@ namespace AsbCloudApp.Repositories /// /// /// + [Obsolete] IEnumerable GetLastDataByRecordId(int idWell, int idRecord); /// @@ -46,6 +57,7 @@ namespace AsbCloudApp.Repositories /// /// /// + [Obsolete] IEnumerable GetLastData(int idWell); } } diff --git a/AsbCloudApp/Requests/GtrWithGetDataRequest.cs b/AsbCloudApp/Requests/GtrRequest.cs similarity index 63% rename from AsbCloudApp/Requests/GtrWithGetDataRequest.cs rename to AsbCloudApp/Requests/GtrRequest.cs index 8e93a02c..8ba678b2 100644 --- a/AsbCloudApp/Requests/GtrWithGetDataRequest.cs +++ b/AsbCloudApp/Requests/GtrRequest.cs @@ -5,7 +5,7 @@ namespace AsbCloudApp.Requests; /// /// Параметры запроса для получения загруженных данных ГТИ по скважине /// -public class GtrWithGetDataRequest +public class GtrRequestBase { /// /// Дата начала выборки.По умолчанию: текущее время - IntervalSec @@ -20,5 +20,22 @@ public class GtrWithGetDataRequest /// /// Желаемое количество точек. Если в выборке точек будет больше, то выборка будет прорежена. /// + [Obsolete] public int ApproxPointsCount { get; set; } = 1024; +} + +/// +/// Параметры запроса для получения загруженных данных ГТИ по скважине +/// +public class GtrRequest : GtrRequestBase +{ + public GtrRequest(int idWell, GtrRequestBase request) + { + IdWell = idWell; + Begin = request.Begin; + IntervalSec = request.IntervalSec; + ApproxPointsCount = request.ApproxPointsCount; + } + + public int IdWell { get; set; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs b/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs index 2a871553..5d0cda7b 100644 --- a/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs +++ b/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs @@ -12,11 +12,58 @@ using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; +using AsbCloudApp.Requests; +using Mapster; namespace AsbCloudInfrastructure.Repository { public class GtrWitsRepository : IGtrRepository { + private static IDictionary<(int, int), string> WitsParameters = new Dictionary<(int, int), string> + { + { (1, 14), nameof(GtrWitsDto.HKLA) }, + { (1, 12), nameof(GtrWitsDto.BLKPOS) }, + { (1, 16), nameof(GtrWitsDto.WOBA) }, + { (1, 18), nameof(GtrWitsDto.TORQA) }, + { (1, 21), nameof(GtrWitsDto.SPPA) }, + { (2, 15), nameof(GtrWitsDto.RPMA) }, + { (1, 13), nameof(GtrWitsDto.ROPA) }, + { (3, 16), nameof(GtrWitsDto.RSUX) }, + { (3, 17), nameof(GtrWitsDto.RSDX) }, + { (1, 30), nameof(GtrWitsDto.MFIA) }, + { (1, 29), nameof(GtrWitsDto.MFOA)}, + { (1, 34), nameof(GtrWitsDto.MTIA) }, + { (1, 33), nameof(GtrWitsDto.MTOA) }, + { (1, 23), nameof(GtrWitsDto.SPM1) }, + { (1, 24), nameof(GtrWitsDto.SPM2) }, + { (1, 25), nameof(GtrWitsDto.SPM3) }, + { (1, 26), nameof(GtrWitsDto.TVOLACT) }, + { (11, 29), nameof(GtrWitsDto.TTVOL1) }, + { (11, 30), nameof(GtrWitsDto.TTVOL2) }, + { (11, 15), nameof(GtrWitsDto.TVOL01) }, + { (11, 16), nameof(GtrWitsDto.TVOL02) }, + { (11, 17), nameof(GtrWitsDto.TVOL03) }, + { (11, 18), nameof(GtrWitsDto.TVOL04) }, + { (11, 19), nameof(GtrWitsDto.TVOL05) }, + { (11, 20), nameof(GtrWitsDto.TVOL06) }, + { (11, 21), nameof(GtrWitsDto.TVOL07) }, + { (11, 22), nameof(GtrWitsDto.TVOL08) }, + { (11, 23), nameof(GtrWitsDto.TVOL09) }, + { (11, 24), nameof(GtrWitsDto.TVOL10) }, + { (11, 25), nameof(GtrWitsDto.TVOL11) }, + { (11, 26), nameof(GtrWitsDto.TVOL12) }, + { (11, 27), nameof(GtrWitsDto.TVOL13) }, + { (11, 28), nameof(GtrWitsDto.TVOL14) }, + { (1, 31), nameof(GtrWitsDto.MDOA) }, + { (1, 32), nameof(GtrWitsDto.MDIA) }, + { (12, 12), nameof(GtrWitsDto.METHANE) }, + { (12, 13), nameof(GtrWitsDto.ETHANE) }, + { (12, 14), nameof(GtrWitsDto.PROPANE) }, + { (12, 15), nameof(GtrWitsDto.IBUTANE) }, + { (12, 16), nameof(GtrWitsDto.NBUTANE) }, + { (1, 40), nameof(GtrWitsDto.GASA) }, + }; + private readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; private static ConcurrentDictionary> cache = new(); @@ -28,6 +75,48 @@ namespace AsbCloudInfrastructure.Repository this.db = db; this.telemetryService = telemetryService; } + + public async Task> GetAsync(GtrRequest request, CancellationToken token) + { + var interval = TimeSpan.FromSeconds(10); + var query = BuildQuery(request); + var entities = await query.AsNoTracking().ToArrayAsync(token); + + var dtos = entities + .GroupBy(e => e.DateTime.Ticks / interval.Ticks) + .Select(Convert); + + return dtos; + } + + private static GtrWitsDto Convert(IEnumerable scr) + { + var values = scr.GroupBy(e => (e.IdRecord, e.IdItem)) + .ToDictionary(g => WitsParameters[g.Key], g => g.LastOrDefault()?.Value); + + var dto = values.Adapt(); + + return dto; + } + + private IQueryable BuildQuery(GtrRequest request) + where TEntity : WitsItemBase + where TType : notnull + { + var query = db.Set().OrderBy(e => e.DateTime) + .Where(e => e.Telemetry != null && + e.Telemetry.Well != null && + e.Telemetry.Well.Id == request.IdWell) + .Where(e => e.DateTime >= DateTime.UtcNow.AddSeconds(-request.IntervalSec)); + + if (request.Begin.HasValue) + { + var dateBeginUtc = request.Begin.Value.ToUniversalTime(); + query = query.Where(e => e.DateTime >= dateBeginUtc); + } + + return query; + } public async Task> GetAsync(int idWell, DateTime? dateBegin, double intervalSec = 600, int approxPointsCount = 1024, CancellationToken token = default) { diff --git a/AsbCloudWebApi.IntegrationTests/Clients/IGtrWitsClient.cs b/AsbCloudWebApi.IntegrationTests/Clients/IGtrWitsClient.cs new file mode 100644 index 00000000..17f76a33 --- /dev/null +++ b/AsbCloudWebApi.IntegrationTests/Clients/IGtrWitsClient.cs @@ -0,0 +1,13 @@ +using AsbCloudApp.Data.GTR; +using AsbCloudApp.Requests; +using Refit; + +namespace AsbCloudWebApi.IntegrationTests.Clients; + +public interface IGtrWitsClient +{ + private const string BaseRoute = "/api/well/{idWell}/gtrWits"; + + [Get(BaseRoute)] + Task>> GetAllAsync(int idWell, [Query] GtrRequestBase request); +} \ No newline at end of file diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/GtrControllerTests.cs b/AsbCloudWebApi.IntegrationTests/Controllers/GtrControllerTests.cs new file mode 100644 index 00000000..eb1e96d8 --- /dev/null +++ b/AsbCloudWebApi.IntegrationTests/Controllers/GtrControllerTests.cs @@ -0,0 +1,68 @@ +using System.Net; +using AsbCloudApp.Requests; +using AsbCloudDb.Model.GTR; +using AsbCloudWebApi.IntegrationTests.Clients; +using Xunit; + +namespace AsbCloudWebApi.IntegrationTests.Controllers; + +public class GtrControllerTests : BaseIntegrationTest +{ + private readonly IGtrWitsClient client; + + public GtrControllerTests(WebAppFactoryFixture factory) + : base(factory) + { + client = factory.GetAuthorizedHttpClient(string.Empty); + } + + [Fact] + public async Task GetAll_returns_success() + { + //arrange + var well = dbContext.Wells.First(); + + var witsItems = CreateWitsItems(well.IdTelemetry!.Value); + dbContext.WitsItemFloat.AddRange(witsItems); + await dbContext.SaveChangesAsync(); + + var request = new GtrRequestBase(); + + //act + var response = await client.GetAllAsync(well.Id, request); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.True(response.Content.Any()); + } + + private static IEnumerable CreateWitsItems(int idTelemetry) => + new[] + { + new WitsItemFloat + { + IdRecord = 1, + IdItem = 14, + Value = 4, + IdTelemetry = idTelemetry, + DateTime = DateTimeOffset.UtcNow + }, + new WitsItemFloat + { + IdRecord = 1, + IdItem = 14, + Value = 5, + IdTelemetry = idTelemetry, + DateTime = DateTimeOffset.UtcNow + }, + new WitsItemFloat + { + IdRecord = 1, + IdItem = 12, + Value = 5, + IdTelemetry = idTelemetry, + DateTime = DateTimeOffset.UtcNow.AddSeconds(10) + } + }; +} \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/GtrWitsController.cs b/AsbCloudWebApi/Controllers/GtrWitsController.cs new file mode 100644 index 00000000..69d8a7d2 --- /dev/null +++ b/AsbCloudWebApi/Controllers/GtrWitsController.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using AsbCloudApp.Data.GTR; +using AsbCloudApp.Exceptions; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using AsbCloudApp.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace AsbCloudWebApi.Controllers; + +[ApiController] +[Route("api/well/{idWell}/[controller]")] +[Authorize] +public class GtrWitsController : ControllerBase +{ + private readonly IWellService wellService; + private readonly IGtrRepository gtrRepository; + + public GtrWitsController(IWellService wellService, + IGtrRepository gtrRepository) + { + this.wellService = wellService; + this.gtrRepository = gtrRepository; + } + + /// + /// Получить значение от ГТИ + /// + /// + /// + /// + /// + [HttpGet] + [Permission] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetAllAsync(int idWell, [FromQuery] GtrRequestBase request, CancellationToken token) + { + await AssertUserHasAccessToWellAsync(idWell, token); + + var requestToRepository = new GtrRequest(idWell, request); + + var dtos = await gtrRepository.GetAsync(requestToRepository, token); + + return Ok(dtos); + } + + private async Task AssertUserHasAccessToWellAsync(int idWell, CancellationToken token) + { + var idUser = User.GetUserId(); + var idCompany = User.GetCompanyId(); + + if (!idUser.HasValue) + throw new ForbidException("Нет доступа к скважине"); + + if (!idCompany.HasValue) + throw new ForbidException("Нет доступа к скважине"); + + if (!await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, token)) + throw new ForbidException("Нет доступа к скважине"); + } +} \ No newline at end of file diff --git a/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs b/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs index f4cfe0b4..95662fe2 100644 --- a/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs @@ -46,7 +46,7 @@ namespace AsbCloudWebApi.Controllers.SAUB /// [HttpGet("{idWell}")] public async Task>> GetDataAsync([Required] int idWell, - [FromQuery] GtrWithGetDataRequest request, + [FromQuery] GtrRequest request, CancellationToken token) { int? idCompany = User.GetCompanyId();