forked from ddrilling/AsbCloudServer
370 lines
16 KiB
C#
370 lines
16 KiB
C#
using AsbCloudApp.Data;
|
|
using AsbCloudApp.Data.DetectedOperation;
|
|
using AsbCloudApp.Data.ProcessMaps;
|
|
using AsbCloudApp.Data.Subsystems;
|
|
using AsbCloudApp.Exceptions;
|
|
using AsbCloudApp.Repositories;
|
|
using AsbCloudApp.Requests;
|
|
using AsbCloudApp.Services;
|
|
using AsbCloudDb.Model;
|
|
using AsbCloudInfrastructure.Services.DetectOperations;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace AsbCloudInfrastructure.Services.Subsystems;
|
|
|
|
public class SubsystemService : ISubsystemService
|
|
{
|
|
private const int IdSubsystemAPD = 1;
|
|
private const int IdSubsystemAPDRotor = 11;
|
|
private const int IdSubsystemAPDSlide = 12;
|
|
private const int IdSubsystemOscillation = 65536;
|
|
|
|
private readonly ICrudRepository<SubsystemDto> subsystemRepository;
|
|
|
|
private readonly IWellService wellService;
|
|
private readonly IDetectedOperationService detectedOperationService;
|
|
private readonly IScheduleRepository scheduleRepository;
|
|
private readonly ITelemetryDataSaubService telemetryDataSaubService;
|
|
private readonly ITelemetryService telemetryService;
|
|
private readonly IChangeLogRepository<ProcessMapPlanSubsystemsDto, ProcessMapPlanBaseRequestWithWell> processMapPlanRepository;
|
|
|
|
private IDictionary<int, SubsystemDto> subsystems = new Dictionary<int, SubsystemDto>();
|
|
|
|
public SubsystemService(ICrudRepository<SubsystemDto> subsystemRepository,
|
|
IWellService wellService,
|
|
IDetectedOperationService detectedOperationService,
|
|
IScheduleRepository scheduleRepository,
|
|
ITelemetryService telemetryService,
|
|
ITelemetryDataSaubService telemetryDataSaubService,
|
|
IChangeLogRepository<ProcessMapPlanSubsystemsDto, ProcessMapPlanBaseRequestWithWell> processMapPlanRepository)
|
|
{
|
|
this.wellService = wellService;
|
|
this.subsystemRepository = subsystemRepository;
|
|
this.detectedOperationService = detectedOperationService;
|
|
this.scheduleRepository = scheduleRepository;
|
|
this.telemetryService = telemetryService;
|
|
this.telemetryDataSaubService = telemetryDataSaubService;
|
|
this.processMapPlanRepository = processMapPlanRepository;
|
|
}
|
|
|
|
// получить расписания бурильщиков по скважинам // ScheduleRepository добавить новый метод
|
|
// [
|
|
// get telemetryId by idWell // IWellService.GetOrDefaultStatAsync
|
|
// получить detectedOperation by telemetry //detectedOperationService.GetOperationsAsync
|
|
// сгруппировать по бурильщикам
|
|
// [
|
|
// рассчитать статистику // CalcStatAsync
|
|
// join driller from group
|
|
// ]
|
|
// join well (cluster, deposit)
|
|
// ]
|
|
public async Task<IEnumerable<DrillerDetectedOperationStatDto>> GetByWellsAsync(GetStatRequest request,
|
|
CancellationToken token)
|
|
{
|
|
var result = new List<DrillerDetectedOperationStatDto>();
|
|
var schedulePage = await scheduleRepository.GetPageAsync(request, token);
|
|
var wells = await wellService.GetAsync(new WellRequest { Ids = request.IdsWells }, token);
|
|
|
|
foreach (var schedule in schedulePage)
|
|
{
|
|
var idWell = schedule.IdWell;
|
|
var well = wells.FirstOrDefault(w => w.Id == idWell)!;
|
|
|
|
var byWellRequest = new DetectedOperationByWellRequest(idWell, new DetectedOperationRequest());
|
|
|
|
var detectedOperations = await detectedOperationService
|
|
.GetOperationsAsync(byWellRequest, token);
|
|
var detectedOperationsByCurrentDriller = detectedOperations.Where(d => d.Driller?.Id == schedule.IdDriller);
|
|
|
|
var drillerOperationsStat = await CalcStatAsync(detectedOperationsByCurrentDriller, token);
|
|
var dto = new DrillerDetectedOperationStatDto
|
|
{
|
|
Statistic = drillerOperationsStat,
|
|
Schedule = schedule,
|
|
Well = well,
|
|
};
|
|
result.Add(dto);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<IEnumerable<SubsystemStatDto>> GetStatAsync(SubsystemRequest request, CancellationToken token)
|
|
{
|
|
var well = await wellService.GetOrDefaultAsync(request.IdWell, token)
|
|
?? throw new ArgumentInvalidException(nameof(request.IdWell),
|
|
$"Well Id: {request.IdWell} does not exist");
|
|
|
|
if (!well.IdTelemetry.HasValue)
|
|
return Enumerable.Empty<SubsystemStatDto>();
|
|
|
|
var detectedOperationSummaryRequest = new DetectedOperationByWellRequest
|
|
{
|
|
IdWell = request.IdWell,
|
|
IdsCategories = WellOperationCategory.MechanicalDrillingSubIds,
|
|
|
|
GeDateStart = request.GeDate,
|
|
LeDateEnd = request.LeDate,
|
|
|
|
GeDepthStart = request.GeDepth,
|
|
LeDepthEnd = request.LeDepth,
|
|
};
|
|
|
|
var operations = await detectedOperationService.GetOperationsAsync(detectedOperationSummaryRequest,
|
|
token);
|
|
|
|
if (request.IdDriller.HasValue)
|
|
operations = operations.Where(o => o.Driller is not null && o.Driller.Id == request.IdDriller.Value);
|
|
|
|
if (!operations.Any())
|
|
return Enumerable.Empty<SubsystemStatDto>();
|
|
|
|
var stat = await CalcStatAsync(operations, token);
|
|
return stat;
|
|
}
|
|
|
|
public async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(IEnumerable<int> wellIds,
|
|
CancellationToken token)
|
|
{
|
|
var activeWells = await wellService.GetAsync(new() { Ids = wellIds, IdState = 1 }, token);
|
|
var result = await GetStatAsync(activeWells, null, null, token);
|
|
return result;
|
|
}
|
|
|
|
public async Task<IEnumerable<SubsystemPlanFactStatDto>> GetStatPlanFactByWellsAsync(SubsystemPlanFactRequest request, CancellationToken token)
|
|
{
|
|
var telemetriesDict = telemetryService
|
|
.GetOrDefaultTelemetriesByIdsWells(request.IdsWell)
|
|
.ToDictionary(t => t.Id, t => t.IdWell);
|
|
|
|
var geDate = request.GeDate ?? SubsystemPlanFactRequest.ValidationMinDate;
|
|
var leDate = request.LeDate ?? DateTimeOffset.UtcNow;
|
|
var groupedTelemetryDataSaub = await telemetryDataSaubService.GetMinAndMaxWellDepths(telemetriesDict.Keys, geDate, leDate, token);
|
|
|
|
var result = new List<SubsystemPlanFactStatDto>();
|
|
|
|
foreach (var telemetryDataSaubItem in groupedTelemetryDataSaub)
|
|
{
|
|
var idWell = telemetriesDict.GetValueOrDefault(telemetryDataSaubItem.Key);
|
|
if (!idWell.HasValue)
|
|
continue;
|
|
|
|
var telemetryDataSaubInfo = telemetryDataSaubItem.Value;
|
|
var requestProcessMapPlan = new ProcessMapPlanBaseRequestWithWell(idWell.Value, telemetryDataSaubInfo.MinDepth, telemetryDataSaubInfo.MaxDepth);
|
|
var processMapPlanSubsystems = await processMapPlanRepository.GetCurrent(requestProcessMapPlan, token);
|
|
|
|
foreach (var processMapPlanSubsystem in processMapPlanSubsystems)
|
|
{
|
|
var stat = new SubsystemPlanFactStatDto();
|
|
stat.IdWell = processMapPlanSubsystem.IdWell;
|
|
stat.IdWellSectionType = processMapPlanSubsystem.IdWellSectionType;
|
|
stat.DepthStart = processMapPlanSubsystem.DepthStart < telemetryDataSaubInfo.MinDepth
|
|
? telemetryDataSaubInfo.MinDepth
|
|
: processMapPlanSubsystem.DepthStart;
|
|
stat.DepthEnd = processMapPlanSubsystem.DepthEnd > telemetryDataSaubInfo.MaxDepth
|
|
? telemetryDataSaubInfo.MaxDepth
|
|
: processMapPlanSubsystem.DepthEnd;
|
|
stat.AutoRotorPlan = processMapPlanSubsystem.AutoRotor;
|
|
stat.AutoSlidePlan = processMapPlanSubsystem.AutoSlide;
|
|
stat.AutoOscillationPlan = processMapPlanSubsystem.AutoOscillation;
|
|
|
|
var subsystemRequest = new SubsystemRequest()
|
|
{
|
|
IdWell = telemetriesDict.GetValueOrDefault(telemetryDataSaubItem.Key)!.Value,
|
|
GeDepth = stat.DepthStart,
|
|
LeDepth = stat.DepthEnd
|
|
};
|
|
var subsystemStatFact = await GetStatAsync(subsystemRequest, token);
|
|
var subsystemStatFactDict = subsystemStatFact.ToDictionary(s => s.IdSubsystem);
|
|
|
|
stat.AutoRotorFact = subsystemStatFactDict.GetValueOrDefault(IdSubsystemAPDRotor)?.KUsage * 100;
|
|
stat.AutoSlideFact = subsystemStatFactDict.GetValueOrDefault(IdSubsystemAPDSlide)?.KUsage * 100;
|
|
stat.AutoOscillationFact = subsystemStatFactDict.GetValueOrDefault(IdSubsystemOscillation)?.KUsage * 100;
|
|
|
|
result.Add(stat);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
private async Task<IEnumerable<SubsystemStatDto>> CalcStatAsync(
|
|
IEnumerable<DetectedOperationWithDrillerDto> operations, CancellationToken token)
|
|
{
|
|
if (!subsystems.Any())
|
|
subsystems = (await subsystemRepository.GetAllAsync(token)).ToDictionary(s => s.Id, s => s);
|
|
|
|
var oscillationStat = CalcOscillationStat(operations);
|
|
var apdStat = CalcApdStat(operations);
|
|
|
|
var stat = new List<SubsystemStatDto> { oscillationStat };
|
|
stat.AddRange(apdStat);
|
|
|
|
return stat;
|
|
}
|
|
|
|
private SubsystemStatDto CalcOscillationStat(IEnumerable<DetectedOperationWithDrillerDto> operations)
|
|
{
|
|
operations = operations.Where(o => o.IdCategory == WellOperationCategory.IdSlide);
|
|
|
|
var (sumDepthInterval, usedTimeHours, operationCount) = AggregateOperations(IdSubsystemOscillation, operations);
|
|
|
|
var oscillationStat = new SubsystemStatDto
|
|
{
|
|
IdSubsystem = IdSubsystemOscillation,
|
|
SubsystemName = subsystems.TryGetValue(IdSubsystemOscillation, out var subsystemDto)
|
|
? subsystemDto.Name
|
|
: "unknown",
|
|
UsedTimeHours = usedTimeHours,
|
|
SumOperationDepthInterval = operations.Sum(o => o.DepthEnd - o.DepthStart),
|
|
SumOperationDurationHours = operations.Sum(o => o.DurationMinutes / 60),
|
|
SumDepthInterval = sumDepthInterval,
|
|
OperationCount = operationCount,
|
|
};
|
|
if (oscillationStat.SumOperationDepthInterval != 0d)
|
|
oscillationStat.KUsage = oscillationStat.SumDepthInterval / oscillationStat.SumOperationDepthInterval;
|
|
|
|
return oscillationStat;
|
|
}
|
|
|
|
private IEnumerable<SubsystemStatDto> CalcApdStat(IEnumerable<DetectedOperationWithDrillerDto> operations)
|
|
{
|
|
var apdRotorAndSlide = operations
|
|
.Where(o => WellOperationCategory.MechanicalDrillingSubIds.Contains(o.IdCategory))
|
|
.GroupBy(o => o.IdCategory)
|
|
.Select(group =>
|
|
{
|
|
var idSubsystem = group.Key switch
|
|
{
|
|
WellOperationCategory.IdRotor => IdSubsystemAPDRotor,
|
|
WellOperationCategory.IdSlide => IdSubsystemAPDSlide,
|
|
_ => throw new ArgumentException($"IdCategory: {group.Key} does not supported in this method",
|
|
nameof(group.Key))
|
|
};
|
|
|
|
var (sumDepthInterval, usedTimeHours, operationCount) = AggregateOperations(idSubsystem, group);
|
|
|
|
var subsystemStat = new SubsystemStatDto
|
|
{
|
|
IdSubsystem = idSubsystem,
|
|
SubsystemName = subsystems.TryGetValue(idSubsystem, out var subsystemDto)
|
|
? subsystemDto.Name
|
|
: "unknown",
|
|
UsedTimeHours = usedTimeHours,
|
|
SumOperationDepthInterval = group.Sum(o => o.DepthEnd - o.DepthStart),
|
|
SumOperationDurationHours = group.Sum(o => o.DurationMinutes / 60),
|
|
SumDepthInterval = sumDepthInterval,
|
|
OperationCount = operationCount,
|
|
};
|
|
|
|
subsystemStat.KUsage = subsystemStat.SumDepthInterval / subsystemStat.SumOperationDepthInterval;
|
|
|
|
return subsystemStat;
|
|
});
|
|
|
|
if (!apdRotorAndSlide.Any())
|
|
return Enumerable.Empty<SubsystemStatDto>();
|
|
|
|
var apdSum = new SubsystemStatDto
|
|
{
|
|
IdSubsystem = IdSubsystemAPD,
|
|
SubsystemName =
|
|
subsystems.TryGetValue(IdSubsystemAPD, out var subsystemDto) ? subsystemDto.Name : "unknown",
|
|
UsedTimeHours = apdRotorAndSlide.Sum(part => part.UsedTimeHours),
|
|
SumOperationDepthInterval = apdRotorAndSlide.Sum(part => part.SumOperationDepthInterval),
|
|
SumOperationDurationHours = apdRotorAndSlide.Sum(part => part.SumOperationDurationHours),
|
|
SumDepthInterval = apdRotorAndSlide.Sum(part => part.SumDepthInterval),
|
|
OperationCount = apdRotorAndSlide.Sum(part => part.OperationCount),
|
|
};
|
|
|
|
apdSum.KUsage = apdSum.SumDepthInterval / apdSum.SumOperationDepthInterval;
|
|
|
|
var apdStat = new List<SubsystemStatDto> { apdSum };
|
|
apdStat.AddRange(apdRotorAndSlide);
|
|
|
|
return apdStat;
|
|
}
|
|
|
|
private static (double SumDepthInterval, double UsedTimeHours, int Count) AggregateOperations(int idSubsystem,
|
|
IEnumerable<DetectedOperationWithDrillerDto> operations) =>
|
|
idSubsystem switch
|
|
{
|
|
IdSubsystemAPDRotor => CalcOperationsByEnableSubsystems(operations, EnabledSubsystemsFlags.AutoRotor),
|
|
IdSubsystemAPDSlide => CalcOperationsByEnableSubsystems(operations,
|
|
EnabledSubsystemsFlags.AutoSlide | EnabledSubsystemsFlags.AutoOscillation),
|
|
IdSubsystemOscillation => CalcOperationsByEnableSubsystems(operations,
|
|
EnabledSubsystemsFlags.AutoOscillation),
|
|
_ => throw new ArgumentException($"IdSubsystem: {idSubsystem} does not supported in this method",
|
|
nameof(idSubsystem))
|
|
};
|
|
|
|
private static (double SumDepthInterval, double UsedTimeHours, int OperationCount) CalcOperationsByEnableSubsystems(
|
|
IEnumerable<DetectedOperationWithDrillerDto> operations,
|
|
EnabledSubsystemsFlags enabledSubsystems)
|
|
{
|
|
var filtered = operations.Where(o => enabledSubsystems.HasEnabledSubsystems(o.EnabledSubsystems));
|
|
|
|
var sumDepthInterval = filtered.Sum(o => o.DepthEnd - o.DepthStart);
|
|
var usedTimeHours = filtered.Sum(o => o.DurationMinutes / 60);
|
|
var operationCount = filtered.Count();
|
|
|
|
return (sumDepthInterval, usedTimeHours, operationCount);
|
|
}
|
|
|
|
private async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatAsync(IEnumerable<WellDto> wells,
|
|
DateTime? geDate,
|
|
DateTime? leDate,
|
|
CancellationToken token)
|
|
{
|
|
if (!wells.Any())
|
|
return Enumerable.Empty<SubsystemActiveWellStatDto>();
|
|
|
|
var idsTelemetries = wells
|
|
.Where(w => w.IdTelemetry is not null)
|
|
.Select(w => w.IdTelemetry!.Value)
|
|
.Distinct();
|
|
|
|
var wellsStat = new List<SubsystemActiveWellStatDto>();
|
|
|
|
foreach (var well in wells)
|
|
{
|
|
var hoursOffset = well.Timezone.Hours;
|
|
|
|
var geDateStartUtc = geDate?.ToUtcDateTimeOffset(hoursOffset);
|
|
|
|
var leDateUtc = leDate?.ToUtcDateTimeOffset(hoursOffset);
|
|
|
|
var request = new DetectedOperationByWellRequest
|
|
{
|
|
IdWell = well.Id,
|
|
IdsCategories = WellOperationCategory.MechanicalDrillingSubIds,
|
|
GeDateStart = geDateStartUtc,
|
|
LeDateEnd = leDateUtc,
|
|
};
|
|
|
|
var telemetryOperations = await detectedOperationService
|
|
.GetOperationsAsync(request, token);
|
|
|
|
var wellStat = new SubsystemActiveWellStatDto { Well = well };
|
|
|
|
if (!telemetryOperations.Any())
|
|
continue;
|
|
|
|
var subsystemStat = await CalcStatAsync(telemetryOperations, token);
|
|
|
|
if (!subsystemStat.Any())
|
|
continue;
|
|
|
|
wellStat.SubsystemAPD = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAPD);
|
|
wellStat.SubsystemOscillation = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemOscillation);
|
|
wellsStat.Add(wellStat);
|
|
}
|
|
|
|
return wellsStat;
|
|
}
|
|
} |