forked from ddrilling/AsbCloudServer
Merge branch 'dev' into feature/#37333881-critical-message
This commit is contained in:
commit
8f11e855db
@ -29,4 +29,9 @@ public class DrillerDto : IId
|
|||||||
/// Отчество
|
/// Отчество
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Patronymic { get; set; }
|
public string? Patronymic { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Полное имя
|
||||||
|
/// </summary>
|
||||||
|
public string FullName => $"{Surname} {Name} {Patronymic}";
|
||||||
}
|
}
|
||||||
|
21
AsbCloudApp/Data/WellReport/DrillerReportDto.cs
Normal file
21
AsbCloudApp/Data/WellReport/DrillerReportDto.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using AsbCloudApp.Data.Subsystems;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Data.WellReport;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Показатели бурильщиков
|
||||||
|
/// </summary>
|
||||||
|
public class DrillerReportDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Расписание
|
||||||
|
/// </summary>
|
||||||
|
public ScheduleDto Shedule { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Наработка подсистем
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<SubsystemStatDto> SubsystemsStat { get; set; } = Enumerable.Empty<SubsystemStatDto>();
|
||||||
|
}
|
27
AsbCloudApp/Data/WellReport/DrillingBySetpointsDto.cs
Normal file
27
AsbCloudApp/Data/WellReport/DrillingBySetpointsDto.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace AsbCloudApp.Data.WellReport;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Бурение по уставкам
|
||||||
|
/// </summary>
|
||||||
|
public class DrillingBySetpointsDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Метры пробуренные по уставке давления
|
||||||
|
/// </summary>
|
||||||
|
public double? MetersByPressure { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Метры пробуренные по уставке нагрузки
|
||||||
|
/// </summary>
|
||||||
|
public double? MetersByLoad { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Метры пробуренные по уставке момента
|
||||||
|
/// </summary>
|
||||||
|
public double? MetersByTorque { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Метры пробуренные по уставке скорости
|
||||||
|
/// </summary>
|
||||||
|
public double? MetersBySpeed { get; set; }
|
||||||
|
}
|
87
AsbCloudApp/Data/WellReport/OperatingModeDto.cs
Normal file
87
AsbCloudApp/Data/WellReport/OperatingModeDto.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
namespace AsbCloudApp.Data.WellReport;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Режим работы
|
||||||
|
/// </summary>
|
||||||
|
public class OperatingModeDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Интервал от
|
||||||
|
/// </summary>
|
||||||
|
public double DepthStart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Интервал до
|
||||||
|
/// </summary>
|
||||||
|
public double DepthEnd { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Скорость проходки мин, м/ч
|
||||||
|
/// </summary>
|
||||||
|
public double? RopMin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Скорость проходки максимум, м/ч
|
||||||
|
/// </summary>
|
||||||
|
public double? RopMax { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Скорость проходки среднее, м/ч
|
||||||
|
/// </summary>
|
||||||
|
public double? RopAvg { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Нагрузка на долото минимум, т
|
||||||
|
/// </summary>
|
||||||
|
public double? WeightOnBitMin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Нагрузка на долото максимум, т
|
||||||
|
/// </summary>
|
||||||
|
public double? WeightOnBitMax { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Нагрузка на долото среднее, т
|
||||||
|
/// </summary>
|
||||||
|
public double? WeightOnBitAvg { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Момент минимум, кН*м
|
||||||
|
/// </summary>
|
||||||
|
public double? DriveTorqueMin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Момент максимум, кН*м
|
||||||
|
/// </summary>
|
||||||
|
public double? DriveTorqueMax { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Момент среднее, кН*м
|
||||||
|
/// </summary>
|
||||||
|
public double? DriveTorqueAvg { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Перепад давления минимум, атм
|
||||||
|
/// </summary>
|
||||||
|
public double? DifferentialPressureMin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Перепад давления максимум, атм
|
||||||
|
/// </summary>
|
||||||
|
public double? DifferentialPressureMax { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Перепад давления среднее, атм
|
||||||
|
/// </summary>
|
||||||
|
public double? DifferentialPressureAvg { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Q насосов минимум л/с
|
||||||
|
/// </summary>
|
||||||
|
public double? FrowRateMin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Q насосов максимум л/с
|
||||||
|
/// </summary>
|
||||||
|
public double? FrowRateMax { get; set; }
|
||||||
|
}
|
30
AsbCloudApp/Data/WellReport/SectionReportDto.cs
Normal file
30
AsbCloudApp/Data/WellReport/SectionReportDto.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using AsbCloudApp.Data.Subsystems;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Data.WellReport;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Показатели по секции
|
||||||
|
/// </summary>
|
||||||
|
public class SectionReportDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Идентификатор секции
|
||||||
|
/// </summary>
|
||||||
|
public int IdSection { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Наработка подсистем
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<SubsystemStatDto> SubsystemsStat { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Режимы бурения
|
||||||
|
/// </summary>
|
||||||
|
public PlanFactDto<OperatingModeDto> OperatingMode { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Бурение по уставкам
|
||||||
|
/// </summary>
|
||||||
|
public DrillingBySetpointsDto? DrillingBySetpoints { get; set; }
|
||||||
|
}
|
61
AsbCloudApp/Data/WellReport/WellReportDto.cs
Normal file
61
AsbCloudApp/Data/WellReport/WellReportDto.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using AsbCloudApp.Data.User;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Data.WellReport;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Отчёт по скважине
|
||||||
|
/// </summary>
|
||||||
|
public class WellReportDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Информация о скважине
|
||||||
|
/// </summary>
|
||||||
|
public WellDto Well { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата начала бурения
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset? DateFrom { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата окончания бурения
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset? DateTo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дни бурения
|
||||||
|
/// </summary>
|
||||||
|
public PlanFactDto<double?> Days { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Проектная глубина
|
||||||
|
/// </summary>
|
||||||
|
public PlanFactDto<double?> WellBoreDepth { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Вертикальная глубина
|
||||||
|
/// </summary>
|
||||||
|
public PlanFactDto<double?> VerticalDepth { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дни бурения без НПВ
|
||||||
|
/// </summary>
|
||||||
|
public double WithoutNtpDays { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Контакты
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<ContactDto> Contacts { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Показатели по секциям
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<SectionReportDto> SectionReports { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Показатели по бурильщикам
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<DrillerReportDto> DrillerReports { get; set; } = [];
|
||||||
|
}
|
19
AsbCloudApp/Services/WellReport/IWellReportExportService.cs
Normal file
19
AsbCloudApp/Services/WellReport/IWellReportExportService.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Services.WellReport;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Сервис экспорта отчёта
|
||||||
|
/// </summary>
|
||||||
|
public interface IWellReportExportService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Экспортировать
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idWell"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<(string Name, Stream File)?> ExportAsync(int idWell, CancellationToken token);
|
||||||
|
}
|
19
AsbCloudApp/Services/WellReport/IWellReportService.cs
Normal file
19
AsbCloudApp/Services/WellReport/IWellReportService.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AsbCloudApp.Data.WellReport;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Services.WellReport;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Сервис формирования отчёта
|
||||||
|
/// </summary>
|
||||||
|
public interface IWellReportService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Сформировать
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idWell"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<WellReportDto?> GetAsync(int idWell, CancellationToken token);
|
||||||
|
}
|
335
AsbCloudInfrastructure.Tests/Services/WellReportServiceTest.cs
Normal file
335
AsbCloudInfrastructure.Tests/Services/WellReportServiceTest.cs
Normal file
@ -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<WellOperationDto> 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<TrajectoryGeoPlanDto> trajectoryPlanRepository;
|
||||||
|
private readonly ITrajectoryRepository<TrajectoryGeoFactDto> trajectoryFactRepository;
|
||||||
|
|
||||||
|
private readonly IChangeLogRepository<ProcessMapPlanRotorDto, ProcessMapPlanBaseRequestWithWell>
|
||||||
|
processMapPlanRotorRepository;
|
||||||
|
|
||||||
|
private readonly IScheduleRepository scheduleRepository;
|
||||||
|
|
||||||
|
private readonly WellReportService wellReportService;
|
||||||
|
|
||||||
|
public WellReportServiceTest()
|
||||||
|
{
|
||||||
|
wellService = Substitute.For<IWellService>();
|
||||||
|
wellOperationService = Substitute.For<IWellOperationService>();
|
||||||
|
wellContactService = Substitute.For<IWellContactService>();
|
||||||
|
processMapReportDrillingService = Substitute.For<IProcessMapReportDrillingService>();
|
||||||
|
subsystemService = Substitute.For<ISubsystemService>();
|
||||||
|
trajectoryPlanRepository = Substitute.For<ITrajectoryRepository<TrajectoryGeoPlanDto>>();
|
||||||
|
trajectoryFactRepository = Substitute.For<ITrajectoryRepository<TrajectoryGeoFactDto>>();
|
||||||
|
processMapPlanRotorRepository =
|
||||||
|
Substitute.For<IChangeLogRepository<ProcessMapPlanRotorDto, ProcessMapPlanBaseRequestWithWell>>();
|
||||||
|
|
||||||
|
scheduleRepository = Substitute.For<IScheduleRepository>();
|
||||||
|
|
||||||
|
wellService.GetOrDefaultAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.ReturnsForAnyArgs(Well);
|
||||||
|
|
||||||
|
wellOperationService
|
||||||
|
.GetAsync(Arg.Is<WellOperationRequest>(x => x.OperationType == WellOperation.IdOperationTypeFact),
|
||||||
|
Arg.Any<CancellationToken>())
|
||||||
|
.Returns(WellOperations.Where(x => x.IdType == WellOperation.IdOperationTypeFact));
|
||||||
|
|
||||||
|
wellOperationService
|
||||||
|
.GetAsync(Arg.Is<WellOperationRequest>(x => x.OperationType == WellOperation.IdOperationTypePlan),
|
||||||
|
Arg.Any<CancellationToken>())
|
||||||
|
.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<WellContactRequest>(), Arg.Any<CancellationToken>())
|
||||||
|
.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<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.ReturnsForAnyArgs(new[] { planTrajectory });
|
||||||
|
|
||||||
|
trajectoryFactRepository.GetAsync(Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.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<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.ReturnsForAnyArgs(schedules);
|
||||||
|
|
||||||
|
//act
|
||||||
|
var result = await wellReportService.GetAsync(1, CancellationToken.None);
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal(2, result.DrillerReports.Count());
|
||||||
|
}
|
||||||
|
}
|
@ -53,6 +53,7 @@
|
|||||||
<EmbeddedResource Include="Services\ProcessMaps\Report\ProcessMapReportTemplate.xlsx" />
|
<EmbeddedResource Include="Services\ProcessMaps\Report\ProcessMapReportTemplate.xlsx" />
|
||||||
<EmbeddedResource Include="Services\WellOperations\Templates\WellOperationFactTemplate.xlsx" />
|
<EmbeddedResource Include="Services\WellOperations\Templates\WellOperationFactTemplate.xlsx" />
|
||||||
<EmbeddedResource Include="Services\WellOperations\Templates\WellOperationPlanTemplate.xlsx" />
|
<EmbeddedResource Include="Services\WellOperations\Templates\WellOperationPlanTemplate.xlsx" />
|
||||||
|
<EmbeddedResource Include="Services\WellReport\WellReport.xlsx" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -47,6 +47,8 @@ using Microsoft.Extensions.Caching.Memory;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System;
|
using System;
|
||||||
|
using AsbCloudApp.Services.WellReport;
|
||||||
|
using AsbCloudInfrastructure.Services.WellReport;
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure;
|
namespace AsbCloudInfrastructure;
|
||||||
|
|
||||||
@ -470,6 +472,8 @@ public static class DependencyInjection
|
|||||||
services.AddTransient<ITrajectoryEditableRepository<TrajectoryGeoPlanDto>, TrajectoryEditableRepository<TrajectoryPlan, TrajectoryGeoPlanDto>>();
|
services.AddTransient<ITrajectoryEditableRepository<TrajectoryGeoPlanDto>, TrajectoryEditableRepository<TrajectoryPlan, TrajectoryGeoPlanDto>>();
|
||||||
services.AddTransient<ITrajectoryEditableRepository<TrajectoryGeoFactDto>, TrajectoryEditableRepository<TrajectoryFact, TrajectoryGeoFactDto>>();
|
services.AddTransient<ITrajectoryEditableRepository<TrajectoryGeoFactDto>, TrajectoryEditableRepository<TrajectoryFact, TrajectoryGeoFactDto>>();
|
||||||
services.AddTransient<ITrajectoryNnbRepository, TrajectoryNnbRepository>();
|
services.AddTransient<ITrajectoryNnbRepository, TrajectoryNnbRepository>();
|
||||||
|
services.AddTransient<ITrajectoryRepository<TrajectoryGeoPlanDto>, TrajectoryEditableRepository<TrajectoryPlan, TrajectoryGeoPlanDto>>();
|
||||||
|
services.AddTransient<ITrajectoryRepository<TrajectoryGeoFactDto>, TrajectoryEditableRepository<TrajectoryFact, TrajectoryGeoFactDto>>();
|
||||||
services.AddTransient<IFaqRepository, FaqRepository>();
|
services.AddTransient<IFaqRepository, FaqRepository>();
|
||||||
services.AddTransient<ISlipsStatService, SlipsStatService>();
|
services.AddTransient<ISlipsStatService, SlipsStatService>();
|
||||||
services.AddTransient<IWellContactService, WellContactService>();
|
services.AddTransient<IWellContactService, WellContactService>();
|
||||||
@ -564,6 +568,9 @@ public static class DependencyInjection
|
|||||||
services.AddTransient<WellOperationParserFactory>();
|
services.AddTransient<WellOperationParserFactory>();
|
||||||
services.AddTransient<WellOperationExportServiceFactory>();
|
services.AddTransient<WellOperationExportServiceFactory>();
|
||||||
|
|
||||||
|
services.AddTransient<IWellReportService, WellReportService>();
|
||||||
|
services.AddTransient<IWellReportExportService, WellReportExportService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ namespace AsbCloudInfrastructure.Repository;
|
|||||||
public class WellOperationRepository : CrudRepositoryBase<WellOperationBaseDto, WellOperation>,
|
public class WellOperationRepository : CrudRepositoryBase<WellOperationBaseDto, WellOperation>,
|
||||||
IWellOperationRepository
|
IWellOperationRepository
|
||||||
{
|
{
|
||||||
|
private const string keyCacheTemplate = "OperationsBySectionSummaries_{0}";
|
||||||
private const string cacheKeyWellOperations = "FirstAndLastFactWellsOperations";
|
private const string cacheKeyWellOperations = "FirstAndLastFactWellsOperations";
|
||||||
private readonly IMemoryCache memoryCache;
|
private readonly IMemoryCache memoryCache;
|
||||||
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
|
private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
|
||||||
@ -109,13 +110,26 @@ public class WellOperationRepository : CrudRepositoryBase<WellOperationBaseDto,
|
|||||||
|
|
||||||
public async Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token)
|
public async Task<IEnumerable<SectionByOperationsDto>> GetSectionsAsync(IEnumerable<int> idsWells, CancellationToken token)
|
||||||
{
|
{
|
||||||
const string keyCacheSections = "OperationsBySectionSummarties";
|
var result = new List<SectionByOperationsDto>();
|
||||||
|
var notFoundIds = new List<int>();
|
||||||
|
|
||||||
var cache = await memoryCache.GetOrCreateAsync(keyCacheSections, async (entry) =>
|
foreach (var idWell in idsWells)
|
||||||
{
|
{
|
||||||
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
|
var cacheKey = string.Format(keyCacheTemplate, idWell);
|
||||||
|
if (memoryCache.TryGetValue<IEnumerable<SectionByOperationsDto>>(cacheKey, out var section))
|
||||||
|
{
|
||||||
|
result.AddRange(section!);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
notFoundIds.Add(idWell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notFoundIds.Count != 0)
|
||||||
|
{
|
||||||
var query = dbContext.Set<WellOperation>()
|
var query = dbContext.Set<WellOperation>()
|
||||||
|
.Where(operation => notFoundIds.Contains( operation.IdWell))
|
||||||
.GroupBy(operation => new
|
.GroupBy(operation => new
|
||||||
{
|
{
|
||||||
operation.IdWell,
|
operation.IdWell,
|
||||||
@ -129,16 +143,10 @@ public class WellOperationRepository : CrudRepositoryBase<WellOperationBaseDto,
|
|||||||
group.Key.IdType,
|
group.Key.IdType,
|
||||||
group.Key.IdWellSectionType,
|
group.Key.IdWellSectionType,
|
||||||
group.Key.Caption,
|
group.Key.Caption,
|
||||||
|
|
||||||
First = group
|
First = group
|
||||||
.OrderBy(operation => operation.DateStart)
|
.OrderBy(operation => operation.DateStart)
|
||||||
.Select(operation => new
|
.Select(operation => new { operation.DateStart, operation.DepthStart, })
|
||||||
{
|
|
||||||
operation.DateStart,
|
|
||||||
operation.DepthStart,
|
|
||||||
})
|
|
||||||
.First(),
|
.First(),
|
||||||
|
|
||||||
Last = group
|
Last = group
|
||||||
.OrderByDescending(operation => operation.DateStart)
|
.OrderByDescending(operation => operation.DateStart)
|
||||||
.Select(operation => new
|
.Select(operation => new
|
||||||
@ -148,32 +156,36 @@ public class WellOperationRepository : CrudRepositoryBase<WellOperationBaseDto,
|
|||||||
operation.DepthEnd,
|
operation.DepthEnd,
|
||||||
})
|
})
|
||||||
.First(),
|
.First(),
|
||||||
})
|
|
||||||
.Where(s => idsWells.Contains(s.IdWell));
|
|
||||||
var dbData = await query.ToArrayAsync(token);
|
|
||||||
var sections = dbData.Select(
|
|
||||||
item => new SectionByOperationsDto
|
|
||||||
{
|
|
||||||
IdWell = item.IdWell,
|
|
||||||
IdType = item.IdType,
|
|
||||||
IdWellSectionType = item.IdWellSectionType,
|
|
||||||
|
|
||||||
Caption = item.Caption,
|
|
||||||
|
|
||||||
DateStart = item.First.DateStart,
|
|
||||||
DepthStart = item.First.DepthStart,
|
|
||||||
|
|
||||||
DateEnd = item.Last.DateStart.AddHours(item.Last.DurationHours),
|
|
||||||
DepthEnd = item.Last.DepthEnd,
|
|
||||||
})
|
|
||||||
.ToArray()
|
|
||||||
.AsEnumerable();
|
|
||||||
|
|
||||||
entry.Value = sections;
|
|
||||||
return sections;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return cache!;
|
var entities = await query.ToArrayAsync(token);
|
||||||
|
var dtos = entities.Select(
|
||||||
|
entity => new SectionByOperationsDto
|
||||||
|
{
|
||||||
|
IdWell = entity.IdWell,
|
||||||
|
IdType = entity.IdType,
|
||||||
|
IdWellSectionType = entity.IdWellSectionType,
|
||||||
|
Caption = entity.Caption,
|
||||||
|
DateStart = entity.First.DateStart,
|
||||||
|
DepthStart = entity.First.DepthStart,
|
||||||
|
DateEnd = entity.Last.DateStart.AddHours(entity.Last.DurationHours),
|
||||||
|
DepthEnd = entity.Last.DepthEnd,
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
result.AddRange(dtos);
|
||||||
|
|
||||||
|
var groupedByWellDtos = dtos
|
||||||
|
.GroupBy(dto => dto.IdWell);
|
||||||
|
|
||||||
|
foreach (var group in groupedByWellDtos)
|
||||||
|
{
|
||||||
|
var cacheKey = string.Format(keyCacheTemplate, group.Key);
|
||||||
|
memoryCache.Set(cacheKey, group.AsEnumerable(), TimeSpan.FromMinutes(30));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken)
|
public async Task<DatesRangeDto?> GetDatesRangeAsync(int idWell, int idType, CancellationToken cancellationToken)
|
||||||
|
BIN
AsbCloudInfrastructure/Services/WellReport/WellReport.xlsx
Normal file
BIN
AsbCloudInfrastructure/Services/WellReport/WellReport.xlsx
Normal file
Binary file not shown.
@ -0,0 +1,386 @@
|
|||||||
|
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<int, int> PlanOperatingModeRows = new Dictionary<int, int>()
|
||||||
|
{
|
||||||
|
{ 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<int, int> FactOperatingModeRows = new Dictionary<int, int>
|
||||||
|
{
|
||||||
|
{ 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<int, int> SubsystemRows = new Dictionary<int, int>()
|
||||||
|
{
|
||||||
|
{ 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<int, int> SetpointsRows = new Dictionary<int, int>()
|
||||||
|
{
|
||||||
|
{ 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<ContactDto> contacts)
|
||||||
|
{
|
||||||
|
var positionsByCompanyType = new Dictionary<int, string>()
|
||||||
|
{
|
||||||
|
{ 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<DrillerReportDto> 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<SectionReportDto> 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<SubsystemStatDto> 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<OperatingModeDto> 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))
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FactOperatingModeRows.TryGetValue(idSection, out var factRow))
|
||||||
|
{
|
||||||
|
sheet.Cell(factRow, depthStartColumn).SetCellValue(operatingMode.Fact?.DepthStart);
|
||||||
|
sheet.Cell(factRow, depthEndColumn).SetCellValue(operatingMode.Fact?.DepthEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
239
AsbCloudInfrastructure/Services/WellReport/WellReportService.cs
Normal file
239
AsbCloudInfrastructure/Services/WellReport/WellReportService.cs
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
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<TrajectoryGeoPlanDto> trajectoryPlanRepository;
|
||||||
|
private readonly ITrajectoryRepository<TrajectoryGeoFactDto> trajectoryFactRepository;
|
||||||
|
|
||||||
|
private readonly IChangeLogRepository<ProcessMapPlanRotorDto, ProcessMapPlanBaseRequestWithWell>
|
||||||
|
processMapPlanRotorRepository;
|
||||||
|
|
||||||
|
private readonly IScheduleRepository scheduleRepository;
|
||||||
|
|
||||||
|
private IEnumerable<WellOperationDto> factWellOperations;
|
||||||
|
private IEnumerable<WellOperationDto> planWellOperations;
|
||||||
|
|
||||||
|
public WellReportService(IWellService wellService,
|
||||||
|
IWellOperationService wellOperationService,
|
||||||
|
IWellContactService wellContactService,
|
||||||
|
IProcessMapReportDrillingService processMapReportDrillingService,
|
||||||
|
ISubsystemService subsystemService,
|
||||||
|
ITrajectoryRepository<TrajectoryGeoPlanDto> trajectoryPlanRepository,
|
||||||
|
ITrajectoryRepository<TrajectoryGeoFactDto> trajectoryFactRepository,
|
||||||
|
IChangeLogRepository<ProcessMapPlanRotorDto, ProcessMapPlanBaseRequestWithWell> 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<WellReportDto?> 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 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.Contains(o.IdCategory));
|
||||||
|
|
||||||
|
return new WellReportDto
|
||||||
|
{
|
||||||
|
Well = well,
|
||||||
|
DateFrom = firstFactOperation?.DateStart,
|
||||||
|
DateTo = lastPlanOperation?.DateStart.AddHours(lastPlanOperation.DurationHours),
|
||||||
|
Days = new PlanFactDto<double?>
|
||||||
|
{
|
||||||
|
Plan = planWellOperations.Sum(x => x.DurationHours) / 24,
|
||||||
|
Fact = factWellOperations.Sum(x => x.DurationHours) / 24
|
||||||
|
},
|
||||||
|
WellBoreDepth = new PlanFactDto<double?>
|
||||||
|
{
|
||||||
|
Plan = planWellOperations.MaxOrDefault(x => x.DepthEnd),
|
||||||
|
Fact = factWellOperations.MaxOrDefault(x => x.DepthEnd)
|
||||||
|
},
|
||||||
|
VerticalDepth = new PlanFactDto<double?>
|
||||||
|
{
|
||||||
|
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<IEnumerable<SectionReportDto>> GetSectionReportsAsync(int idWell, CancellationToken token)
|
||||||
|
{
|
||||||
|
var factWellOperationsBySection = factWellOperations.GroupBy(x => x.IdWellSectionType)
|
||||||
|
.ToDictionary(x => x.Key, x => x.AsEnumerable());
|
||||||
|
|
||||||
|
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 idsSection = factWellOperationsBySection.Keys
|
||||||
|
.Concat(processMapPlanRotorBySection.Keys)
|
||||||
|
.Concat(processMapReportBySection.Keys)
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
var sectionReports = new List<SectionReportDto>();
|
||||||
|
|
||||||
|
foreach (var idSection in idsSection)
|
||||||
|
{
|
||||||
|
var sectionReport = new SectionReportDto
|
||||||
|
{
|
||||||
|
IdSection = idSection,
|
||||||
|
OperatingMode = new PlanFactDto<OperatingModeDto>()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (factWellOperationsBySection.TryGetValue(idSection, out var factOperations))
|
||||||
|
{
|
||||||
|
var subsystemRequest = new SubsystemRequest
|
||||||
|
{
|
||||||
|
IdWell = idWell,
|
||||||
|
GeDepth = factOperations.Min(y => y.DepthStart),
|
||||||
|
LeDepth = factOperations.Max(y => y.DepthEnd)
|
||||||
|
};
|
||||||
|
|
||||||
|
sectionReport.SubsystemsStat = await subsystemService.GetStatAsync(subsystemRequest, token);
|
||||||
|
sectionReport.OperatingMode.Fact = new OperatingModeDto
|
||||||
|
{
|
||||||
|
DepthStart = factOperations.Min(w => w.DepthStart),
|
||||||
|
DepthEnd = factOperations.Max(w => w.DepthEnd)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processMapPlanRotorBySection.TryGetValue(idSection, 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(idSection, 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<IEnumerable<DrillerReportDto>> GetDrillerReportsAsync(int idWell, CancellationToken token)
|
||||||
|
{
|
||||||
|
var schedules = await scheduleRepository.GetByIdWellAsync(idWell, token);
|
||||||
|
|
||||||
|
var result = new List<DrillerReportDto>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -15,8 +15,11 @@ public static class XLExtentions
|
|||||||
workbook.Worksheets.FirstOrDefault(ws => string.Equals(ws.Name.Trim(), sheetName.Trim(), StringComparison.CurrentCultureIgnoreCase))
|
workbook.Worksheets.FirstOrDefault(ws => string.Equals(ws.Name.Trim(), sheetName.Trim(), StringComparison.CurrentCultureIgnoreCase))
|
||||||
?? throw new FileFormatException(string.Format(NotFoundSheetTemplate, sheetName));
|
?? throw new FileFormatException(string.Format(NotFoundSheetTemplate, sheetName));
|
||||||
|
|
||||||
public static IXLCell SetCellValue<T>(this IXLCell cell, T value, string? format = null)
|
public static IXLCell SetCellValue<T>(this IXLCell cell, T? value, string? format = null)
|
||||||
{
|
{
|
||||||
|
if (value == null)
|
||||||
|
return cell;
|
||||||
|
|
||||||
if (value is DateTime || value is DateTimeOffset)
|
if (value is DateTime || value is DateTimeOffset)
|
||||||
{
|
{
|
||||||
cell.Style.DateFormat.Format = format ?? "DD.MM.YYYY HH:MM:SS";
|
cell.Style.DateFormat.Format = format ?? "DD.MM.YYYY HH:MM:SS";
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
using AsbCloudApp.Data;
|
using AsbCloudApp.Data;
|
||||||
using AsbCloudApp.Exceptions;
|
|
||||||
using AsbCloudApp.Services;
|
using AsbCloudApp.Services;
|
||||||
using AsbCloudDb.Model;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AsbCloudApp.Services.WellReport;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace AsbCloudWebApi.Controllers;
|
namespace AsbCloudWebApi.Controllers;
|
||||||
|
|
||||||
@ -20,10 +20,12 @@ namespace AsbCloudWebApi.Controllers;
|
|||||||
public class WellController : ControllerBase
|
public class WellController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly IWellService wellService;
|
private readonly IWellService wellService;
|
||||||
|
private readonly IWellReportExportService wellReportExportService;
|
||||||
|
|
||||||
public WellController(IWellService wellService)
|
public WellController(IWellService wellService, IWellReportExportService wellReportExportService)
|
||||||
{
|
{
|
||||||
this.wellService = wellService;
|
this.wellService = wellService;
|
||||||
|
this.wellReportExportService = wellReportExportService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -149,4 +151,25 @@ public class WellController : ControllerBase
|
|||||||
return Ok(result);
|
return Ok(result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: навзание пока такое. У нас в API уже есть метод с такой сигнатурой.
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить отчёт по скважине
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idWell"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("{idWell}/report/export")]
|
||||||
|
[ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
public async Task<IActionResult> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user