2021-08-24 10:59:10 +05:00
|
|
|
|
using AsbCloudApp.Data;
|
2021-08-24 16:47:10 +05:00
|
|
|
|
using AsbCloudApp.Services;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
using AsbCloudDb.Model;
|
|
|
|
|
using AsbCloudInfrastructure.Services.Cache;
|
2021-09-10 11:28:57 +05:00
|
|
|
|
using Mapster;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2021-10-09 20:16:22 +05:00
|
|
|
|
namespace AsbCloudInfrastructure.Services.WellOperationService
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2021-11-22 17:29:19 +05:00
|
|
|
|
public class OperationsStatService : IOperationsStatService
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
|
|
|
|
private readonly IAsbCloudDbContext db;
|
2021-08-25 11:13:56 +05:00
|
|
|
|
private readonly IWellService wellService;
|
2021-11-22 17:29:19 +05:00
|
|
|
|
private readonly ITelemetryService telemetryService;
|
2021-08-25 17:58:35 +05:00
|
|
|
|
private readonly CacheTable<WellSectionType> cacheSectionsTypes;
|
|
|
|
|
private readonly CacheTable<WellType> cacheWellType;
|
2021-08-25 11:13:56 +05:00
|
|
|
|
private readonly CacheTable<Cluster> cacheCluster;
|
2021-08-24 16:47:10 +05:00
|
|
|
|
private const int idOperationBhaAssembly = 1025;
|
|
|
|
|
private const int idOperationBhaDisassembly = 1026;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
private const int idOperationNonProductiveTime = 1043;
|
|
|
|
|
private const int idOperationDrilling = 1001;
|
|
|
|
|
private const int idOperationBhaDown = 1046;
|
|
|
|
|
private const int idOperationBhaUp = 1047;
|
2021-08-27 12:15:04 +05:00
|
|
|
|
private const int idOperationCasingDown = 1048;
|
|
|
|
|
private const int idOperationTypePlan = 0;
|
|
|
|
|
private const int idOperationTypeFact = 1;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
|
2021-11-22 17:29:19 +05:00
|
|
|
|
public OperationsStatService(IAsbCloudDbContext db, CacheDb cache, IWellService wellService,
|
|
|
|
|
ITelemetryService telemetryService)
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
|
|
|
|
this.db = db;
|
2021-08-25 11:13:56 +05:00
|
|
|
|
this.wellService = wellService;
|
2021-11-22 17:29:19 +05:00
|
|
|
|
this.telemetryService = telemetryService;
|
2021-08-25 17:58:35 +05:00
|
|
|
|
cacheSectionsTypes = cache.GetCachedTable<WellSectionType>((DbContext)db);
|
|
|
|
|
cacheWellType = cache.GetCachedTable<WellType>((DbContext)db);
|
2021-08-25 11:13:56 +05:00
|
|
|
|
cacheCluster = cache.GetCachedTable<Cluster>((DbContext)db);
|
2021-08-24 10:59:10 +05:00
|
|
|
|
}
|
2021-11-22 17:29:19 +05:00
|
|
|
|
|
|
|
|
|
public async Task<ClusterRopStatDto> GetRopStatByIdWellAsync(int idWell,
|
|
|
|
|
CancellationToken token)
|
|
|
|
|
{
|
|
|
|
|
return await GetRopStatAsync(idWell, token).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<ClusterRopStatDto> GetRopStatByUidAsync(string uid,
|
|
|
|
|
CancellationToken token)
|
|
|
|
|
{
|
2021-12-07 18:27:52 +05:00
|
|
|
|
var idWell = telemetryService.GetIdWellByTelemetryUid(uid);
|
2021-11-22 17:29:19 +05:00
|
|
|
|
|
|
|
|
|
if (idWell is null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
return await GetRopStatAsync((int)idWell, token).ConfigureAwait(false);
|
|
|
|
|
}
|
2021-08-24 10:59:10 +05:00
|
|
|
|
|
2021-10-12 18:06:47 +05:00
|
|
|
|
public async Task<StatClusterDto> GetStatClusterAsync(int idCluster, int idCompany, CancellationToken token = default)
|
2021-08-24 16:47:10 +05:00
|
|
|
|
{
|
2021-10-12 18:06:47 +05:00
|
|
|
|
var allWellsByCompany = await wellService.GetWellsByCompanyAsync(idCompany, token).ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
var idWellsByCompany = allWellsByCompany.Select(w=>w.Id).Distinct();
|
|
|
|
|
|
2021-09-02 11:42:05 +05:00
|
|
|
|
var wells = await db.Wells
|
|
|
|
|
.Include(w => w.WellOperations)
|
|
|
|
|
.Where(o => o.IdCluster == idCluster)
|
2021-10-12 18:06:47 +05:00
|
|
|
|
.Where(w => idWellsByCompany.Contains(w.Id))
|
|
|
|
|
.Select(w => w.Id)
|
2021-08-24 16:47:10 +05:00
|
|
|
|
.ToListAsync(token);
|
|
|
|
|
|
2021-10-12 18:06:47 +05:00
|
|
|
|
var statsWells = await GetWellsStatAsync(wells, token).ConfigureAwait(false);
|
2021-08-24 16:47:10 +05:00
|
|
|
|
|
2021-10-12 16:07:08 +05:00
|
|
|
|
var cluster = await cacheCluster.FirstOrDefaultAsync(c => c.Id == idCluster, token);
|
2021-08-24 16:47:10 +05:00
|
|
|
|
var statClusterDto = new StatClusterDto
|
|
|
|
|
{
|
2021-08-25 11:13:56 +05:00
|
|
|
|
Id = idCluster,
|
|
|
|
|
Caption = cluster.Caption,
|
|
|
|
|
StatsWells = statsWells,
|
2021-08-24 16:47:10 +05:00
|
|
|
|
};
|
|
|
|
|
return statClusterDto;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-12 16:07:08 +05:00
|
|
|
|
public async Task<IEnumerable<StatWellDto>> GetWellsStatAsync(IEnumerable<int> idWells, CancellationToken token)
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2021-10-12 16:07:08 +05:00
|
|
|
|
var wells = await db.Wells
|
|
|
|
|
.Include(w => w.WellOperations)
|
|
|
|
|
.Where(w => idWells.Contains(w.Id))
|
2021-08-24 10:59:10 +05:00
|
|
|
|
.AsNoTracking()
|
|
|
|
|
.ToListAsync(token);
|
|
|
|
|
|
2021-10-12 16:07:08 +05:00
|
|
|
|
var statsWells = new List<StatWellDto>(wells.Count);
|
2021-08-29 11:59:20 +05:00
|
|
|
|
|
2021-10-12 16:07:08 +05:00
|
|
|
|
foreach (var well in wells)
|
|
|
|
|
{
|
2022-03-02 16:21:07 +05:00
|
|
|
|
var statWellDto = await CalcWellStatAsync(well, token);
|
2021-10-12 16:07:08 +05:00
|
|
|
|
statsWells.Add(statWellDto);
|
|
|
|
|
}
|
|
|
|
|
return statsWells;
|
2021-08-25 11:13:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 16:21:07 +05:00
|
|
|
|
public async Task<StatWellDto> GetWellStatAsync(int idWell,
|
2021-08-25 11:13:56 +05:00
|
|
|
|
CancellationToken token = default)
|
|
|
|
|
{
|
2021-10-12 16:07:08 +05:00
|
|
|
|
var well = await db.Wells
|
|
|
|
|
.Include(w => w.WellOperations)
|
|
|
|
|
.FirstOrDefaultAsync(w => w.Id == idWell, token)
|
|
|
|
|
.ConfigureAwait(false);
|
2021-08-25 11:13:56 +05:00
|
|
|
|
|
2022-03-02 16:21:07 +05:00
|
|
|
|
var statWellDto = await CalcWellStatAsync(well, token);
|
2021-10-12 16:07:08 +05:00
|
|
|
|
return statWellDto;
|
|
|
|
|
}
|
2021-08-25 11:13:56 +05:00
|
|
|
|
|
2021-11-22 17:29:19 +05:00
|
|
|
|
private async Task<ClusterRopStatDto> GetRopStatAsync(int idWell, CancellationToken token)
|
|
|
|
|
{
|
|
|
|
|
var clusterWellsIds = await wellService.GetClusterWellsIdsAsync(idWell, token)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
if (clusterWellsIds is null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
var idLastSectionType = await (from o in db.WellOperations
|
|
|
|
|
where o.IdWell == idWell &&
|
|
|
|
|
o.IdType == 1
|
|
|
|
|
orderby o.DepthStart
|
|
|
|
|
select o.IdWellSectionType)
|
|
|
|
|
.LastOrDefaultAsync(token)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
|
2021-11-23 11:31:47 +05:00
|
|
|
|
if (idLastSectionType == default)
|
|
|
|
|
return null;
|
|
|
|
|
|
2021-11-22 17:29:19 +05:00
|
|
|
|
var operations = await (from o in db.WellOperations
|
|
|
|
|
where clusterWellsIds.Contains(o.IdWell) &&
|
|
|
|
|
o.IdType == 1 &&
|
|
|
|
|
o.IdWellSectionType == idLastSectionType
|
|
|
|
|
select o)
|
|
|
|
|
.ToListAsync(token)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
var statsList = clusterWellsIds.Select(clusterWellId =>
|
|
|
|
|
{
|
|
|
|
|
var currentWellOps = operations.Where(o => o.IdWell == clusterWellId);
|
2022-01-05 17:50:45 +05:00
|
|
|
|
var timezoneOffsetH = wellService.GetTimezone(clusterWellId).Hours;
|
|
|
|
|
var stat = CalcStat(currentWellOps, timezoneOffsetH);
|
2021-11-22 17:29:19 +05:00
|
|
|
|
return stat;
|
|
|
|
|
}).Where(c => c is not null);
|
|
|
|
|
|
|
|
|
|
if (!statsList.Any())
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
var clusterRops = new ClusterRopStatDto()
|
|
|
|
|
{
|
|
|
|
|
RopMax = statsList.Max(s => s.Rop),
|
|
|
|
|
RopAverage = statsList.Average(s => s.Rop)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return clusterRops;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 16:21:07 +05:00
|
|
|
|
private async Task<StatWellDto> CalcWellStatAsync(Well well, CancellationToken token = default)
|
2021-10-12 16:07:08 +05:00
|
|
|
|
{
|
2022-01-05 17:50:45 +05:00
|
|
|
|
var wellType = await cacheWellType.FirstOrDefaultAsync(t => t.Id == well.IdWellType, token);
|
2021-10-12 16:07:08 +05:00
|
|
|
|
var statWellDto = new StatWellDto()
|
2021-08-24 16:47:10 +05:00
|
|
|
|
{
|
2021-10-12 16:07:08 +05:00
|
|
|
|
Id = well.Id,
|
2021-08-25 11:13:56 +05:00
|
|
|
|
Caption = well.Caption,
|
2021-11-11 10:57:08 +05:00
|
|
|
|
WellType = wellType?.Caption ?? "",
|
2021-10-20 14:15:53 +05:00
|
|
|
|
IdState = well.IdState,
|
2021-10-20 12:52:31 +05:00
|
|
|
|
State = wellService.GetStateText(well.IdState),
|
2022-01-05 17:50:45 +05:00
|
|
|
|
LastTelemetryDate = wellService.GetLastTelemetryDate(well.Id).DateTime,
|
2021-08-24 16:47:10 +05:00
|
|
|
|
};
|
2021-10-12 16:07:08 +05:00
|
|
|
|
|
2021-10-13 15:53:43 +05:00
|
|
|
|
statWellDto.Companies = await wellService.GetCompaniesAsync(well.Id, token);
|
|
|
|
|
|
2021-10-13 15:45:24 +05:00
|
|
|
|
if (well.WellOperations is null)
|
|
|
|
|
return statWellDto;
|
|
|
|
|
|
|
|
|
|
var wellOperations = well.WellOperations
|
|
|
|
|
.OrderBy(o => o.DateStart)
|
|
|
|
|
.ThenBy(o => o.DepthEnd);
|
|
|
|
|
|
2021-10-12 16:07:08 +05:00
|
|
|
|
if (!wellOperations.Any())
|
|
|
|
|
return statWellDto;
|
|
|
|
|
|
2022-01-05 17:50:45 +05:00
|
|
|
|
var timezoneOffsetH = wellService.GetTimezone(well.Id).Hours;
|
|
|
|
|
statWellDto.Sections = CalcSectionsStats(wellOperations, timezoneOffsetH);
|
|
|
|
|
statWellDto.Total = GetStatTotal(wellOperations, well.IdState, timezoneOffsetH);
|
2021-10-12 16:07:08 +05:00
|
|
|
|
|
2021-08-24 16:47:10 +05:00
|
|
|
|
return statWellDto;
|
|
|
|
|
}
|
2021-08-24 10:59:10 +05:00
|
|
|
|
|
2022-01-05 17:50:45 +05:00
|
|
|
|
private IEnumerable<StatSectionDto> CalcSectionsStats(IEnumerable<WellOperation> operations, double timezoneOffsetH)
|
2021-08-24 16:47:10 +05:00
|
|
|
|
{
|
2021-08-25 11:13:56 +05:00
|
|
|
|
var sectionTypeIds = operations
|
|
|
|
|
.Select(o => o.IdWellSectionType)
|
|
|
|
|
.Distinct();
|
|
|
|
|
|
2021-08-25 17:58:35 +05:00
|
|
|
|
var sectionTypes = cacheSectionsTypes
|
2021-08-25 11:13:56 +05:00
|
|
|
|
.Where(s => sectionTypeIds.Contains(s.Id))
|
|
|
|
|
.ToDictionary(s => s.Id);
|
|
|
|
|
|
|
|
|
|
var sections = new List<StatSectionDto>(sectionTypes.Count);
|
2021-10-08 11:30:06 +05:00
|
|
|
|
var operationsPlan = operations.Where(o => o.IdType == idOperationTypePlan);
|
|
|
|
|
var operationsFact = operations.Where(o => o.IdType == idOperationTypeFact);
|
2021-08-24 10:59:10 +05:00
|
|
|
|
|
2021-08-25 11:13:56 +05:00
|
|
|
|
foreach ((var id, var sectionType) in sectionTypes)
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2021-10-08 11:30:06 +05:00
|
|
|
|
var section = new StatSectionDto
|
2021-08-25 11:13:56 +05:00
|
|
|
|
{
|
|
|
|
|
Id = id,
|
2021-09-10 11:28:57 +05:00
|
|
|
|
Caption = sectionType.Caption,
|
2022-01-05 17:50:45 +05:00
|
|
|
|
Plan = CalcSectionStat(operationsPlan, id, timezoneOffsetH),
|
|
|
|
|
Fact = CalcSectionStat(operationsFact, id, timezoneOffsetH),
|
2021-08-25 11:13:56 +05:00
|
|
|
|
};
|
2021-08-24 10:59:10 +05:00
|
|
|
|
sections.Add(section);
|
|
|
|
|
}
|
|
|
|
|
return sections;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-21 16:36:28 +05:00
|
|
|
|
private static PlanFactBase<StatOperationsDto> GetStatTotal(IEnumerable<WellOperation> operations,
|
2022-01-05 17:50:45 +05:00
|
|
|
|
int idWellState, double timezoneOffsetH)
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2021-10-08 11:30:06 +05:00
|
|
|
|
var operationsPlan = operations.Where(o => o.IdType == idOperationTypePlan);
|
|
|
|
|
var operationsFact = operations.Where(o => o.IdType == idOperationTypeFact);
|
2022-01-05 17:50:45 +05:00
|
|
|
|
var factEnd = CalcStat(operationsFact, timezoneOffsetH);
|
2022-01-25 15:00:17 +05:00
|
|
|
|
if (factEnd is not null && idWellState != 2)
|
2021-12-21 16:36:28 +05:00
|
|
|
|
factEnd.End = null;
|
2021-08-25 11:13:56 +05:00
|
|
|
|
var section = new PlanFactBase<StatOperationsDto>
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2022-01-05 17:50:45 +05:00
|
|
|
|
Plan = CalcStat(operationsPlan, timezoneOffsetH),
|
2021-12-21 16:36:28 +05:00
|
|
|
|
Fact = factEnd,
|
2021-08-24 10:59:10 +05:00
|
|
|
|
};
|
2021-08-24 16:47:10 +05:00
|
|
|
|
return section;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 17:50:45 +05:00
|
|
|
|
private static StatOperationsDto CalcSectionStat(IEnumerable<WellOperation> operations, int idSectionType, double timezoneOffsetH)
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2021-08-24 16:47:10 +05:00
|
|
|
|
var sectionOperations = operations
|
2021-08-24 10:59:10 +05:00
|
|
|
|
.Where(o => o.IdWellSectionType == idSectionType)
|
2021-10-08 11:30:06 +05:00
|
|
|
|
.OrderBy(o => o.DateStart)
|
|
|
|
|
.ThenBy(o => o.DepthStart);
|
2021-09-10 11:28:57 +05:00
|
|
|
|
|
2022-01-05 17:50:45 +05:00
|
|
|
|
return CalcStat(sectionOperations, timezoneOffsetH);
|
2021-08-24 16:47:10 +05:00
|
|
|
|
}
|
2021-12-21 16:36:28 +05:00
|
|
|
|
|
2022-01-05 17:50:45 +05:00
|
|
|
|
private static StatOperationsDto CalcStat(IEnumerable<WellOperation> operations, double timezoneOffsetH)
|
2021-08-24 16:47:10 +05:00
|
|
|
|
{
|
2021-08-29 12:05:43 +05:00
|
|
|
|
if (!operations.Any())
|
|
|
|
|
return null;
|
|
|
|
|
|
2022-01-05 17:50:45 +05:00
|
|
|
|
var races = GetCompleteRaces(operations, timezoneOffsetH);
|
2021-08-26 10:18:59 +05:00
|
|
|
|
|
2021-08-25 11:13:56 +05:00
|
|
|
|
var section = new StatOperationsDto
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2022-01-05 17:50:45 +05:00
|
|
|
|
Start = operations.FirstOrDefault()?.DateStart.ToRemoteDateTime(timezoneOffsetH),
|
|
|
|
|
End = operations.Max(o => o.DateStart.ToRemoteDateTime(timezoneOffsetH).AddHours(o.DurationHours)),
|
2021-10-08 11:30:06 +05:00
|
|
|
|
WellDepthStart = operations.Min(o => o.DepthStart),
|
|
|
|
|
WellDepthEnd = operations.Max(o => o.DepthStart),
|
2021-08-24 16:47:10 +05:00
|
|
|
|
Rop = CalcROP(operations),
|
2021-08-26 10:18:59 +05:00
|
|
|
|
RouteSpeed = CalcAvgRaceSpeed(races),
|
|
|
|
|
BhaDownSpeed = CalcBhaDownSpeed(races),
|
|
|
|
|
BhaUpSpeed = CalcBhaUpSpeed(races),
|
|
|
|
|
CasingDownSpeed = CalcCasingDownSpeed(operations),
|
|
|
|
|
NonProductiveHours = operations
|
|
|
|
|
.Where(o => o.IdCategory == idOperationNonProductiveTime)
|
2021-10-08 11:30:06 +05:00
|
|
|
|
.Sum(o => o.DurationHours),
|
2021-08-24 10:59:10 +05:00
|
|
|
|
};
|
|
|
|
|
return section;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-08 11:30:06 +05:00
|
|
|
|
private static double CalcROP(IEnumerable<WellOperation> operationsProps)
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
|
|
|
|
var drillingOperations = operationsProps.Where(o => o.IdCategory == idOperationDrilling);
|
|
|
|
|
var dDepth = 0d;
|
|
|
|
|
var dHours = 0d;
|
|
|
|
|
foreach (var operation in drillingOperations)
|
|
|
|
|
{
|
2021-10-08 11:30:06 +05:00
|
|
|
|
var deltaDepth = operation.DepthEnd - operation.DepthStart;
|
|
|
|
|
dDepth += deltaDepth;
|
|
|
|
|
dHours += operation.DurationHours;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
}
|
|
|
|
|
return dDepth / (dHours + double.Epsilon);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-08 11:30:06 +05:00
|
|
|
|
private static double CalcCasingDownSpeed(IEnumerable<WellOperation> operationsProps)
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2021-08-27 12:15:04 +05:00
|
|
|
|
var ops = operationsProps.Where(o => o.IdCategory == idOperationCasingDown);
|
2021-08-26 10:18:59 +05:00
|
|
|
|
var depth = 0d;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
var dHours = 0d;
|
2021-08-26 10:18:59 +05:00
|
|
|
|
foreach (var operation in ops)
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2021-10-08 11:30:06 +05:00
|
|
|
|
depth += operation.DepthStart;
|
|
|
|
|
dHours += operation.DurationHours;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
}
|
2021-08-26 10:18:59 +05:00
|
|
|
|
return depth / (dHours + double.Epsilon);
|
2021-08-24 10:59:10 +05:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 17:50:45 +05:00
|
|
|
|
private static IEnumerable<Race> GetCompleteRaces(IEnumerable<WellOperation> operations, double timezoneOffsetH)
|
2021-08-24 10:59:10 +05:00
|
|
|
|
{
|
2021-08-24 16:47:10 +05:00
|
|
|
|
var races = new List<Race>();
|
2021-08-25 17:58:35 +05:00
|
|
|
|
var iterator = operations
|
2021-10-08 11:30:06 +05:00
|
|
|
|
.OrderBy(o => o.DateStart)
|
2021-08-25 17:58:35 +05:00
|
|
|
|
.GetEnumerator();
|
2021-08-24 10:59:10 +05:00
|
|
|
|
while (iterator.MoveNext())
|
|
|
|
|
{
|
|
|
|
|
if (iterator.Current.IdCategory == idOperationBhaAssembly)
|
|
|
|
|
{
|
|
|
|
|
var race = new Race
|
|
|
|
|
{
|
2022-01-05 17:50:45 +05:00
|
|
|
|
StartDate = iterator.Current.DateStart.ToRemoteDateTime(timezoneOffsetH).AddHours(iterator.Current.DurationHours),
|
2021-10-08 11:30:06 +05:00
|
|
|
|
StartWellDepth = iterator.Current.DepthStart,
|
|
|
|
|
Operations = new List<WellOperation>(10),
|
2021-08-24 10:59:10 +05:00
|
|
|
|
};
|
|
|
|
|
while (iterator.MoveNext())
|
|
|
|
|
{
|
|
|
|
|
if (iterator.Current.IdCategory == idOperationNonProductiveTime)
|
|
|
|
|
{
|
2021-10-08 11:30:06 +05:00
|
|
|
|
race.NonProductiveHours += iterator.Current.DurationHours;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
}
|
|
|
|
|
if (iterator.Current.IdCategory == idOperationBhaDisassembly)
|
|
|
|
|
{
|
2022-01-05 17:50:45 +05:00
|
|
|
|
race.EndDate = iterator.Current.DateStart.ToRemoteDateTime(timezoneOffsetH);
|
2021-10-08 11:30:06 +05:00
|
|
|
|
race.EndWellDepth = iterator.Current.DepthStart;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
races.Add(race);
|
2021-08-25 17:58:35 +05:00
|
|
|
|
break;
|
2021-08-24 10:59:10 +05:00
|
|
|
|
}
|
2021-08-27 15:53:38 +05:00
|
|
|
|
race.Operations.Add(iterator.Current);
|
2021-08-24 10:59:10 +05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return races;
|
|
|
|
|
}
|
2021-08-26 10:18:59 +05:00
|
|
|
|
|
|
|
|
|
private static double CalcAvgRaceSpeed(IEnumerable<Race> races)
|
|
|
|
|
{
|
|
|
|
|
var dDepth = 0d;
|
|
|
|
|
var dHours = 0d;
|
|
|
|
|
foreach (var race in races)
|
|
|
|
|
{
|
|
|
|
|
dHours += race.DeltaHoursTimeNoNpt;
|
|
|
|
|
dDepth += race.DeltaDepth;
|
|
|
|
|
}
|
|
|
|
|
return dDepth / (dHours + double.Epsilon);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static double CalcBhaDownSpeed(IEnumerable<Race> races)
|
|
|
|
|
{
|
|
|
|
|
var dDepth = 0d;
|
|
|
|
|
var dHours = 0d;
|
|
|
|
|
foreach (var race in races)
|
|
|
|
|
{
|
|
|
|
|
dDepth += race.StartWellDepth;
|
2021-08-27 15:53:38 +05:00
|
|
|
|
for (var i = 0; i < race.Operations.Count; i++)
|
2021-08-26 10:18:59 +05:00
|
|
|
|
{
|
2021-08-27 15:53:38 +05:00
|
|
|
|
if (race.Operations[i].IdCategory == idOperationBhaDown)
|
2021-10-08 11:30:06 +05:00
|
|
|
|
dHours += race.Operations[i].DurationHours;
|
2021-08-27 15:53:38 +05:00
|
|
|
|
if (race.Operations[i].IdCategory == idOperationDrilling)
|
2021-08-26 10:18:59 +05:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return dDepth / (dHours + double.Epsilon);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static double CalcBhaUpSpeed(IEnumerable<Race> races)
|
|
|
|
|
{
|
|
|
|
|
var dDepth = 0d;
|
|
|
|
|
var dHours = 0d;
|
|
|
|
|
foreach (var race in races)
|
|
|
|
|
{
|
|
|
|
|
dDepth += race.EndWellDepth;
|
2021-09-10 11:28:57 +05:00
|
|
|
|
for (var i = race.Operations.Count - 1; i > 0; i--)
|
2021-08-26 10:18:59 +05:00
|
|
|
|
{
|
2021-08-27 15:53:38 +05:00
|
|
|
|
if (race.Operations[i].IdCategory == idOperationBhaUp)
|
2021-10-08 11:30:06 +05:00
|
|
|
|
dHours += race.Operations[i].DurationHours;
|
2021-08-27 15:53:38 +05:00
|
|
|
|
if (race.Operations[i].IdCategory == idOperationDrilling)
|
2021-08-26 10:18:59 +05:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return dDepth / (dHours + double.Epsilon);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-27 12:15:04 +05:00
|
|
|
|
public async Task<IEnumerable<PlanFactPredictBase<WellOperationDto>>> GetTvdAsync(int idWell, CancellationToken token)
|
|
|
|
|
{
|
|
|
|
|
var wellOperations = await db.WellOperations
|
|
|
|
|
.Include(o => o.OperationCategory)
|
|
|
|
|
.Include(o => o.WellSectionType)
|
|
|
|
|
.Where(o => o.IdWell == idWell)
|
2021-10-08 11:30:06 +05:00
|
|
|
|
.OrderBy(o => o.DateStart)
|
|
|
|
|
.ThenBy(o => o.DepthEnd)
|
2021-08-27 12:15:04 +05:00
|
|
|
|
.AsNoTracking()
|
|
|
|
|
.ToListAsync(token)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
var wellOperationsPlan = wellOperations
|
|
|
|
|
.Where(o => o.IdType == idOperationTypePlan)
|
2021-10-08 11:30:06 +05:00
|
|
|
|
.OrderBy(o => o.DateStart)
|
|
|
|
|
.ThenBy(o => o.DepthEnd);
|
2021-08-27 12:15:04 +05:00
|
|
|
|
|
|
|
|
|
var wellOperationsFact = wellOperations
|
|
|
|
|
.Where(o => o.IdType == idOperationTypeFact)
|
2021-10-08 11:30:06 +05:00
|
|
|
|
.OrderBy(o => o.DateStart)
|
|
|
|
|
.ThenBy(o => o.DepthEnd);
|
2021-08-27 12:15:04 +05:00
|
|
|
|
|
2021-10-06 16:30:46 +05:00
|
|
|
|
var sectionsIds = wellOperations
|
|
|
|
|
.Select(o => o.IdWellSectionType)
|
|
|
|
|
.Distinct();
|
|
|
|
|
|
2021-08-29 11:59:20 +05:00
|
|
|
|
if (!wellOperationsPlan.Any())
|
|
|
|
|
return null;
|
|
|
|
|
|
2021-10-06 16:30:46 +05:00
|
|
|
|
var merged = MergeArraysBySections(sectionsIds, wellOperationsPlan, wellOperationsFact);
|
2021-08-27 12:15:04 +05:00
|
|
|
|
var tvd = new List<PlanFactPredictBase<WellOperationDto>>(merged.Count);
|
2022-01-12 17:46:33 +05:00
|
|
|
|
var firstPoint = merged.First();
|
|
|
|
|
var dateStart = firstPoint.Item1?.DateStart ?? firstPoint.Item2.DateStart;
|
2021-08-27 12:15:04 +05:00
|
|
|
|
int iLastMatch = 0;
|
|
|
|
|
int iLastFact = 0;
|
|
|
|
|
for (int i = 0; i < merged.Count; i++)
|
|
|
|
|
{
|
2021-08-29 11:59:20 +05:00
|
|
|
|
var item = merged[i];
|
|
|
|
|
|
2021-08-27 12:15:04 +05:00
|
|
|
|
var planFactPredict = new PlanFactPredictBase<WellOperationDto>
|
|
|
|
|
{
|
2021-10-08 11:30:06 +05:00
|
|
|
|
Plan = item.Item1?.Adapt(WellOperationDtoMutation),
|
|
|
|
|
Fact = item.Item2?.Adapt(WellOperationDtoMutation),
|
2021-08-27 12:15:04 +05:00
|
|
|
|
Predict = null,
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-12 17:46:33 +05:00
|
|
|
|
if(planFactPredict.Plan is not null)
|
|
|
|
|
planFactPredict.Plan.Day = (planFactPredict.Plan.DateStart - dateStart).TotalDays;
|
|
|
|
|
|
|
|
|
|
if (planFactPredict.Fact is not null)
|
|
|
|
|
planFactPredict.Fact.Day = (planFactPredict.Fact.DateStart - dateStart).TotalDays;
|
|
|
|
|
|
2021-08-27 12:15:04 +05:00
|
|
|
|
tvd.Add(planFactPredict);
|
|
|
|
|
if ((item.Item1 is not null) && (item.Item2 is not null))
|
|
|
|
|
iLastMatch = i;
|
|
|
|
|
if (item.Item2 is not null)
|
|
|
|
|
iLastFact = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iLastMatch == 0 || iLastMatch == merged.Count - 1)
|
|
|
|
|
return tvd;
|
|
|
|
|
|
|
|
|
|
var lastMatchPlan = merged[iLastMatch].Item1;
|
2021-10-08 11:30:06 +05:00
|
|
|
|
var lastMatchPlanOperationEnd = lastMatchPlan.DateStart.AddHours(lastMatchPlan.DurationHours);
|
|
|
|
|
//var lastMatchFact = merged[iLastMatch].Item2;
|
|
|
|
|
//var lastMatchFactDateEnd = lastMatchFact.DateStart.AddHours(lastMatchFact.DurationHours);
|
|
|
|
|
var lastFact = merged[iLastFact].Item2;
|
|
|
|
|
var lastFactDateEnd = lastFact.DateStart.AddHours(lastFact.DurationHours);
|
|
|
|
|
var startOffset = lastFactDateEnd - lastMatchPlanOperationEnd;
|
2021-08-27 12:15:04 +05:00
|
|
|
|
|
|
|
|
|
for (int i = iLastMatch + 1; i < merged.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (merged[i].Item1 is null)
|
|
|
|
|
continue;
|
|
|
|
|
tvd[i].Predict = merged[i].Item1.Adapt<WellOperationDto>();
|
|
|
|
|
tvd[i].Predict.IdType = 2;
|
2021-10-08 11:30:06 +05:00
|
|
|
|
tvd[i].Predict.DateStart = tvd[i].Predict.DateStart + startOffset;
|
2022-01-12 17:46:33 +05:00
|
|
|
|
tvd[i].Predict.Day = (tvd[i].Predict.DateStart - dateStart).TotalDays;
|
2021-08-27 12:15:04 +05:00
|
|
|
|
}
|
2022-01-12 17:46:33 +05:00
|
|
|
|
|
2021-08-27 12:15:04 +05:00
|
|
|
|
return tvd;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-11 10:57:08 +05:00
|
|
|
|
private static List<Tuple<WellOperation, WellOperation>> MergeArraysBySections(
|
2021-10-08 11:30:06 +05:00
|
|
|
|
IEnumerable<int> sectionsIds,
|
2021-10-06 16:30:46 +05:00
|
|
|
|
IOrderedEnumerable<WellOperation> wellOperationsPlan,
|
|
|
|
|
IOrderedEnumerable<WellOperation> wellOperationsFact)
|
|
|
|
|
{
|
|
|
|
|
var merged = new List<Tuple<WellOperation, WellOperation>>(wellOperationsPlan.Count());
|
|
|
|
|
foreach (var sectionId in sectionsIds)
|
|
|
|
|
{
|
|
|
|
|
var sectionOperationsPlan = wellOperationsPlan
|
|
|
|
|
.Where(o => o.IdWellSectionType == sectionId);
|
|
|
|
|
var sectionOperationsFact = wellOperationsFact
|
|
|
|
|
.Where(o => o.IdWellSectionType == sectionId);
|
|
|
|
|
var sectionMerged = MergeArrays(sectionOperationsPlan, sectionOperationsFact);
|
|
|
|
|
merged.AddRange(sectionMerged);
|
|
|
|
|
}
|
|
|
|
|
return merged;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-12 16:07:08 +05:00
|
|
|
|
private static List<Tuple<WellOperation, WellOperation>> MergeArrays(IEnumerable<WellOperation> array1, IEnumerable<WellOperation> array2)
|
2021-08-29 11:59:20 +05:00
|
|
|
|
{
|
|
|
|
|
var a1 = array1.ToArray();
|
|
|
|
|
var a2 = array2.ToArray();
|
|
|
|
|
|
|
|
|
|
var m = new List<Tuple<WellOperation, WellOperation>>(a1.Length);
|
|
|
|
|
void Add(WellOperation item1, WellOperation item2) =>
|
|
|
|
|
m.Add(new Tuple<WellOperation, WellOperation>(item1, item2));
|
|
|
|
|
|
2021-10-06 16:30:46 +05:00
|
|
|
|
static bool Compare(WellOperation item1, WellOperation item2) =>
|
2021-10-08 11:30:06 +05:00
|
|
|
|
item1.IdCategory == item2.IdCategory && Math.Abs(item1.DepthEnd - item2.DepthEnd) < (30d + 0.005d * (item1.DepthEnd + item2.DepthEnd));
|
2021-08-29 11:59:20 +05:00
|
|
|
|
|
|
|
|
|
int i1 = 0;
|
|
|
|
|
int i2 = 0;
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
var is1 = a1.Length > i1;
|
|
|
|
|
var is2 = a2.Length > i2;
|
|
|
|
|
if (!(is1 || is2))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (is1 && is2)
|
|
|
|
|
{
|
|
|
|
|
if (Compare(a1[i1], a2[i2]))
|
|
|
|
|
Add(a1[i1++], a2[i2++]);
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-10-08 11:30:06 +05:00
|
|
|
|
int nextI1 = Array.FindIndex(a1, i1, (item) => Compare(item, a2[i2]));
|
|
|
|
|
int nextI2 = Array.FindIndex(a2, i2, (item) => Compare(item, a1[i1]));
|
2021-08-29 11:59:20 +05:00
|
|
|
|
|
2021-10-06 16:30:46 +05:00
|
|
|
|
bool deltaI1_Lt_deltaI2 = (nextI1 - i1) < (nextI2 - i2);
|
|
|
|
|
|
|
|
|
|
if (nextI1 == -1 && nextI2 == -1)
|
2021-08-29 11:59:20 +05:00
|
|
|
|
{
|
2021-10-08 11:30:06 +05:00
|
|
|
|
if (a1[i1].DepthEnd < a2[i2].DepthEnd)
|
2021-10-06 16:30:46 +05:00
|
|
|
|
{
|
|
|
|
|
Add(a1[i1++], null);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Add(null, a2[i2++]);
|
|
|
|
|
}
|
2021-08-29 11:59:20 +05:00
|
|
|
|
}
|
2021-10-06 16:30:46 +05:00
|
|
|
|
else if (nextI1 > -1 && nextI2 == -1)
|
2021-08-29 11:59:20 +05:00
|
|
|
|
{
|
2021-10-06 16:30:46 +05:00
|
|
|
|
Add(a1[i1++], null);
|
2021-08-29 11:59:20 +05:00
|
|
|
|
}
|
2021-10-06 16:30:46 +05:00
|
|
|
|
else if (nextI1 == -1 && nextI2 > -1)
|
2021-08-29 11:59:20 +05:00
|
|
|
|
{
|
2021-10-06 16:30:46 +05:00
|
|
|
|
Add(null, a2[i2++]);
|
|
|
|
|
}
|
|
|
|
|
else if (deltaI1_Lt_deltaI2)
|
|
|
|
|
{
|
|
|
|
|
Add(a1[i1++], null);
|
|
|
|
|
}
|
|
|
|
|
else if (!deltaI1_Lt_deltaI2)
|
|
|
|
|
{
|
|
|
|
|
Add(null, a2[i2++]);
|
2021-08-29 11:59:20 +05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (is1)
|
|
|
|
|
{
|
|
|
|
|
Add(a1[i1++], null);
|
|
|
|
|
}
|
|
|
|
|
else if (is2)
|
|
|
|
|
{
|
|
|
|
|
Add(null, a2[i2++]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-10 11:28:57 +05:00
|
|
|
|
private static readonly Action<WellOperationDto, WellOperation> WellOperationDtoMutation = (WellOperationDto dest, WellOperation source) =>
|
|
|
|
|
{
|
2021-08-27 12:15:04 +05:00
|
|
|
|
dest.CategoryName = source.OperationCategory?.Name;
|
|
|
|
|
dest.WellSectionTypeName = source.WellSectionType?.Caption;
|
|
|
|
|
};
|
2021-08-24 10:59:10 +05:00
|
|
|
|
}
|
|
|
|
|
}
|