diff --git a/AsbCloudApp/Data/ProcessMap/ProcessMapPlanDto.cs b/AsbCloudApp/Data/ProcessMap/ProcessMapPlanDto.cs
index 8849e13d..0a4c1044 100644
--- a/AsbCloudApp/Data/ProcessMap/ProcessMapPlanDto.cs
+++ b/AsbCloudApp/Data/ProcessMap/ProcessMapPlanDto.cs
@@ -87,10 +87,5 @@ namespace AsbCloudApp.Data.ProcessMap
/// Плановый процент использования spin master
///
public double UsageSpin { get; set; }
-
- ///
- /// DTO типа секции
- ///
- public WellSectionTypeDto WellSectionType { get; set; } = null!;
}
}
diff --git a/AsbCloudApp/Data/WellDto.cs b/AsbCloudApp/Data/WellDto.cs
index 4ffd262e..b347bdd8 100644
--- a/AsbCloudApp/Data/WellDto.cs
+++ b/AsbCloudApp/Data/WellDto.cs
@@ -5,6 +5,23 @@ using System.Linq;
namespace AsbCloudApp.Data
{
+ ///
+ /// базовая информация о скважине
+ ///
+ public class WellWithTimezoneDto : WellInfoDto
+ {
+ ///
+ [Required]
+ public SimpleTimezoneDto Timezone { get; set; } = null!;
+
+ ///
+ /// 0 - неизвестно,
+ /// 1 - в работе,
+ /// 2 - завершена
+ ///
+ public int IdState { get; set; }
+ }
+
///
/// Скважина
///
diff --git a/AsbCloudApp/Data/WellboreDto.cs b/AsbCloudApp/Data/WellboreDto.cs
new file mode 100644
index 00000000..dbd9b697
--- /dev/null
+++ b/AsbCloudApp/Data/WellboreDto.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace AsbCloudApp.Data;
+
+///
+/// Ствол скважины
+///
+public class WellboreDto
+{
+ public WellWithTimezoneDto Well { get; set; }
+
+ ///
+ /// Идентификатор
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// Название
+ ///
+ public string Name { get; set; } = null!;
+
+ ///
+ /// Начальная глубина ствола
+ ///
+ public double DepthStart { get; set; }
+
+ ///
+ /// Конечная глубина скважины
+ ///
+ public double DepthEnd { get; set; }
+
+ ///
+ /// Дата начала первой операции
+ ///
+ public DateTimeOffset DateStart { get; set; }
+
+ ///
+ /// Дата завершения последней операции
+ ///
+ public DateTimeOffset DateEnd { get; set; }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Requests/WellboreRequest.cs b/AsbCloudApp/Requests/WellboreRequest.cs
new file mode 100644
index 00000000..32b4cbe2
--- /dev/null
+++ b/AsbCloudApp/Requests/WellboreRequest.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+
+namespace AsbCloudApp.Requests;
+
+///
+/// Параметры запроса для ствола скважины
+///
+public class WellboreRequest : RequestBase
+{
+ ///
+ /// Пары идентификаторов скважины и секции
+ ///
+ public IEnumerable<(int idWell, int? idSection)> Ids { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs b/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs
index 16cdaed6..13bdb1ea 100644
--- a/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs
+++ b/AsbCloudApp/Services/AutoGeneratedDailyReports/IAutoGeneratedDailyReportService.cs
@@ -31,6 +31,14 @@ public interface IAutoGeneratedDailyReportService
///
///
///
- Task<(string fileName, Stream stream)> GenerateReportAsync(int idWell, DateOnly reportDate,
+ Task<(string fileName, Stream stream)> GenerateAsync(int idWell, DateOnly reportDate,
CancellationToken cancellationToken);
+
+ ///
+ /// Получение диапазона дат
+ ///
+ ///
+ ///
+ ///
+ Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken);
}
\ No newline at end of file
diff --git a/AsbCloudApp/Services/ITelemetryDataService.cs b/AsbCloudApp/Services/ITelemetryDataService.cs
index 20ac7f90..c1704509 100644
--- a/AsbCloudApp/Services/ITelemetryDataService.cs
+++ b/AsbCloudApp/Services/ITelemetryDataService.cs
@@ -26,6 +26,16 @@ namespace AsbCloudApp.Services
DateTime dateBegin = default, double intervalSec = 600d,
int approxPointsCount = 1024, CancellationToken token = default);
+ ///
+ /// Получение статистики за период
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task GetRangeAsync(int idWell, DateTimeOffset start, DateTimeOffset end, CancellationToken token);
+
///
/// добавить/изменить данные тех. процесса (используется панелью)
///
diff --git a/AsbCloudApp/Services/IWellboreService.cs b/AsbCloudApp/Services/IWellboreService.cs
new file mode 100644
index 00000000..f42485ae
--- /dev/null
+++ b/AsbCloudApp/Services/IWellboreService.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data;
+using AsbCloudApp.Requests;
+
+namespace AsbCloudApp.Services;
+
+///
+/// Сервис для ствола скважины
+///
+public interface IWellboreService
+{
+ ///
+ /// Получение ствола скважины
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task GetWellboreAsync(int idWell, int idSection, CancellationToken cancellationToken);
+
+ ///
+ /// Получение стволов скважин
+ ///
+ ///
+ ///
+ ///
+ Task> GetWellboresAsync(WellboreRequest request, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs
index 6d2a43be..6b72eea2 100644
--- a/AsbCloudInfrastructure/DependencyInjection.cs
+++ b/AsbCloudInfrastructure/DependencyInjection.cs
@@ -227,6 +227,8 @@ namespace AsbCloudInfrastructure
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
+
return services;
}
diff --git a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs
index 514b0b66..0e364319 100644
--- a/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs
+++ b/AsbCloudInfrastructure/Services/AutoGeneratedDailyReports/AutoGeneratedDailyReportService.cs
@@ -6,7 +6,6 @@ using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.AutogeneratedDailyReport;
-using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
@@ -15,7 +14,6 @@ using AsbCloudApp.Services;
using AsbCloudApp.Services.AutoGeneratedDailyReports;
using AsbCloudApp.Services.Subsystems;
using AsbCloudDb.Model;
-using AsbCloudInfrastructure.Services.SAUB;
namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports;
@@ -25,7 +23,6 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService
private readonly IWellService wellService;
private readonly IWellOperationRepository wellOperationRepository;
- private readonly TelemetryDataCache telemetryDataCache;
private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
private readonly ICrudRepository subsystemRepository;
private readonly ILimitingParameterService limitingParameterService;
@@ -33,7 +30,6 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService
public AutoGeneratedDailyReportService(IWellService wellService,
IWellOperationRepository wellOperationRepository,
- TelemetryDataCache telemetryDataCache,
ISubsystemOperationTimeService subsystemOperationTimeService,
ICrudRepository subsystemRepository,
ILimitingParameterService limitingParameterService,
@@ -41,7 +37,6 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService
{
this.wellOperationRepository = wellOperationRepository;
this.wellService = wellService;
- this.telemetryDataCache = telemetryDataCache;
this.subsystemOperationTimeService = subsystemOperationTimeService;
this.subsystemRepository = subsystemRepository;
this.limitingParameterService = limitingParameterService;
@@ -67,12 +62,10 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService
if (!well.IdTelemetry.HasValue)
throw new ArgumentInvalidException("Телеметрия для скважины отсутствует", nameof(idWell));
- var datesRange = telemetryDataCache.GetOrDefaultDataDateRange(well.IdTelemetry.Value);
-
- if (datesRange is null)
+ var datesRange = await GetDatesRangeAsync(idWell, cancellationToken);
+
+ if (datesRange is null)
return result;
-
- result.Count = (int)(Math.Ceiling((datesRange.To - DateTime.UnixEpoch).TotalDays) - Math.Floor((datesRange.From - DateTime.UnixEpoch).TotalDays));
if (request.StartDate.HasValue)
{
@@ -92,6 +85,9 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService
datesRange.To = finishDate;
}
+ if (datesRange.From.AddDays(result.Skip) <= datesRange.To)
+ result.Count = (int)(Math.Ceiling((datesRange.To - DateTime.UnixEpoch).TotalDays) - Math.Floor((datesRange.From - DateTime.UnixEpoch).TotalDays));
+
for (int day = result.Skip; (day - result.Skip) < result.Take && (datesRange.From.AddDays(day)) <= datesRange.To; day++)
{
var reportDate = DateOnly.FromDateTime(datesRange.From.AddDays(day));
@@ -109,7 +105,7 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService
return result;
}
- public async Task<(string fileName, Stream stream)> GenerateReportAsync(int idWell, DateOnly reportDate,
+ public async Task<(string fileName, Stream stream)> GenerateAsync(int idWell, DateOnly reportDate,
CancellationToken cancellationToken)
{
var startDate = new DateTime(reportDate.Year, reportDate.Month, reportDate.Day);
@@ -144,6 +140,21 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService
return (report.FileName, stream);
}
+ public async Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken)
+ {
+ var factOperations = await GetFactOperationsAsync(idWell, null, null,
+ cancellationToken);
+
+ if (!factOperations.Any())
+ return null;
+
+ return new DatesRangeDto
+ {
+ From = factOperations.Min(o => o.DateStart).Date,
+ To = factOperations.Max(o => o.DateStart).Date
+ };
+ }
+
private HeadBlockDto CreateHeadBlock(WellDto well, IEnumerable factOperations)
{
var customer = well.Companies.FirstOrDefault(company => company.IdCompanyType == 1);
@@ -199,8 +210,8 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService
});
}
- private Task> GetFactOperationsAsync(int idWell, DateTime startDate,
- DateTime finishDate, CancellationToken cancellationToken)
+ private async Task> GetFactOperationsAsync(int idWell, DateTime? startDate,
+ DateTime? finishDate, CancellationToken cancellationToken)
{
var request = new WellOperationRequest
{
@@ -211,7 +222,8 @@ public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService
SortFields = new[] { "DateStart asc" },
};
- return wellOperationRepository.GetAsync(request, cancellationToken);
+ return (await wellOperationRepository.GetAsync(request, cancellationToken))
+ .OrderBy(w => w.DateStart);
}
private Task?> GetSubsystemStatsAsync(int idWell, DateTime startDate,
diff --git a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs
index dc5d87b3..21f118a8 100644
--- a/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs
+++ b/AsbCloudInfrastructure/Services/ProcessMap/ProcessMapPlanImportService.cs
@@ -75,6 +75,8 @@ public class ProcessMapPlanImportService : IProcessMapPlanImportService
public async Task ExportAsync(int idWell, CancellationToken cancellationToken)
{
+ sections = (await wellSectionTypeRepository.GetAllAsync(cancellationToken)).ToArray();
+
var processMapPlans = (await processMapPlanRepository.GetByIdWellAsync(idWell,
cancellationToken)).ToArray();
@@ -120,7 +122,7 @@ public class ProcessMapPlanImportService : IProcessMapPlanImportService
private void AddToRow(IXLRow row, ProcessMapPlanDto processMap)
{
- row.Cell(columnWellSectionType).Value = processMap.WellSectionType.Caption;
+ row.Cell(columnWellSectionType).Value = sections.First(x => x.Id == processMap.IdWellSectionType).Caption;
row.Cell(columnMode).Value = GetModeCaption(processMap.IdMode);
row.Cell(columnDepthStart).Value = processMap.DepthStart;
row.Cell(columnDepthEnd).Value = processMap.DepthEnd;
diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs
index ffd6416c..8d76ee70 100644
--- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs
+++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs
@@ -1,4 +1,5 @@
-using AsbCloudApp.Services;
+using AsbCloudApp.Data;
+using AsbCloudApp.Services;
using AsbCloudDb;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
@@ -13,7 +14,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
{
public abstract class TelemetryDataBaseService : ITelemetryDataService
where TDto : AsbCloudApp.Data.ITelemetryData
- where TEntity : class, ITelemetryData
+ where TEntity : class, AsbCloudDb.Model.ITelemetryData
{
protected readonly IAsbCloudDbContext db;
protected readonly ITelemetryService telemetryService;
@@ -146,6 +147,44 @@ namespace AsbCloudInfrastructure.Services.SAUB
return dtos;
}
+ ///
+ public virtual async Task GetRangeAsync(
+ int idWell,
+ DateTimeOffset start,
+ DateTimeOffset end,
+ CancellationToken token)
+ {
+ var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell);
+ if (telemetry is null)
+ return default;
+
+ var timezone = telemetryService.GetTimezone(telemetry.Id);
+ var startUtc = start.ToOffset(TimeSpan.Zero);
+ var endUtc = end.ToOffset(TimeSpan.Zero);
+
+ var dbSet = db.Set();
+ var query = dbSet
+ .Where(i => i.IdTelemetry == telemetry.Id)
+ .Where(i => i.DateTime >= startUtc)
+ .Where(i => i.DateTime <= endUtc)
+ .GroupBy(i => i.IdTelemetry)
+ .Select(g => new
+ {
+ DateStart = g.Min(i => i.DateTime),
+ DateEnd = g.Max(i => i.DateTime),
+ });
+
+ var data = await query.FirstOrDefaultAsync(token);
+ if (data is null)
+ return default;
+
+ return new DatesRangeDto
+ {
+ From = data.DateStart.ToRemoteDateTime(timezone.Hours),
+ To = data.DateEnd.ToRemoteDateTime(timezone.Hours),
+ };
+ }
+
public abstract TDto Convert(TEntity src, double timezoneOffset);
public abstract TEntity Convert(TDto src, double timezoneOffset);
diff --git a/AsbCloudInfrastructure/Services/WellContactService.cs b/AsbCloudInfrastructure/Services/WellContactService.cs
index a8e280da..261ed9f9 100644
--- a/AsbCloudInfrastructure/Services/WellContactService.cs
+++ b/AsbCloudInfrastructure/Services/WellContactService.cs
@@ -29,18 +29,21 @@ namespace AsbCloudInfrastructure.Services
{
Caption = c.Caption,
Id = c.Id,
- Users = c.Users.Select(u => new UserContactDto()
- {
- Id = u.Id,
- Name = u.Name,
- Patronymic = u.Patronymic,
- Surname = u.Surname,
- Company = u.Company.Adapt(),
- Email = u.Email,
- Phone = u.Phone,
- Position = u.Position,
- IsContact = u.RelationContactsWells.Any(rel => rel.IdWell == wellId)
- })
+ Users = c.Users
+ .Where(u => u.IdState == 1)
+ .OrderBy(u => u.Surname)
+ .Select(u => new UserContactDto()
+ {
+ Id = u.Id,
+ Name = u.Name,
+ Patronymic = u.Patronymic,
+ Surname = u.Surname,
+ Company = u.Company.Adapt(),
+ Email = u.Email,
+ Phone = u.Phone,
+ Position = u.Position,
+ IsContact = u.RelationContactsWells.Any(rel => rel.IdWell == wellId)
+ })
});
var entities = await query.AsNoTracking()
diff --git a/AsbCloudInfrastructure/Services/WellboreService.cs b/AsbCloudInfrastructure/Services/WellboreService.cs
new file mode 100644
index 00000000..79ef8a3c
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/WellboreService.cs
@@ -0,0 +1,91 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data;
+using AsbCloudApp.Repositories;
+using AsbCloudApp.Requests;
+using AsbCloudApp.Services;
+using AsbCloudDb.Model;
+using Mapster;
+
+namespace AsbCloudInfrastructure.Services;
+
+public class WellboreService : IWellboreService
+{
+ private readonly IWellService wellService;
+ private readonly IWellOperationRepository wellOperationRepository;
+
+ public WellboreService(IWellService wellService, IWellOperationRepository wellOperationRepository)
+ {
+ this.wellService = wellService;
+ this.wellOperationRepository = wellOperationRepository;
+ }
+
+ public async Task GetWellboreAsync(int idWell, int idSection, CancellationToken cancellationToken)
+ {
+ var request = new WellboreRequest
+ {
+ Ids = new (int, int?)[] { (idWell, idSection) },
+ Take = 1,
+ };
+ var data = await GetWellboresAsync(request, cancellationToken);
+ return data.FirstOrDefault();
+ }
+
+ public async Task> GetWellboresAsync(WellboreRequest request,
+ CancellationToken cancellationToken)
+ {
+ var wellbores = new List(request.Ids.Count());
+ var skip = request.Skip ?? 0;
+ var take = request.Take ?? 10;
+
+ var sections = wellOperationRepository.GetSectionTypes()
+ .ToDictionary(w => w.Id, w => w);
+
+ var ids = request.Ids.GroupBy(i => i.idWell);
+
+ foreach (var id in ids)
+ {
+ var well = await wellService.GetOrDefaultAsync(id.Key, cancellationToken);
+
+ if (well is null)
+ continue;
+
+ var wellOperations = await GetFactOperationsAsync(well.Id, id.Select(i => i.idSection), cancellationToken);
+ var groupedOperations = wellOperations.GroupBy(o => o.IdWellSectionType);
+ var wellWellbores = groupedOperations.Select(group => new WellboreDto {
+ Id = group.Key,
+ Name = sections[group.Key].Caption,
+ Well = well.Adapt(),
+ DateStart = group.Min(operation => operation.DateStart),
+ DateEnd = group.Max(operation => operation.DateStart.AddHours(operation.DurationHours)),
+ DepthStart = group.Min(operation => operation.DepthStart),
+ DepthEnd = group.Max(operation => operation.DepthEnd),
+ });
+ wellbores.AddRange(wellWellbores);
+ }
+
+ return wellbores
+ .OrderBy(w => w.Well.Id).ThenBy(w => w.Id)
+ .Skip(skip).Take(take);
+ }
+
+ private async Task> GetFactOperationsAsync(int idWell, IEnumerable idsSections,
+ CancellationToken cancellationToken)
+ {
+ var request = new WellOperationRequest
+ {
+ IdWell = idWell,
+ OperationType = WellOperation.IdOperationTypeFact,
+ SortFields = new[] { "DateStart asc" },
+ };
+
+ request.SectionTypeIds = idsSections.All(i => i.HasValue)
+ ? idsSections.Select(i => i!.Value)
+ : null;
+
+ return (await wellOperationRepository.GetAsync(request, cancellationToken))
+ .OrderBy(o => o.DateStart);
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs
index f38a3ea8..de26e843 100644
--- a/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs
+++ b/AsbCloudWebApi/Controllers/AutoGeneratedDailyReportController.cs
@@ -49,7 +49,7 @@ public class AutoGeneratedDailyReportController : ControllerBase
if (!await CanUserAccessToWellAsync(idWell, cancellationToken))
return Forbid();
- var reportFile = await autoGeneratedDailyReportService.GenerateReportAsync(idWell,
+ var reportFile = await autoGeneratedDailyReportService.GenerateAsync(idWell,
reportDate,
cancellationToken);
@@ -78,7 +78,23 @@ public class AutoGeneratedDailyReportController : ControllerBase
return Ok(reports);
}
-
+
+ ///
+ /// Диапазон дат для формирования суточных отчётов
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("datesRange")]
+ [ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)]
+ public async Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken)
+ {
+ if (!await CanUserAccessToWellAsync(idWell, cancellationToken))
+ return Forbid();
+
+ return Ok(await autoGeneratedDailyReportService.GetDatesRangeAsync(idWell, cancellationToken));
+ }
+
private async Task CanUserAccessToWellAsync(int idWell, CancellationToken cancellationToken)
{
int? idCompany = User.GetCompanyId();
diff --git a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs
index 05af2b9a..f80693a1 100644
--- a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs
+++ b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -95,6 +96,37 @@ namespace AsbCloudWebApi.Controllers.SAUB
return Ok(content);
}
+ ///
+ /// Возвращает диапазон дат за которые есть телеметрия за период времени
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{idWell}/dateRange")]
+ public virtual async Task> GetRangeAsync(
+ [FromRoute] int idWell,
+ [Required] DateTimeOffset start,
+ [Required] DateTimeOffset end,
+ CancellationToken token)
+ {
+ int? idCompany = User.GetCompanyId();
+
+ if (idCompany is null)
+ return Forbid();
+
+ bool isCompanyOwnsWell = await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
+ idWell, token).ConfigureAwait(false);
+
+ if (!isCompanyOwnsWell)
+ return Forbid();
+
+ var content = await telemetryDataService.GetRangeAsync(idWell, start, end, token);
+
+ return Ok(content);
+ }
+
///
/// Возвращает диапазон дат сохраненных данных.
///
diff --git a/AsbCloudWebApi/Controllers/WellboreController.cs b/AsbCloudWebApi/Controllers/WellboreController.cs
new file mode 100644
index 00000000..e9a590a0
--- /dev/null
+++ b/AsbCloudWebApi/Controllers/WellboreController.cs
@@ -0,0 +1,91 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data;
+using AsbCloudApp.Exceptions;
+using AsbCloudApp.Requests;
+using AsbCloudApp.Services;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace AsbCloudWebApi.Controllers;
+
+///
+/// Ствол скважины
+///
+[Authorize]
+[ApiController]
+[Route("api/[controller]")]
+public class WellboreController : ControllerBase
+{
+ private readonly IWellboreService wellboreService;
+
+ public WellboreController(IWellboreService wellboreService)
+ {
+ this.wellboreService = wellboreService;
+ }
+
+ ///
+ /// Получение ствола скважины
+ ///
+ /// Id скважины
+ /// Id типа секции скважины
+ ///
+ ///
+
+ [HttpGet("{idWell:int}/{idSection:int}")]
+ [ProducesResponseType(typeof(WellboreDto), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ public async Task GetAsync(int idWell, int idSection, CancellationToken cancellationToken)
+ {
+ var wellbore = await wellboreService.GetWellboreAsync(idWell, idSection, cancellationToken);
+
+ if (wellbore is null)
+ return NoContent();
+
+ return Ok(wellbore);
+ }
+
+ ///
+ /// Получение списка стволов скважин
+ ///
+ /// Пары идентификаторов скважины и секции
+ /// Опциональный параметр. Количество пропускаемых записей
+ /// Опциональный параметр. Количество получаемых записей
+ ///
+ ///
+ [HttpGet]
+ [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)]
+ public async Task GetAllAsync([FromQuery] IEnumerable ids,
+ int? skip,
+ int? take,
+ CancellationToken cancellationToken)
+ {
+ var request = new WellboreRequest
+ {
+ Ids = ids.Select(id => ParseId(id)),
+ Skip = skip,
+ Take = take
+ };
+
+ return Ok(await wellboreService.GetWellboresAsync(request, cancellationToken));
+ }
+
+ private static (int, int?) ParseId(string id)
+ {
+ var idPair = id.Split(',');
+ if (!int.TryParse(idPair[0], out var idWell))
+ throw new ArgumentInvalidException($"Не удалось получить Id скважины \"{idPair[0]}\"", nameof(id));
+
+ if (idPair.Length > 1)
+ {
+ if (int.TryParse(idPair[1], out int idWellSectionType))
+ return (idWell, idWellSectionType);
+ else
+ throw new ArgumentInvalidException($"Не удалось получить Id ствола \"{idPair[1]}\"", nameof(id));
+ }
+ return (idWell, null);
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudWebApi/Rest/wellbore.http b/AsbCloudWebApi/Rest/wellbore.http
new file mode 100644
index 00000000..9a7918ac
--- /dev/null
+++ b/AsbCloudWebApi/Rest/wellbore.http
@@ -0,0 +1,15 @@
+@baseUrl = http://127.0.0.1:5000
+@contentType = application/json
+@auth = Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkpXVCJ9.eyJpZCI6IjEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiZGV2IiwiaWRDb21wYW55IjoiMSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InJvb3QiLCJuYmYiOjE2NjI1NDgxNjIsImV4cCI6MTY5NDEwNTc2MiwiaXNzIjoiYSIsImF1ZCI6ImEifQ.OEAlNzxi7Jat6pzDBTAjTbChskc-tdJthJexyWwwUKE
+
+@uid = 20210101_000000000
+@idCluster = 1
+@idWell = 1
+
+# https://marketplace.visualstudio.com/items?itemName=humao.rest-client
+
+###
+GET {{baseUrl}}/api/well/wellbore?ids=1,2
+Content-Type: {{contentType}}
+accept: */*
+Authorization: {{auth}}