diff --git a/AsbCloudApp/Data/DrillerDto.cs b/AsbCloudApp/Data/DrillerDto.cs
index 1a11bd2a..01a4473d 100644
--- a/AsbCloudApp/Data/DrillerDto.cs
+++ b/AsbCloudApp/Data/DrillerDto.cs
@@ -29,4 +29,9 @@ public class DrillerDto : IId
/// Отчество
///
public string? Patronymic { get; set; }
+
+ ///
+ /// Полное имя
+ ///
+ public string FullName => $"{Surname} {Name} {Patronymic}";
}
diff --git a/AsbCloudApp/Data/WellReport/DrillerReportDto.cs b/AsbCloudApp/Data/WellReport/DrillerReportDto.cs
new file mode 100644
index 00000000..fed9a917
--- /dev/null
+++ b/AsbCloudApp/Data/WellReport/DrillerReportDto.cs
@@ -0,0 +1,21 @@
+using AsbCloudApp.Data.Subsystems;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AsbCloudApp.Data.WellReport;
+
+///
+/// Показатели бурильщиков
+///
+public class DrillerReportDto
+{
+ ///
+ /// Расписание
+ ///
+ public ScheduleDto Shedule { get; set; } = null!;
+
+ ///
+ /// Наработка подсистем
+ ///
+ public IEnumerable SubsystemsStat { get; set; } = Enumerable.Empty();
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Data/WellReport/DrillingBySetpointsDto.cs b/AsbCloudApp/Data/WellReport/DrillingBySetpointsDto.cs
new file mode 100644
index 00000000..a1c43036
--- /dev/null
+++ b/AsbCloudApp/Data/WellReport/DrillingBySetpointsDto.cs
@@ -0,0 +1,27 @@
+namespace AsbCloudApp.Data.WellReport;
+
+///
+/// Бурение по уставкам
+///
+public class DrillingBySetpointsDto
+{
+ ///
+ /// Метры пробуренные по уставке давления
+ ///
+ public double? MetersByPressure { get; set; }
+
+ ///
+ /// Метры пробуренные по уставке нагрузки
+ ///
+ public double? MetersByLoad { get; set; }
+
+ ///
+ /// Метры пробуренные по уставке момента
+ ///
+ public double? MetersByTorque { get; set; }
+
+ ///
+ /// Метры пробуренные по уставке скорости
+ ///
+ public double? MetersBySpeed { get; set; }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Data/WellReport/OperatingModeDto.cs b/AsbCloudApp/Data/WellReport/OperatingModeDto.cs
new file mode 100644
index 00000000..c355c623
--- /dev/null
+++ b/AsbCloudApp/Data/WellReport/OperatingModeDto.cs
@@ -0,0 +1,87 @@
+namespace AsbCloudApp.Data.WellReport;
+
+///
+/// Режим работы
+///
+public class OperatingModeDto
+{
+ ///
+ /// Интервал от
+ ///
+ public double DepthStart { get; set; }
+
+ ///
+ /// Интервал до
+ ///
+ public double DepthEnd { get; set; }
+
+ ///
+ /// Скорость проходки мин, м/ч
+ ///
+ public double? RopMin { get; set; }
+
+ ///
+ /// Скорость проходки максимум, м/ч
+ ///
+ public double? RopMax { get; set; }
+
+ ///
+ /// Скорость проходки среднее, м/ч
+ ///
+ public double? RopAvg { get; set; }
+
+ ///
+ /// Нагрузка на долото минимум, т
+ ///
+ public double? WeightOnBitMin { get; set; }
+
+ ///
+ /// Нагрузка на долото максимум, т
+ ///
+ public double? WeightOnBitMax { get; set; }
+
+ ///
+ /// Нагрузка на долото среднее, т
+ ///
+ public double? WeightOnBitAvg { get; set; }
+
+ ///
+ /// Момент минимум, кН*м
+ ///
+ public double? DriveTorqueMin { get; set; }
+
+ ///
+ /// Момент максимум, кН*м
+ ///
+ public double? DriveTorqueMax { get; set; }
+
+ ///
+ /// Момент среднее, кН*м
+ ///
+ public double? DriveTorqueAvg { get; set; }
+
+ ///
+ /// Перепад давления минимум, атм
+ ///
+ public double? DifferentialPressureMin { get; set; }
+
+ ///
+ /// Перепад давления максимум, атм
+ ///
+ public double? DifferentialPressureMax { get; set; }
+
+ ///
+ /// Перепад давления среднее, атм
+ ///
+ public double? DifferentialPressureAvg { get; set; }
+
+ ///
+ /// Q насосов минимум л/с
+ ///
+ public double? FrowRateMin { get; set; }
+
+ ///
+ /// Q насосов максимум л/с
+ ///
+ public double? FrowRateMax { get; set; }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Data/WellReport/SectionReportDto.cs b/AsbCloudApp/Data/WellReport/SectionReportDto.cs
new file mode 100644
index 00000000..0acf094d
--- /dev/null
+++ b/AsbCloudApp/Data/WellReport/SectionReportDto.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using AsbCloudApp.Data.Subsystems;
+
+namespace AsbCloudApp.Data.WellReport;
+
+///
+/// Показатели по секции
+///
+public class SectionReportDto
+{
+ ///
+ /// Идентификатор секции
+ ///
+ public int IdSection { get; set; }
+
+ ///
+ /// Наработка подсистем
+ ///
+ public IEnumerable SubsystemsStat { get; set; } = [];
+
+ ///
+ /// Режимы бурения
+ ///
+ public PlanFactDto OperatingMode { get; set; } = null!;
+
+ ///
+ /// Бурение по уставкам
+ ///
+ public DrillingBySetpointsDto? DrillingBySetpoints { get; set; }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Data/WellReport/WellReportDto.cs b/AsbCloudApp/Data/WellReport/WellReportDto.cs
new file mode 100644
index 00000000..ae8e1b9c
--- /dev/null
+++ b/AsbCloudApp/Data/WellReport/WellReportDto.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using AsbCloudApp.Data.User;
+
+namespace AsbCloudApp.Data.WellReport;
+
+///
+/// Отчёт по скважине
+///
+public class WellReportDto
+{
+ ///
+ /// Информация о скважине
+ ///
+ public WellDto Well { get; set; }
+
+ ///
+ /// Дата начала бурения
+ ///
+ public DateTimeOffset? DateFrom { get; set; }
+
+ ///
+ /// Дата окончания бурения
+ ///
+ public DateTimeOffset? DateTo { get; set; }
+
+ ///
+ /// Дни бурения
+ ///
+ public PlanFactDto Days { get; set; } = null!;
+
+ ///
+ /// Проектная глубина
+ ///
+ public PlanFactDto WellBoreDepth { get; set; } = null!;
+
+ ///
+ /// Вертикальная глубина
+ ///
+ public PlanFactDto VerticalDepth { get; set; } = null!;
+
+ ///
+ /// Дни бурения без НПВ
+ ///
+ public double WithoutNtpDays { get; set; }
+
+ ///
+ /// Контакты
+ ///
+ public IEnumerable Contacts { get; set; } = [];
+
+ ///
+ /// Показатели по секциям
+ ///
+ public IEnumerable SectionReports { get; set; } = [];
+
+ ///
+ /// Показатели по бурильщикам
+ ///
+ public IEnumerable DrillerReports { get; set; } = [];
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Repositories/IWellOperationRepository.cs b/AsbCloudApp/Repositories/IWellOperationRepository.cs
index f80ad02d..c18056e2 100644
--- a/AsbCloudApp/Repositories/IWellOperationRepository.cs
+++ b/AsbCloudApp/Repositories/IWellOperationRepository.cs
@@ -68,13 +68,13 @@ public interface IWellOperationRepository
///
(WellOperationBaseDto First, WellOperationBaseDto Last)? GetFirstAndLastFact(int idWell);
- ///
- /// Получить список операций по запросу
- ///
- ///
- ///
- ///
- Task> GetAll(WellOperationRequest request, CancellationToken token);
+ ///
+ /// Получить список операций по запросу
+ ///
+ ///
+ ///
+ ///
+ Task> GetAll(WellOperationRequest request, CancellationToken token);
///
/// Получить список операций по запросу
diff --git a/AsbCloudApp/Services/WellReport/IWellReportExportService.cs b/AsbCloudApp/Services/WellReport/IWellReportExportService.cs
new file mode 100644
index 00000000..a697e260
--- /dev/null
+++ b/AsbCloudApp/Services/WellReport/IWellReportExportService.cs
@@ -0,0 +1,19 @@
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace AsbCloudApp.Services.WellReport;
+
+///
+/// Сервис экспорта отчёта
+///
+public interface IWellReportExportService
+{
+ ///
+ /// Экспортировать
+ ///
+ ///
+ ///
+ ///
+ Task<(string Name, Stream File)?> ExportAsync(int idWell, CancellationToken token);
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Services/WellReport/IWellReportService.cs b/AsbCloudApp/Services/WellReport/IWellReportService.cs
new file mode 100644
index 00000000..9bf61049
--- /dev/null
+++ b/AsbCloudApp/Services/WellReport/IWellReportService.cs
@@ -0,0 +1,19 @@
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data.WellReport;
+
+namespace AsbCloudApp.Services.WellReport;
+
+///
+/// Сервис формирования отчёта
+///
+public interface IWellReportService
+{
+ ///
+ /// Сформировать
+ ///
+ ///
+ ///
+ ///
+ Task GetAsync(int idWell, CancellationToken token);
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure.Tests/Services/WellReportServiceTest.cs b/AsbCloudInfrastructure.Tests/Services/WellReportServiceTest.cs
new file mode 100644
index 00000000..2c3fe4c2
--- /dev/null
+++ b/AsbCloudInfrastructure.Tests/Services/WellReportServiceTest.cs
@@ -0,0 +1,335 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Data.ProcessMaps.Operations;
+using AsbCloudApp.Data.Trajectory;
+using AsbCloudApp.Data.User;
+using AsbCloudApp.Data.WellOperation;
+using AsbCloudApp.Repositories;
+using AsbCloudApp.Requests;
+using AsbCloudApp.Services;
+using AsbCloudApp.Services.ProcessMaps.WellDrilling;
+using AsbCloudInfrastructure.Services.WellReport;
+using NSubstitute;
+using System.Collections.Generic;
+using System;
+using System.Threading;
+using Xunit;
+using AsbCloudDb.Model;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace AsbCloudInfrastructure.Tests.Services;
+
+public class WellReportServiceTest
+{
+ private static readonly WellDto Well = new()
+ {
+ Caption = "Скважина №1",
+ Cluster = "Кластер A",
+ Deposit = "Месторождение Б",
+ Latitude = 55.7558,
+ Longitude = 37.6176,
+ Timezone = new SimpleTimezoneDto { Hours = 3 },
+ WellType = "Разведочная",
+ IdWellType = 1,
+ IdCluster = 1001,
+ IdState = 1,
+ StartDate = DateTimeOffset.Now.AddMonths(-2),
+ LastTelemetryDate = DateTimeOffset.Now,
+ IdTelemetry = 12345,
+ };
+
+ private static readonly IEnumerable WellOperations = new[]
+ {
+ new WellOperationDto
+ {
+ Id = 1,
+ IdWell = 101,
+ IdWellSectionType = 1001,
+ IdType = 1,
+ IdCategory = 2001,
+ DepthStart = 1500,
+ DepthEnd = 1550,
+ DateStart = new DateTimeOffset(new DateTime(2024, 1, 13, 2, 0, 0)),
+ DurationHours = 48,
+ IdPlan = null,
+ IdParentCategory = 2001,
+ Day = 5
+ },
+ new WellOperationDto
+ {
+ Id = 4,
+ IdWell = 101,
+ IdWellSectionType = 1002,
+ IdType = 1,
+ IdCategory = 2001,
+ DepthStart = 1500,
+ DepthEnd = 1550,
+ DateStart = new DateTimeOffset(new DateTime(2024, 1, 10, 0, 0, 0)),
+ DurationHours = 48,
+ IdPlan = null,
+ IdParentCategory = 2001,
+ Day = 3
+ },
+ new WellOperationDto
+ {
+ Id = 2,
+ IdWell = 102,
+ IdWellSectionType = 1002,
+ IdType = 0,
+ IdCategory = 2002,
+ DepthStart = 2500,
+ DepthEnd = 2600,
+ DateStart = new DateTimeOffset(new DateTime(2024, 1, 10, 0, 0, 0)),
+ DurationHours = 72,
+ IdPlan = 1,
+ IdParentCategory = 3002,
+ Day = 3
+ },
+ new WellOperationDto
+ {
+ Id = 3,
+ IdWell = 103,
+ IdWellSectionType = 1003,
+ IdType = 0,
+ IdCategory = 2003,
+ DepthStart = 3500,
+ DepthEnd = 3600,
+ DateStart = new DateTimeOffset(new DateTime(2024, 1, 10, 1, 0, 0)),
+ DurationHours = 24,
+ IdPlan = 2,
+ IdParentCategory = 3003,
+ Day = 4
+ }
+ };
+
+ private readonly IWellService wellService;
+ private readonly IWellOperationService wellOperationService;
+ private readonly IWellContactService wellContactService;
+ private readonly IProcessMapReportDrillingService processMapReportDrillingService;
+ private readonly ISubsystemService subsystemService;
+
+ private readonly ITrajectoryRepository trajectoryPlanRepository;
+ private readonly ITrajectoryRepository trajectoryFactRepository;
+
+ private readonly IChangeLogRepository
+ processMapPlanRotorRepository;
+
+ private readonly IScheduleRepository scheduleRepository;
+
+ private readonly WellReportService wellReportService;
+
+ public WellReportServiceTest()
+ {
+ wellService = Substitute.For();
+ wellOperationService = Substitute.For();
+ wellContactService = Substitute.For();
+ processMapReportDrillingService = Substitute.For();
+ subsystemService = Substitute.For();
+ trajectoryPlanRepository = Substitute.For>();
+ trajectoryFactRepository = Substitute.For>();
+ processMapPlanRotorRepository =
+ Substitute.For>();
+
+ scheduleRepository = Substitute.For();
+
+ wellService.GetOrDefaultAsync(Arg.Any(), Arg.Any())
+ .ReturnsForAnyArgs(Well);
+
+ wellOperationService
+ .GetAsync(Arg.Is(x => x.OperationType == WellOperation.IdOperationTypeFact),
+ Arg.Any())
+ .Returns(WellOperations.Where(x => x.IdType == WellOperation.IdOperationTypeFact));
+
+ wellOperationService
+ .GetAsync(Arg.Is(x => x.OperationType == WellOperation.IdOperationTypePlan),
+ Arg.Any())
+ .Returns(WellOperations.Where(x => x.IdType == WellOperation.IdOperationTypePlan));
+
+ wellReportService = new WellReportService(wellService,
+ wellOperationService,
+ wellContactService,
+ processMapReportDrillingService,
+ subsystemService,
+ trajectoryPlanRepository,
+ trajectoryFactRepository,
+ processMapPlanRotorRepository,
+ scheduleRepository);
+ }
+
+ [Fact]
+ public async Task Returns_well_info_not_null()
+ {
+ //act
+ var result = await wellReportService.GetAsync(1, CancellationToken.None);
+
+ //assert
+ Assert.NotNull(result);
+ Assert.NotNull(result.Well);
+ }
+
+ [Fact]
+ public async Task Returns_contacts_not_empty()
+ {
+ //arrange
+ var contacts = new[]
+ {
+ new ContactDto()
+ {
+ Id = 1,
+ IdCompanyType = 2,
+ IdWell = 101,
+ FullName = "Ivan Petrov",
+ Email = "ivan.petrov@example.com",
+ Phone = "+7 (123) 456-78-90",
+ Position = "Chief Engineer",
+ Company = "test"
+ }
+ };
+
+ wellContactService.GetAllAsync(Arg.Any(), Arg.Any())
+ .ReturnsForAnyArgs(contacts);
+
+ //act
+ var result = await wellReportService.GetAsync(1, CancellationToken.None);
+
+ //assert
+ Assert.NotNull(result);
+ Assert.Single(result.Contacts);
+ }
+
+ [Fact]
+ public async Task Returns_valid_from_and_to_dates()
+ {
+ //act
+ var result = await wellReportService.GetAsync(1, CancellationToken.None);
+
+ //assert
+ Assert.NotNull(result);
+
+ var expectedDateFrom = new DateTimeOffset(new DateTime(2024, 1, 10, 0, 0, 0));
+ var expectedDateTo = new DateTimeOffset(new DateTime(2024, 1, 11, 1, 0, 0));
+
+ Assert.Equal(expectedDateFrom, result.DateFrom);
+ Assert.Equal(expectedDateTo, result.DateTo);
+ }
+
+ [Fact]
+ public async Task Returns_valid_days()
+ {
+ //act
+ var result = await wellReportService.GetAsync(1, CancellationToken.None);
+
+ //assert
+ Assert.NotNull(result);
+
+ Assert.Equal(4, result.Days.Plan);
+ Assert.Equal(5, result.Days.Fact);
+ }
+
+ [Fact]
+ public async Task Returns_valid_vertical_depth()
+ {
+ //arrange
+ var planTrajectory = new TrajectoryGeoPlanDto
+ {
+ Id = 1,
+ IdWell = 123,
+ WellboreDepth = 1500.75,
+ ZenithAngle = 45.5,
+ AzimuthGeo = 120.0,
+ AzimuthMagnetic = 115.5,
+ VerticalDepth = 1480.3,
+ UpdateDate = DateTimeOffset.UtcNow,
+ IdUser = 42
+ };
+
+ var factTrajectory = new TrajectoryGeoFactDto
+ {
+ Id = 1,
+ IdWell = 123,
+ WellboreDepth = 1500.75,
+ ZenithAngle = 45.5,
+ AzimuthGeo = 120.0,
+ AzimuthMagnetic = 115.5,
+ VerticalDepth = 1600,
+ UpdateDate = DateTimeOffset.UtcNow,
+ IdUser = 42
+ };
+
+ trajectoryPlanRepository.GetAsync(Arg.Any(), Arg.Any())
+ .ReturnsForAnyArgs(new[] { planTrajectory });
+
+ trajectoryFactRepository.GetAsync(Arg.Any(), Arg.Any())
+ .ReturnsForAnyArgs(new[] { factTrajectory });
+
+ //act
+ var result = await wellReportService.GetAsync(1, CancellationToken.None);
+
+ //assert
+ Assert.NotNull(result);
+
+ Assert.Equal(result.VerticalDepth.Plan, 1480.3);
+ Assert.Equal(result.VerticalDepth.Fact, 1600);
+ }
+
+ [Fact]
+ public async Task Returns_valid_without_ntp_days()
+ {
+ //act
+ var result = await wellReportService.GetAsync(1, CancellationToken.None);
+
+ //assert
+ Assert.NotNull(result);
+
+ Assert.Equal(4, result.WithoutNtpDays);
+ }
+
+
+ [Fact]
+ public async Task Returns_section_reports_not_empty()
+ {
+ //act
+ var result = await wellReportService.GetAsync(1, CancellationToken.None);
+
+ //assert
+ Assert.NotNull(result);
+ Assert.Equal(2, result.SectionReports.Count());
+ }
+
+ [Fact]
+ public async Task Returns_driller_reports_not_empty()
+ {
+ //arrange
+ var schedules = new[]
+ {
+ new ScheduleDto
+ {
+ Id = 1,
+ IdDriller = 2001,
+ ShiftStart = new TimeDto(7),
+ ShiftEnd = new TimeDto(20),
+ DrillStart = new DateTimeOffset(2024, 9, 1, 7, 0, 0, TimeSpan.Zero),
+ DrillEnd = new DateTimeOffset(2024, 9, 1, 19, 0, 0, TimeSpan.Zero),
+ },
+ new ScheduleDto
+ {
+ Id = 2,
+ IdDriller = 2002,
+ ShiftStart = new TimeDto(20),
+ ShiftEnd = new TimeDto(16),
+ DrillStart = new DateTimeOffset(2024, 9, 1, 7, 0, 0, TimeSpan.Zero),
+ DrillEnd = new DateTimeOffset(2024, 9, 1, 19, 0, 0, TimeSpan.Zero),
+ }
+ };
+
+ scheduleRepository.GetByIdWellAsync(Arg.Any(), Arg.Any())
+ .ReturnsForAnyArgs(schedules);
+
+ //act
+ var result = await wellReportService.GetAsync(1, CancellationToken.None);
+
+ //assert
+ Assert.NotNull(result);
+ Assert.Equal(2, result.DrillerReports.Count());
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
index 2942a43a..327b70b7 100644
--- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
+++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
@@ -53,6 +53,7 @@
+
diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs
index 7e8d5867..ca66ebe8 100644
--- a/AsbCloudInfrastructure/DependencyInjection.cs
+++ b/AsbCloudInfrastructure/DependencyInjection.cs
@@ -47,6 +47,8 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
+using AsbCloudApp.Services.WellReport;
+using AsbCloudInfrastructure.Services.WellReport;
namespace AsbCloudInfrastructure;
@@ -469,7 +471,9 @@ public static class DependencyInjection
services.AddTransient, TrajectoryEditableRepository>();
services.AddTransient, TrajectoryEditableRepository>();
services.AddTransient();
- services.AddTransient();
+ services.AddTransient, TrajectoryEditableRepository>();
+ services.AddTransient, TrajectoryEditableRepository>();
+ services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient, CrudCacheRepositoryBase();
services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+
return services;
}
}
diff --git a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
index f557d106..2fa7b639 100644
--- a/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
+++ b/AsbCloudInfrastructure/Repository/WellOperationRepository.cs
@@ -197,34 +197,34 @@ public class WellOperationRepository : CrudRepositoryBase
- {
- entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
- var query = dbContext.Set()
- .Where(o => o.IdType == WellOperation.IdOperationTypeFact)
- .GroupBy(o => o.IdWell)
- .Select(group => new
- {
- IdWell = group.Key,
- FirstFact = group.OrderBy(o => o.DateStart).First(),
- LastFact = group.OrderBy(o => o.DateStart).Last(),
- });
+ var cachedDictionary = memoryCache.GetOrCreate(cacheKeyWellOperations, (entry) =>
+ {
+ entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
+ var query = dbContext.Set()
+ .Where(o => o.IdType == WellOperation.IdOperationTypeFact)
+ .GroupBy(o => o.IdWell)
+ .Select(group => new
+ {
+ IdWell = group.Key,
+ FirstFact = group.OrderBy(o => o.DateStart).First(),
+ LastFact = group.OrderBy(o => o.DateStart).Last(),
+ });
- var entities = query.ToArray();
+ var entities = query.ToArray();
- var dictionary = entities.ToDictionary(s => s.IdWell, s => (Convert(s.FirstFact), Convert(s.LastFact)));
- entry.Value = dictionary;
+ var dictionary = entities.ToDictionary(s => s.IdWell, s => (Convert(s.FirstFact), Convert(s.LastFact)));
+ entry.Value = dictionary;
- return dictionary;
+ return dictionary;
- })!;
+ })!;
- var firstAndLast = cachedDictionary.GetValueOrDefault(idWell);
- return firstAndLast;
+ var firstAndLast = cachedDictionary.GetValueOrDefault(idWell);
+ return firstAndLast;
}
- public override async Task DeleteAsync(int id, CancellationToken token)
+ public override async Task DeleteAsync(int id, CancellationToken token)
{
var result = await base.DeleteAsync(id, token);
if (result > 0)
diff --git a/AsbCloudInfrastructure/Services/WellReport/WellReport.xlsx b/AsbCloudInfrastructure/Services/WellReport/WellReport.xlsx
new file mode 100644
index 00000000..9e3b5ffd
Binary files /dev/null and b/AsbCloudInfrastructure/Services/WellReport/WellReport.xlsx differ
diff --git a/AsbCloudInfrastructure/Services/WellReport/WellReportExportService.cs b/AsbCloudInfrastructure/Services/WellReport/WellReportExportService.cs
new file mode 100644
index 00000000..55ec4fb0
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/WellReport/WellReportExportService.cs
@@ -0,0 +1,383 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data;
+using AsbCloudApp.Data.Subsystems;
+using AsbCloudApp.Data.User;
+using AsbCloudApp.Data.WellReport;
+using AsbCloudApp.Services.WellReport;
+using ClosedXML.Excel;
+
+namespace AsbCloudInfrastructure.Services.WellReport;
+
+public class WellReportExportService : IWellReportExportService
+{
+ private static readonly IDictionary PlanOperatingModeRows = new Dictionary()
+ {
+ { 2, 79 }, // Направление план
+ { 3, 81 }, // Кондуктор план
+ { 9, 83 }, // Кондуктор 2 план
+ { 1, 85 }, // Пилотный ствол план
+ { 7, 87 }, // Пилотный ствол 2 план
+ { 13, 89 }, // Пилотный ствол 3 план
+ { 4, 91 }, // Эксплуатационная колонна план
+ { 10, 93 }, // Эксплуатационная колонна 2 план
+ { 6, 95 }, // Хвостовик план
+ { 12, 97 }, // Хвостовик 2 план
+ { 18, 99 }, // Хвостовик 3 план
+ { 24, 101 }, // Хвостовик 4 план
+ { 30, 103 }, // Хвостовик 5 план
+ { 34, 105 }, // Хвостовик 6 план
+ { 35, 107 }, // Хвостовик 7 план
+ { 36, 109 }, // Хвостовик 8 план
+ { 37, 111 }, // Хвостовик 9 план
+ { 38, 113 } // Хвостовик 10 план
+ };
+
+ private static readonly IDictionary FactOperatingModeRows = new Dictionary
+ {
+ { 2, 80 }, // Направление факт
+ { 3, 82 }, // Кондуктор факт
+ { 9, 84 }, // Кондуктор 2 факт
+ { 1, 86 }, // Пилотный ствол факт
+ { 7, 88 }, // Пилотный ствол 2 факт
+ { 13, 90 }, // Пилотный ствол 3 факт
+ { 4, 92 }, // Эксплуатационная колонна факт
+ { 10, 94 }, // Эксплуатационная колонна 2 факт
+ { 6, 96 }, // Хвостовик факт
+ { 12, 98 }, // Хвостовик 2 факт
+ { 18, 100 }, // Хвостовик 3 факт
+ { 24, 102 }, // Хвостовик 4 факт
+ { 30, 104 }, // Хвостовик 5 факт
+ { 34, 106 }, // Хвостовик 6 факт
+ { 35, 108 }, // Хвостовик 7 факт
+ { 36, 110 }, // Хвостовик 8 факт
+ { 37, 112 }, // Хвостовик 9 факт
+ { 38, 114 } // Хвостовик 10 факт
+ };
+
+ private static readonly IDictionary SubsystemRows = new Dictionary()
+ {
+ { 2, 140 }, // Направление
+ { 3, 141 }, // Кондуктор
+ { 9, 142 }, // Кондуктор 2
+ { 1, 143 }, // Пилотный ствол
+ { 7, 144 }, // Пилотный ствол 2
+ { 13, 145 }, // Пилотный ствол 3
+ { 4, 146 }, // Эксплуатационная колонна
+ { 10, 147 }, // Эксплуатационная колонна 2
+ { 6, 148 }, // Хвостовик
+ { 12, 149 }, // Хвостовик 2
+ { 18, 150 }, // Хвостовик 3
+ { 24, 151 }, // Хвостовик 4
+ { 30, 152 }, // Хвостовик 5
+ { 34, 153 }, // Хвостовик 6
+ { 35, 154 }, // Хвостовик 7
+ { 36, 155 }, // Хвостовик 8
+ { 37, 156 }, // Хвостовик 9
+ { 38, 157 } // Хвостовик 10
+ };
+
+ private static readonly IDictionary SetpointsRows = new Dictionary()
+ {
+ { 2, 161 }, // Направление
+ { 3, 162 }, // Кондуктор
+ { 9, 163 }, // Кондуктор 2
+ { 1, 164 }, // Пилотный ствол
+ { 7, 165 }, // Пилотный ствол 2
+ { 13, 166 }, // Пилотный ствол 3
+ { 4, 167 }, // Эксплуатационная колонна
+ { 10, 168 }, // Эксплуатационная колонна 2
+ { 6, 169 }, // Хвостовик
+ { 12, 170 }, // Хвостовик 2
+ { 18, 171 }, // Хвостовик 3
+ { 24, 172 }, // Хвостовик 4
+ { 30, 173 }, // Хвостовик 5
+ { 34, 174 }, // Хвостовик 6
+ { 35, 175 }, // Хвостовик 7
+ { 36, 176 }, // Хвостовик 8
+ { 37, 177 }, // Хвостовик 9
+ { 38, 178 } // Хвостовик 10
+ };
+
+ private const string TemplateName = "WellReport.xlsx";
+ private const string SheetName = "Отчёт";
+
+ private readonly IWellReportService wellReportService;
+
+ private const string DateFromCell = "D5";
+ private const string DateToCell = "E5";
+ private const string DaysPlanCell = "D6";
+ private const string DaysFactCell = "E6";
+ private const string WithoutNtpDaysCell = "D8";
+ private const string WellBoreDepthPlanCell = "D12";
+ private const string VerticalDepthPlanCell = "E12";
+ private const string WellBoreDepthFactCell = "D13";
+ private const string VerticalDepthFactCell = "E13";
+ private const string WellCell = "I5";
+ private const string ClusterCell = "I6";
+ private const string DepositCell = "I7";
+ private const string CustomerCell = "N5";
+
+ public WellReportExportService(IWellReportService wellReportService)
+ {
+ this.wellReportService = wellReportService;
+ }
+
+ public async Task<(string Name, Stream File)?> ExportAsync(int idWell, CancellationToken token)
+ {
+ var report = await wellReportService.GetAsync(idWell, token);
+
+ if (report == null)
+ return null;
+
+ var stream = Assembly.GetExecutingAssembly().GetTemplateCopyStream(TemplateName);
+ using var workbook = new XLWorkbook(stream);
+
+ var sheet = workbook.GetWorksheet(SheetName);
+
+ FillSheet(sheet, report);
+
+ MemoryStream memoryStream = new();
+ workbook.SaveAs(memoryStream, new SaveOptions { });
+ memoryStream.Seek(0, SeekOrigin.Begin);
+
+ var name = $"Отчёт по скважине {report.Well.Caption} куст {report.Well.Cluster}.xlsx";
+
+ return (name, memoryStream);
+ }
+
+ private static void FillSheet(IXLWorksheet sheet, WellReportDto report)
+ {
+ sheet.Cell(DateFromCell).SetCellValue(report.DateFrom);
+ sheet.Cell(DateToCell).SetCellValue(report.DateTo);
+ sheet.Cell(DaysPlanCell).SetCellValue(report.Days.Plan);
+ sheet.Cell(DaysFactCell).SetCellValue(report.Days.Fact);
+ sheet.Cell(WithoutNtpDaysCell).SetCellValue(report.WithoutNtpDays);
+ sheet.Cell(WellBoreDepthPlanCell).SetCellValue(report.WellBoreDepth.Plan);
+ sheet.Cell(WellBoreDepthFactCell).SetCellValue(report.WellBoreDepth.Fact);
+ sheet.Cell(VerticalDepthPlanCell).SetCellValue(report.VerticalDepth.Plan);
+ sheet.Cell(VerticalDepthFactCell).SetCellValue(report.VerticalDepth.Fact);
+ sheet.Cell(WellCell).SetCellValue(report.Well.Caption);
+ sheet.Cell(ClusterCell).SetCellValue(report.Well.Cluster);
+ sheet.Cell(DepositCell).SetCellValue(report.Well.Deposit);
+
+ var customer = report.Well.Companies.FirstOrDefault(x => x.IdCompanyType == 1);
+ sheet.Cell(CustomerCell).SetCellValue(customer?.Caption);
+
+ FillContacts(sheet, report.Contacts);
+ FillSectionReports(sheet, report.SectionReports);
+ FillDrillerReports(sheet, report.DrillerReports);
+ }
+
+ private static void FillContacts(IXLWorksheet sheet, IEnumerable contacts)
+ {
+ var positionsByCompanyType = new Dictionary()
+ {
+ { 7, "Супервайзер" },
+ { 2, "Мастер" },
+ { 3, "Инженер по автоматизации" },
+ { 5, "Инженер по р-рам " },
+ { 6, "Инженер ННБ" },
+ { 14, "Инженер по долотам" },
+ { 4, "Инженер ГТИ" },
+ { 9, "Инженер по цементированию" }
+ };
+
+ const int positionColumn = 11;
+ const int fullNameColumn = 14;
+ const int companyColumn = 16;
+ const int phoneColumn = 18;
+
+ contacts = contacts.OrderByDescending(x => x.Id)
+ .GroupBy(x => x.IdCompanyType)
+ .Select(x => x.First());
+
+ var row = 6;
+
+ foreach (var contact in contacts)
+ {
+ if (!positionsByCompanyType.TryGetValue(contact.IdCompanyType, out var position))
+ continue;
+
+ sheet.Cell(row, positionColumn).SetCellValue(position);
+ sheet.Cell(row, fullNameColumn).SetCellValue(contact.FullName);
+ sheet.Cell(row, companyColumn).SetCellValue(contact.Company);
+ sheet.Cell(row, phoneColumn).SetCellValue(contact.Phone);
+
+ row++;
+ }
+ }
+
+ private static void FillDrillerReports(IXLWorksheet sheet, IEnumerable drillerReports)
+ {
+ drillerReports = drillerReports.OrderBy(x => x.Shedule.DrillStart);
+
+ const int IdSubsystemAPDRotor = 11;
+ const int IdSubsystemAPDSlide = 12;
+ const int IdSubsystemOscillation = 65536;
+
+ const int fullNameColumn = 1;
+ const int drillStartColumn = 5;
+ const int drillEndColumn = 6;
+ const int shiftStart = 7;
+ const int shiftEnd = 8;
+ const int kUsageApdRotorColumn = 9;
+ const int kUsageApdSlideColumn = 10;
+ const int kUsageOscillationColumn = 11;
+
+ var row = 182;
+
+ foreach (var drillingReport in drillerReports)
+ {
+ sheet.Cell(row, fullNameColumn).SetCellValue(drillingReport.Shedule.Driller?.FullName);
+ sheet.Cell(row, drillStartColumn).SetCellValue(drillingReport.Shedule.DrillStart);
+ sheet.Cell(row, drillEndColumn).SetCellValue(drillingReport.Shedule.DrillEnd);
+ sheet.Cell(row, shiftStart).SetCellValue(drillingReport.Shedule.ShiftStart.ToString());
+ sheet.Cell(row, shiftEnd).SetCellValue(drillingReport.Shedule.ShiftEnd.ToString());
+
+ foreach (var subsystemStat in drillingReport.SubsystemsStat)
+ {
+ switch (subsystemStat.IdSubsystem)
+ {
+ case IdSubsystemAPDRotor:
+ sheet.Cell(row, kUsageApdRotorColumn).SetCellValue(subsystemStat.KUsage);
+ break;
+ case IdSubsystemAPDSlide:
+ sheet.Cell(row, kUsageApdSlideColumn).SetCellValue(subsystemStat.KUsage);
+ break;
+ case IdSubsystemOscillation:
+ sheet.Cell(row, kUsageOscillationColumn).SetCellValue(subsystemStat.KUsage);
+ break;
+ }
+ }
+
+ row++;
+ }
+ }
+
+ private static void FillSectionReports(IXLWorksheet sheet, IEnumerable sectionReports)
+ {
+ foreach (var sectionReport in sectionReports)
+ {
+ FillOperatingMode(sheet, sectionReport.IdSection, sectionReport.OperatingMode);
+
+ var drillingBySetpoints = sectionReport.DrillingBySetpoints;
+
+ if (drillingBySetpoints != null)
+ FillDrillingBySetpoints(sheet, sectionReport.IdSection, drillingBySetpoints);
+
+ FillSubsystemsStat(sheet, sectionReport.IdSection, sectionReport.SubsystemsStat);
+ }
+ }
+
+ private static void FillDrillingBySetpoints(IXLWorksheet sheet, int idSection,
+ DrillingBySetpointsDto drillingBySetpoints)
+ {
+ const int pressureColumn = 8;
+ const int axialLoadColumn = 9;
+ const int topDriveTorqueColumn = 10;
+ const int speedLimitColumn = 11;
+
+ if (!SetpointsRows.TryGetValue(idSection, out var row))
+ return;
+
+ sheet.Cell(row, pressureColumn).SetCellValue(drillingBySetpoints.MetersByPressure);
+ sheet.Cell(row, axialLoadColumn).SetCellValue(drillingBySetpoints.MetersByLoad);
+ sheet.Cell(row, topDriveTorqueColumn).SetCellValue(drillingBySetpoints.MetersByTorque);
+ sheet.Cell(row, speedLimitColumn).SetCellValue(drillingBySetpoints.MetersBySpeed);
+ }
+
+ private static void FillSubsystemsStat(IXLWorksheet sheet, int idSection,
+ IEnumerable subsystemsStat)
+ {
+ const int idSubsystemAPDRotor = 11;
+ const int idSubsystemAPDSlide = 12;
+ const int idSubsystemOscillation = 65536;
+
+ const int kUsageApdRotorColumn = 3;
+ const int kUsageApdSlideColumn = 4;
+ const int kUsageOscillationColumn = 5;
+
+ const int sumDepthIntervalApdRotorColumn = 14;
+ const int sumDepthIntervalApdSlideColumn = 15;
+ const int sumDepthIntervalApdOscillation = 17;
+
+ if (!SubsystemRows.TryGetValue(idSection, out var row))
+ return;
+
+ foreach (var subsystemStat in subsystemsStat)
+ {
+ switch (subsystemStat.IdSubsystem)
+ {
+ case idSubsystemAPDRotor:
+ sheet.Cell(row, kUsageApdRotorColumn).SetCellValue(subsystemStat.KUsage);
+ sheet.Cell(row, sumDepthIntervalApdRotorColumn).SetCellValue(subsystemStat.SumDepthInterval);
+ break;
+ case idSubsystemAPDSlide:
+ sheet.Cell(row, kUsageApdSlideColumn).SetCellValue(subsystemStat.KUsage);
+ sheet.Cell(row, sumDepthIntervalApdSlideColumn).SetCellValue(subsystemStat.SumDepthInterval);
+ break;
+ case idSubsystemOscillation:
+ sheet.Cell(row, kUsageOscillationColumn).SetCellValue(subsystemStat.KUsage);
+ sheet.Cell(row, sumDepthIntervalApdOscillation).SetCellValue(subsystemStat.SumDepthInterval);
+ break;
+ }
+ }
+ }
+
+ private static void FillOperatingMode(IXLWorksheet sheet, int idSection,
+ PlanFactDto operatingMode)
+ {
+ const int depthStartColumn = 3;
+ const int depthEndColumn = 4;
+ const int ropMinColumn = 6;
+ const int ropMaxColumn = 7;
+ const int ropAvgColumn = 8;
+ const int weightOnBitMinColumn = 9;
+ const int weightOnBitMaxColumn = 10;
+ const int weightOnBitAvgColumn = 11;
+ const int driveTorqueMinColumn = 12;
+ const int driveTorqueMaxColumn = 13;
+ const int driveTorqueAvgColumn = 14;
+ const int differentialPressureMinColumn = 15;
+ const int differentialPressureMaxColumn = 16;
+ const int differentialPressureAvgColumn = 17;
+ const int frowRateMinColumn = 18;
+ const int frowRateMaxColumn = 19;
+
+ if (!PlanOperatingModeRows.TryGetValue(idSection, out var planRow))
+ return;
+
+ if (!FactOperatingModeRows.TryGetValue(idSection, out var factRow))
+ return;
+
+ sheet.Cell(planRow, depthStartColumn).SetCellValue(operatingMode.Plan?.DepthStart);
+ sheet.Cell(planRow, depthEndColumn).SetCellValue(operatingMode.Plan?.DepthEnd);
+
+ sheet.Cell(planRow, ropMinColumn).SetCellValue(operatingMode.Plan?.RopMin);
+ sheet.Cell(planRow, ropMaxColumn).SetCellValue(operatingMode.Plan?.RopMax);
+ sheet.Cell(planRow, ropAvgColumn).SetCellValue(operatingMode.Plan?.RopAvg);
+
+ sheet.Cell(planRow, weightOnBitMinColumn).SetCellValue(operatingMode.Plan?.WeightOnBitMin);
+ sheet.Cell(planRow, weightOnBitMaxColumn).SetCellValue(operatingMode.Plan?.WeightOnBitMax);
+ sheet.Cell(planRow, weightOnBitAvgColumn).SetCellValue(operatingMode.Plan?.WeightOnBitAvg);
+
+ sheet.Cell(planRow, driveTorqueMinColumn).SetCellValue(operatingMode.Plan?.DriveTorqueMin);
+ sheet.Cell(planRow, driveTorqueMaxColumn).SetCellValue(operatingMode.Plan?.DriveTorqueMax);
+ sheet.Cell(planRow, driveTorqueAvgColumn).SetCellValue(operatingMode.Plan?.DriveTorqueAvg);
+
+ sheet.Cell(planRow, differentialPressureMinColumn).SetCellValue(operatingMode.Plan?.DifferentialPressureMin);
+ sheet.Cell(planRow, differentialPressureMaxColumn).SetCellValue(operatingMode.Plan?.DifferentialPressureMax);
+ sheet.Cell(planRow, differentialPressureAvgColumn).SetCellValue(operatingMode.Plan?.DifferentialPressureAvg);
+
+ sheet.Cell(planRow, frowRateMinColumn).SetCellValue(operatingMode.Plan?.FrowRateMin);
+ sheet.Cell(planRow, frowRateMaxColumn).SetCellValue(operatingMode.Plan?.FrowRateMax);
+
+ sheet.Cell(factRow, depthStartColumn).SetCellValue(operatingMode.Fact?.DepthStart);
+ sheet.Cell(factRow, depthEndColumn).SetCellValue(operatingMode.Fact?.DepthEnd);
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/WellReport/WellReportService.cs b/AsbCloudInfrastructure/Services/WellReport/WellReportService.cs
new file mode 100644
index 00000000..cf9ef0fe
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/WellReport/WellReportService.cs
@@ -0,0 +1,234 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using AsbCloudApp.Data;
+using AsbCloudApp.Data.ProcessMaps.Operations;
+using AsbCloudApp.Data.Trajectory;
+using AsbCloudApp.Data.WellOperation;
+using AsbCloudApp.Data.WellReport;
+using AsbCloudApp.Repositories;
+using AsbCloudApp.Requests;
+using AsbCloudApp.Services;
+using AsbCloudApp.Services.ProcessMaps.WellDrilling;
+using AsbCloudApp.Services.WellReport;
+using AsbCloudDb.Model;
+
+namespace AsbCloudInfrastructure.Services.WellReport;
+
+public class WellReportService : IWellReportService
+{
+ private readonly IWellService wellService;
+ private readonly IWellOperationService wellOperationService;
+ private readonly IWellContactService wellContactService;
+ private readonly IProcessMapReportDrillingService processMapReportDrillingService;
+ private readonly ISubsystemService subsystemService;
+
+ private readonly ITrajectoryRepository trajectoryPlanRepository;
+ private readonly ITrajectoryRepository trajectoryFactRepository;
+
+ private readonly IChangeLogRepository
+ processMapPlanRotorRepository;
+
+ private readonly IScheduleRepository scheduleRepository;
+
+ private IEnumerable factWellOperations;
+ private IEnumerable planWellOperations;
+
+ public WellReportService(IWellService wellService,
+ IWellOperationService wellOperationService,
+ IWellContactService wellContactService,
+ IProcessMapReportDrillingService processMapReportDrillingService,
+ ISubsystemService subsystemService,
+ ITrajectoryRepository trajectoryPlanRepository,
+ ITrajectoryRepository trajectoryFactRepository,
+ IChangeLogRepository processMapPlanRotorRepository,
+ IScheduleRepository scheduleRepository)
+ {
+ this.wellService = wellService;
+ this.wellOperationService = wellOperationService;
+ this.wellContactService = wellContactService;
+ this.processMapReportDrillingService = processMapReportDrillingService;
+ this.subsystemService = subsystemService;
+ this.trajectoryPlanRepository = trajectoryPlanRepository;
+ this.trajectoryFactRepository = trajectoryFactRepository;
+ this.processMapPlanRotorRepository = processMapPlanRotorRepository;
+ this.scheduleRepository = scheduleRepository;
+ }
+
+ public async Task GetAsync(int idWell, CancellationToken token)
+ {
+ var well = await wellService.GetOrDefaultAsync(idWell, token);
+
+ if (well == null)
+ return null;
+
+ await InitWellOperations(idWell, token);
+
+ var wellContactRequest = new WellContactRequest
+ {
+ IdsWells = new[] { idWell },
+ };
+
+ var contacts = await wellContactService.GetAllAsync(wellContactRequest, token);
+
+ var sectionReports = await GetSectionReportsAsync(idWell, token);
+ var drillerReports = await GetDrillerReportsAsync(idWell, token);
+
+ var firstFactOperation = factWellOperations.MinByOrDefault(x => x.DateStart);
+ var lastFactOperation = factWellOperations.MaxByOrDefault(x => x.DateStart);
+
+ var lastPlanOperation = planWellOperations.MaxByOrDefault(x => x.DateStart);
+
+ var planTrajectories = await trajectoryPlanRepository.GetAsync(idWell, token);
+ var factTrajectories = await trajectoryFactRepository.GetAsync(idWell, token);
+
+ var factOperationsWithoutNpt = factWellOperations.Where(o => WellOperationCategory.NonProductiveTimeSubIds
+ .Any(x => x != o.IdCategory));
+
+ return new WellReportDto
+ {
+ Well = well,
+ DateFrom = firstFactOperation?.DateStart,
+ DateTo = lastPlanOperation?.DateStart.AddHours(lastPlanOperation.DurationHours),
+ Days = new PlanFactDto
+ {
+ Plan = lastPlanOperation?.Day,
+ Fact = lastFactOperation?.Day
+ },
+ WellBoreDepth = new PlanFactDto
+ {
+ Plan = planWellOperations.MaxOrDefault(x => x.DepthEnd),
+ Fact = factWellOperations.MaxOrDefault(x => x.DepthEnd)
+ },
+ VerticalDepth = new PlanFactDto
+ {
+ Plan = planTrajectories.Max(x => x.VerticalDepth),
+ Fact = factTrajectories.Max(x => x.VerticalDepth)
+ },
+ WithoutNtpDays = factOperationsWithoutNpt.Sum(x => x.DurationHours) / 24,
+ Contacts = contacts,
+ SectionReports = sectionReports,
+ DrillerReports = drillerReports,
+ };
+ }
+
+ private async Task InitWellOperations(int idWell, CancellationToken token)
+ {
+ var request = new WellOperationRequest(new[] { idWell })
+ {
+ OperationType = WellOperation.IdOperationTypeFact
+ };
+
+ factWellOperations = await wellOperationService.GetAsync(request, token);
+
+ request.OperationType = WellOperation.IdOperationTypePlan;
+
+ planWellOperations = await wellOperationService.GetAsync(request, token);
+ }
+
+ private async Task> GetSectionReportsAsync(int idWell, CancellationToken token)
+ {
+ var factWellOperationsBySection = factWellOperations.GroupBy(x => x.IdWellSectionType);
+
+ var processMapPlanRequest = new ProcessMapPlanBaseRequestWithWell(idWell);
+
+ var processMapPlanRotorBySection =
+ (await processMapPlanRotorRepository.GetCurrent(processMapPlanRequest, token))
+ .GroupBy(x => x.IdWellSectionType)
+ .ToDictionary(x => x.Key, x => x.AsEnumerable());
+
+ var dataSaubStatRequest = new DataSaubStatRequest();
+
+ var processMapReportBySection =
+ (await processMapReportDrillingService.GetAsync(idWell, dataSaubStatRequest, token))
+ .GroupBy(x => x.IdWellSectionType)
+ .ToDictionary(x => x.Key, x => x.AsEnumerable());
+
+ var sectionReports = new List();
+
+ foreach (var group in factWellOperationsBySection)
+ {
+ var subsystemRequest = new SubsystemRequest
+ {
+ IdWell = idWell,
+ GeDepth = group.Min(y => y.DepthStart),
+ LeDepth = group.Max(y => y.DepthEnd)
+ };
+
+ var sectionReport = new SectionReportDto
+ {
+ IdSection = group.Key,
+ SubsystemsStat = await subsystemService.GetStatAsync(subsystemRequest, token),
+ OperatingMode = new PlanFactDto
+ {
+ Fact = new OperatingModeDto
+ {
+ DepthStart = group.Min(w => w.DepthStart),
+ DepthEnd = group.Max(w => w.DepthEnd)
+ }
+ }
+ };
+
+ if (processMapPlanRotorBySection.TryGetValue(group.Key, out var processMapPlanRotor))
+ sectionReport.OperatingMode.Plan = new OperatingModeDto
+ {
+ DepthStart = processMapPlanRotor.Min(p => p.DepthStart),
+ DepthEnd = processMapPlanRotor.Max(p => p.DepthEnd),
+ RopMin = processMapPlanRotor.Min(p => p.RopMax),
+ RopMax = processMapPlanRotor.Max(p => p.RopMax),
+ RopAvg = processMapPlanRotor.Average(p => p.RopMax),
+ WeightOnBitMin = processMapPlanRotor.Min(p => p.WeightOnBit),
+ WeightOnBitMax = processMapPlanRotor.Max(p => p.WeightOnBitMax),
+ WeightOnBitAvg = processMapPlanRotor.Average(p => p.WeightOnBit),
+ DriveTorqueMin = processMapPlanRotor.Min(p => p.TopDriveTorque),
+ DriveTorqueMax = processMapPlanRotor.Max(p => p.TopDriveTorqueMax),
+ DriveTorqueAvg = processMapPlanRotor.Average(p => p.TopDriveTorque),
+ DifferentialPressureMin = processMapPlanRotor.Min(p => p.DifferentialPressure),
+ DifferentialPressureMax = processMapPlanRotor.Max(p => p.DifferentialPressureMax),
+ DifferentialPressureAvg = processMapPlanRotor.Average(p => p.DifferentialPressure),
+ FrowRateMin = processMapPlanRotor.Min(p => p.FlowRate),
+ FrowRateMax = processMapPlanRotor.Max(p => p.FlowRateMax)
+ };
+
+ if (processMapReportBySection.TryGetValue(group.Key, out var processMapReport))
+ sectionReport.DrillingBySetpoints = new DrillingBySetpointsDto
+ {
+ MetersByPressure = processMapReport.Sum(x => x.DeltaDepth * x.PressureDiff.SetpointUsage / 100),
+ MetersByLoad = processMapReport.Sum(x => x.DeltaDepth * x.AxialLoad.SetpointUsage / 100),
+ MetersByTorque = processMapReport.Sum(x => x.DeltaDepth * x.TopDriveTorque.SetpointUsage / 100),
+ MetersBySpeed = processMapReport.Sum(x => x.DeltaDepth * x.SpeedLimit.SetpointUsage / 100)
+ };
+
+ sectionReports.Add(sectionReport);
+ }
+
+ return sectionReports;
+ }
+
+ private async Task> GetDrillerReportsAsync(int idWell, CancellationToken token)
+ {
+ var schedules = await scheduleRepository.GetByIdWellAsync(idWell, token);
+
+ var result = new List();
+
+ foreach (var schedule in schedules)
+ {
+ var subsystemRequest = new SubsystemRequest
+ {
+ IdWell = idWell,
+ IdDriller = schedule.IdDriller
+ };
+
+ var drillerReport = new DrillerReportDto
+ {
+ Shedule = schedule,
+ SubsystemsStat = await subsystemService.GetStatAsync(subsystemRequest, token)
+ };
+
+ result.Add(drillerReport);
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/XLExtentions.cs b/AsbCloudInfrastructure/XLExtentions.cs
index 7c85c0a6..62992ec2 100644
--- a/AsbCloudInfrastructure/XLExtentions.cs
+++ b/AsbCloudInfrastructure/XLExtentions.cs
@@ -15,22 +15,25 @@ public static class XLExtentions
workbook.Worksheets.FirstOrDefault(ws => string.Equals(ws.Name.Trim(), sheetName.Trim(), StringComparison.CurrentCultureIgnoreCase))
?? throw new FileFormatException(string.Format(NotFoundSheetTemplate, sheetName));
- public static IXLCell SetCellValue(this IXLCell cell, T value, string? format = null)
+ public static IXLCell SetCellValue(this IXLCell cell, T? value, string? format = null)
{
- if (value is DateTime || value is DateTimeOffset)
- {
- cell.Style.DateFormat.Format = format ?? "DD.MM.YYYY HH:MM:SS";
+ if (value == null)
+ return cell;
- if (value is DateTimeOffset dateTimeOffset)
- {
- cell.Value = XLCellValue.FromObject(dateTimeOffset.DateTime);
- return cell;
- }
- }
+ if (value is DateTime || value is DateTimeOffset)
+ {
+ cell.Style.DateFormat.Format = format ?? "DD.MM.YYYY HH:MM:SS";
- cell.Value = XLCellValue.FromObject(value);
+ if (value is DateTimeOffset dateTimeOffset)
+ {
+ cell.Value = XLCellValue.FromObject(dateTimeOffset.DateTime);
+ return cell;
+ }
+ }
- return cell;
+ cell.Value = XLCellValue.FromObject(value);
+
+ return cell;
}
public static IXLCell SetHyperlink(this IXLCell cell, string link)
diff --git a/AsbCloudWebApi/Controllers/WellController.cs b/AsbCloudWebApi/Controllers/WellController.cs
index 98e44773..d6d97547 100644
--- a/AsbCloudWebApi/Controllers/WellController.cs
+++ b/AsbCloudWebApi/Controllers/WellController.cs
@@ -1,13 +1,13 @@
using AsbCloudApp.Data;
-using AsbCloudApp.Exceptions;
using AsbCloudApp.Services;
-using AsbCloudDb.Model;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudApp.Services.WellReport;
+using Microsoft.AspNetCore.Http;
namespace AsbCloudWebApi.Controllers;
@@ -20,11 +20,13 @@ namespace AsbCloudWebApi.Controllers;
public class WellController : ControllerBase
{
private readonly IWellService wellService;
+ private readonly IWellReportExportService wellReportExportService;
- public WellController(IWellService wellService)
- {
- this.wellService = wellService;
- }
+ public WellController(IWellService wellService, IWellReportExportService wellReportExportService)
+ {
+ this.wellService = wellService;
+ this.wellReportExportService = wellReportExportService;
+ }
///
/// Возвращает список доступных скважин
@@ -149,4 +151,25 @@ public class WellController : ControllerBase
return Ok(result);
}
+
+ //TODO: навзание пока такое. У нас в API уже есть метод с такой сигнатурой.
+
+ ///
+ /// Получить отчёт по скважине
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{idWell}/report/export")]
+ [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ public async Task ExportAsync(int idWell, CancellationToken token)
+ {
+ var report = await wellReportExportService.ExportAsync(idWell, token);
+
+ if (report is null)
+ return NoContent();
+
+ return File(report.Value.File, "application/octet-stream", report.Value.Name);
+ }
}