DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/WellOperationsStatService.cs

507 lines
19 KiB
C#
Raw Normal View History

using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.Cache;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Mapster;
namespace AsbCloudInfrastructure.Services
{
class OperationParams
{
public OperationParams() { }
public OperationParams(WellOperation operation)
{
Id = operation.Id;
IdWellSectionType = operation.IdWellSectionType;
IdCategory = operation.IdCategory;
Start = operation.StartDate;
2021-08-25 15:17:24 +05:00
WellDepth = operation.WellDepth;
DeltaDepth = 0;
2021-08-26 10:18:59 +05:00
Hours = operation.DurationHours;
}
public int IdWellSectionType { get; }
public int IdCategory { get; }
public int Id { get; }
public DateTime Start { get; }
2021-08-25 15:17:24 +05:00
public double WellDepth { get; set; }
public double DeltaDepth { get; set; }
2021-08-26 10:18:59 +05:00
public double Hours { get; set; }
}
class Race
{
public DateTime StartDate { get; set; }
public double StartWellDepth { get; set; }
public DateTime EndDate { get; set; }
public double EndWellDepth { get; set; }
public double DrillingTime { get; set; }
public double NonProductiveHours { get; set; }
public double DeltaDepth => EndWellDepth - StartWellDepth;
public double DeltaHoursTimeNoNpt => (EndDate - StartDate).TotalHours - NonProductiveHours;
public double Speed => DeltaDepth / (DeltaHoursTimeNoNpt + double.Epsilon);
2021-08-26 10:18:59 +05:00
2021-08-27 15:53:38 +05:00
public List<OperationParams> Operations { get; internal set; }
}
public class WellOperationsStatService : IWellOperationsStatService
{
private readonly IAsbCloudDbContext db;
2021-08-25 11:13:56 +05:00
private readonly IWellService wellService;
2021-08-25 17:58:35 +05:00
private readonly CacheTable<WellSectionType> cacheSectionsTypes;
private readonly CacheTable<Well> cacheWell;
private readonly CacheTable<WellType> cacheWellType;
2021-08-25 11:13:56 +05:00
private readonly CacheTable<Cluster> cacheCluster;
private const int idOperationBhaAssembly = 1025;
private const int idOperationBhaDisassembly = 1026;
private const int idOperationNonProductiveTime = 1043;
private const int idOperationDrilling = 1001;
private const int idOperationBhaDown = 1046;
private const int idOperationBhaUp = 1047;
private const int idOperationCasingDown = 1048;
private const int idOperationTypePlan = 0;
private const int idOperationTypeFact = 1;
2021-08-25 11:13:56 +05:00
public WellOperationsStatService(IAsbCloudDbContext db, CacheDb cache, IWellService wellService)
{
this.db = db;
2021-08-25 11:13:56 +05:00
this.wellService = wellService;
2021-08-25 17:58:35 +05:00
cacheSectionsTypes = cache.GetCachedTable<WellSectionType>((DbContext)db);
cacheWell = cache.GetCachedTable<Well>((DbContext)db);
cacheWellType = cache.GetCachedTable<WellType>((DbContext)db);
2021-08-25 11:13:56 +05:00
cacheCluster = cache.GetCachedTable<Cluster>((DbContext)db);
}
2021-08-25 11:30:50 +05:00
public async Task<StatClusterDto> GetStatClusterAsync(int idCluster, CancellationToken token = default)
{
var wells = await db.Wells
.Include(w => w.WellOperations)
.Where(o => o.IdCluster == idCluster)
.AsNoTracking()
.ToListAsync(token);
var operations = wells.SelectMany(w => w.WellOperations).OrderBy(o => o.StartDate);
2021-08-25 11:13:56 +05:00
var cluster = await cacheCluster.FirstOrDefaultAsync(c => c.Id == idCluster, token);
var wellsIds = wells.Select(o => o.Id).Distinct();
2021-08-25 11:13:56 +05:00
var statsWells = new List<StatWellDto>(wellsIds.Count());
foreach (var idWell in wellsIds)
{
2021-08-25 11:13:56 +05:00
var statWellDto = await CalcStatWell(operations, idWell, token);
statsWells.Add(statWellDto);
}
var statClusterDto = new StatClusterDto
{
2021-08-25 11:13:56 +05:00
Id = idCluster,
Caption = cluster.Caption,
StatsWells = statsWells,
};
return statClusterDto;
}
2021-08-25 11:30:50 +05:00
public async Task<StatWellDto> GetStatWellAsync(int idWell,
CancellationToken token = default)
{
var operations = await db.WellOperations
.Where(o => o.IdWell == idWell)
.OrderBy(o => o.StartDate) // ускорит дальнейшие сортировки
.AsNoTracking()
.ToListAsync(token);
if (!operations.Any())
return null;
2021-08-25 11:13:56 +05:00
var statWellDto = await CalcStatWell(operations, idWell, token);
return statWellDto;
}
private async Task<StatWellDto> CalcStatWell(IEnumerable<WellOperation> operations, int idWell,
CancellationToken token = default)
{
var wellOperations = operations
.Where(o => o.IdWell == idWell);
2021-08-25 17:58:35 +05:00
var well = await cacheWell.FirstOrDefaultAsync(w => w.Id == idWell, token);
var wellType = await cacheWellType.FirstOrDefaultAsync(t => t.Id == well.IdWellType, token);
2021-08-25 11:13:56 +05:00
if (!wellOperations.Any())
return new StatWellDto()
{
Caption = well.Caption,
WellType = wellType.Caption
};
var statWellDto = new StatWellDto
{
2021-08-25 11:13:56 +05:00
Id = idWell,
Caption = well.Caption,
2021-08-25 17:58:35 +05:00
WellType = wellType?.Caption,
2021-08-25 11:13:56 +05:00
Companies = await wellService.GetCompaniesAsync(idWell, token),
2021-08-26 10:18:59 +05:00
Sections = CalcSectionsStats(wellOperations),
Total = GetStat(wellOperations),
};
return statWellDto;
}
2021-08-25 11:13:56 +05:00
private IEnumerable<StatSectionDto> CalcSectionsStats(IEnumerable<WellOperation> operations)
{
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);
var operationsPlan = MakeOperationsExt(operations.Where(o => o.IdType == idOperationTypePlan));
var operationsFact = MakeOperationsExt(operations.Where(o => o.IdType == idOperationTypeFact));
2021-08-25 11:13:56 +05:00
foreach ((var id, var sectionType) in sectionTypes)
{
2021-08-25 11:13:56 +05:00
StatSectionDto section = new StatSectionDto
{
Id = id,
2021-08-25 15:17:24 +05:00
Caption = sectionType.Caption,
2021-08-25 11:13:56 +05:00
Plan = CalcSectionStat(operationsPlan, id),
Fact = CalcSectionStat(operationsFact, id),
};
sections.Add(section);
}
return sections;
}
2021-08-25 11:13:56 +05:00
private static PlanFactBase<StatOperationsDto> GetStat(IEnumerable<WellOperation> operations)
{
var operationsPlan = MakeOperationsExt(operations.Where(o => o.IdType == idOperationTypePlan));
var operationsFact = MakeOperationsExt(operations.Where(o => o.IdType == idOperationTypeFact));
2021-08-25 11:13:56 +05:00
var section = new PlanFactBase<StatOperationsDto>
{
Plan = CalcStat(operationsPlan),
Fact = CalcStat(operationsFact),
};
return section;
}
2021-08-25 11:13:56 +05:00
private static StatOperationsDto CalcSectionStat(IEnumerable<OperationParams> operations, int idSectionType)
{
var sectionOperations = operations
.Where(o => o.IdWellSectionType == idSectionType)
.OrderBy(o => o.Start)
.ThenBy(o => o.WellDepth);
2021-08-29 12:05:43 +05:00
return CalcStat(sectionOperations);
}
2021-08-25 11:13:56 +05:00
private static StatOperationsDto CalcStat(IEnumerable<OperationParams> operations)
{
2021-08-29 12:05:43 +05:00
if (!operations.Any())
return null;
2021-08-26 10:18:59 +05:00
var races = GetCompleteRaces(operations);
2021-08-25 11:13:56 +05:00
var section = new StatOperationsDto
{
Start = operations.First().Start,
2021-08-26 10:18:59 +05:00
End = operations.Max(o => (o.Start.AddHours(o.Hours))),
2021-08-25 15:17:24 +05:00
WellDepthStart = operations.Min(o => o.WellDepth),
WellDepthEnd = operations.Max(o => o.WellDepth),
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)
.Sum(o => o.Hours),
};
return section;
}
private static double CalcROP(IEnumerable<OperationParams> operationsProps)
{
var drillingOperations = operationsProps.Where(o => o.IdCategory == idOperationDrilling);
var dDepth = 0d;
var dHours = 0d;
foreach (var operation in drillingOperations)
{
dDepth += operation.DeltaDepth;
2021-08-26 10:18:59 +05:00
dHours += operation.Hours;
}
return dDepth / (dHours + double.Epsilon);
}
2021-08-26 10:18:59 +05:00
private static double CalcCasingDownSpeed(IEnumerable<OperationParams> operationsProps)
{
var ops = operationsProps.Where(o => o.IdCategory == idOperationCasingDown);
2021-08-26 10:18:59 +05:00
var depth = 0d;
var dHours = 0d;
2021-08-26 10:18:59 +05:00
foreach (var operation in ops)
{
2021-08-26 10:18:59 +05:00
depth += operation.WellDepth;
dHours += operation.Hours;
}
2021-08-26 10:18:59 +05:00
return depth / (dHours + double.Epsilon);
}
private static IEnumerable<Race> GetCompleteRaces(IEnumerable<OperationParams> operations)
{
var races = new List<Race>();
2021-08-25 17:58:35 +05:00
var iterator = operations
.OrderBy(o => o.Start)
.GetEnumerator();
while (iterator.MoveNext())
{
if (iterator.Current.IdCategory == idOperationBhaAssembly)
{
var race = new Race
{
2021-08-26 10:18:59 +05:00
StartDate = iterator.Current.Start.AddHours(iterator.Current.Hours),
StartWellDepth = iterator.Current.WellDepth,
2021-08-27 15:53:38 +05:00
Operations = new List<OperationParams>(4),
};
while (iterator.MoveNext())
{
if (iterator.Current.IdCategory == idOperationNonProductiveTime)
{
2021-08-26 10:18:59 +05:00
race.NonProductiveHours += iterator.Current.Hours;
}
if (iterator.Current.IdCategory == idOperationBhaDisassembly)
{
2021-08-25 17:58:35 +05:00
race.EndDate = iterator.Current.Start;
2021-08-25 15:17:24 +05:00
race.EndWellDepth = iterator.Current.WellDepth;
races.Add(race);
2021-08-25 17:58:35 +05:00
break;
}
2021-08-27 15:53:38 +05:00
race.Operations.Add(iterator.Current);
}
}
}
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)
dHours += race.Operations[i].Hours;
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-08-27 15:53:38 +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)
dHours += race.Operations[i].Hours;
if (race.Operations[i].IdCategory == idOperationDrilling)
2021-08-26 10:18:59 +05:00
break;
}
}
return dDepth / (dHours + double.Epsilon);
}
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)
.OrderBy(o => o.StartDate)
.AsNoTracking()
.ToListAsync(token)
.ConfigureAwait(false);
var wellOperationsPlan = wellOperations
.Where(o => o.IdType == idOperationTypePlan)
.OrderBy(o => o.StartDate);
var wellOperationsFact = wellOperations
.Where(o => o.IdType == idOperationTypeFact)
.OrderBy(o => o.StartDate);
if (!wellOperationsPlan.Any())
return null;
var merged = MergeArrays(wellOperationsPlan, wellOperationsFact);
var tvd = new List<PlanFactPredictBase<WellOperationDto>>(merged.Count);
int iLastMatch = 0;
int iLastFact = 0;
for (int i = 0; i < merged.Count; i++)
{
var item = merged[i];
var planFactPredict = new PlanFactPredictBase<WellOperationDto>
{
Plan = item.Item1?.Adapt<WellOperationDto, WellOperation>(WellOperationDtoMutation),
Fact = item.Item2?.Adapt<WellOperationDto, WellOperation>(WellOperationDtoMutation),
Predict = null,
};
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;
var lastMatchPlanOperationEnd = lastMatchPlan.StartDate.AddHours(lastMatchPlan.DurationHours);
var lastMatchFact = merged[iLastMatch].Item2;
var startOffset = lastMatchFact.StartDate.AddHours(lastMatchFact.DurationHours) - lastMatchPlanOperationEnd;
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;
tvd[i].Predict.StartDate = tvd[i].Predict.StartDate + startOffset;
}
return tvd;
}
public static List<Tuple<WellOperation, WellOperation>> MergeArrays(IEnumerable<WellOperation> array1, IEnumerable<WellOperation> array2)
{
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));
bool Compare(WellOperation item1, WellOperation item2) =>
item1.IdCategory == item2.IdCategory && Math.Abs(item1.WellDepth - item2.WellDepth) < 250;
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
{
int nextI1 = Array.FindIndex<WellOperation>(a1, i1, (item) => Compare(item, a2[i2]));
int nextI2 = Array.FindIndex<WellOperation>(a2, i2, (item) => Compare(item, a1[i1]));
if ((nextI1 > -1) && ((nextI2 == -1) || ((nextI1 - i1) < (nextI2 - i2))))
{
for (; i1 < nextI1; i1++)
Add(a1[i1], null);
}
if ((nextI2 > -1) && ((nextI1 == -1) || ((nextI1 - i1) > (nextI2 - i2))))
{
for (; i2 < nextI2; i2++)
Add(null, a2[i2]);
}
if ((nextI1 == -1) && (nextI2 == -1))
{
for (; i2 < a2.Length; i2++)
Add(null, a2[i2]);
for (; i1 < a1.Length; i1++)
Add(a1[i1], null);
}
}
}
else if (is1)
{
Add(a1[i1++], null);
}
else if (is2)
{
Add(null, a2[i2++]);
}
}
return m;
}
2021-08-27 15:53:38 +05:00
private static readonly Action<WellOperationDto, WellOperation> WellOperationDtoMutation = (WellOperationDto dest, WellOperation source) =>
{
dest.CategoryName = source.OperationCategory?.Name;
dest.WellSectionTypeName = source.WellSectionType?.Caption;
};
2021-08-26 10:18:59 +05:00
private static IEnumerable<OperationParams> MakeOperationsExt(IEnumerable<WellOperation> operations)
{
var ops = new List<OperationParams>();
if (operations.Any())
2021-08-26 10:18:59 +05:00
{
var sortedOperations = operations.OrderBy(o => o.StartDate);
var count = operations.Count();
ops = new List<OperationParams>(count);
var item = operations.ElementAt(0);
var wellDepth = item.WellDepth;
var pre = new OperationParams(item);
var current = new OperationParams(item);
for (int i = 1; i < count; i++)
{
item = operations.ElementAt(i);
current = new OperationParams(item) { WellDepth = Helper.Max(wellDepth, item.WellDepth) };
pre.DeltaDepth = current.WellDepth - wellDepth;
wellDepth = current.WellDepth;
pre.Hours = (current.Start - pre.Start).TotalHours;
ops.Add(pre);
pre = current;
}
ops.Add(current);
2021-08-26 10:18:59 +05:00
}
2021-08-26 10:18:59 +05:00
return ops;
}
}
}