DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/WellOperationsStatService.cs
2021-08-25 11:30:50 +05:00

303 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
namespace AsbCloudInfrastructure.Services
{
class OperationParams
{
public OperationParams() { }
public OperationParams(WellOperation operation)
{
Id = operation.Id;
IdWellSectionType = operation.IdWellSectionType;
IdCategory = operation.IdCategory;
WellDepth = operation.WellDepth;
Start = operation.StartDate;
DurationHours = operation.DurationHours;
WellDepthReal = operation.WellDepth;
DeltaDepth = 0;
DurationToNextOperationHours = operation.DurationHours;
}
public int IdWellSectionType { get; }
public int IdCategory { get; }
public int Id { get; }
public DateTime Start { get; }
public double WellDepth { get; }
public double DurationHours { get; }
public double WellDepthReal { get; set; }
public double DeltaDepth { get; set; }
public double DurationToNextOperationHours { 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);
}
public class WellOperationsStatService : IWellOperationsStatService
{
private readonly IAsbCloudDbContext db;
private readonly IWellService wellService;
private readonly CacheTable<WellSectionType> cachedSectionsTypes;
private readonly CacheTable<Well> cachedWell;
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;
public WellOperationsStatService(IAsbCloudDbContext db, CacheDb cache, IWellService wellService)
{
this.db = db;
this.wellService = wellService;
cachedSectionsTypes = cache.GetCachedTable<WellSectionType>((DbContext)db);
cachedWell = cache.GetCachedTable<Well>((DbContext)db);
cacheCluster = cache.GetCachedTable<Cluster>((DbContext)db);
}
public async Task<StatClusterDto> GetStatClusterAsync(int idCluster, CancellationToken token = default)
{
var operations = await db.WellOperations
.Where(o => o.Well.IdCluster == idCluster)
.OrderBy(o => o.StartDate)
.AsNoTracking()
.ToListAsync(token);
var cluster = await cacheCluster.FirstOrDefaultAsync(c => c.Id == idCluster, token);
var wellsIds = operations.Select(o => o.IdWell).Distinct();
var statsWells = new List<StatWellDto>(wellsIds.Count());
foreach (var idWell in wellsIds)
{
var statWellDto = await CalcStatWell(operations, idWell, token);
statsWells.Add(statWellDto);
}
var statClusterDto = new StatClusterDto
{
Id = idCluster,
Caption = cluster.Caption,
StatsWells = statsWells,
};
return statClusterDto;
}
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);
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);
var well = await cachedWell.FirstOrDefaultAsync(w => w.Id == idWell, token);
var statWellDto = new StatWellDto
{
Id = idWell,
Caption = well.Caption,
Companies = await wellService.GetCompaniesAsync(idWell, token),
Sections = CalcSectionsStats(operations),
Total = GetStat(operations),
};
return statWellDto;
}
private IEnumerable<StatSectionDto> CalcSectionsStats(IEnumerable<WellOperation> operations)
{
var sectionTypeIds = operations
.Select(o => o.IdWellSectionType)
.Distinct();
var sectionTypes = cachedSectionsTypes
.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 == 0));
var operationsFact = MakeOperationsExt(operations.Where(o => o.IdType == 0));
foreach ((var id, var sectionType) in sectionTypes)
{
StatSectionDto section = new StatSectionDto
{
Id = id,
Caption = sectionType.Caption,
Plan = CalcSectionStat(operationsPlan, id),
Fact = CalcSectionStat(operationsFact, id),
};
sections.Add(section);
}
return sections;
}
private static PlanFactBase<StatOperationsDto> GetStat(IEnumerable<WellOperation> operations)
{
var operationsPlan = MakeOperationsExt(operations.Where(o => o.IdType == 0));
var operationsFact = MakeOperationsExt(operations.Where(o => o.IdType == 0));
var section = new PlanFactBase<StatOperationsDto>
{
Plan = CalcStat(operationsPlan),
Fact = CalcStat(operationsFact),
};
return section;
}
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);
return CalcStat(sectionOperations);
}
private static StatOperationsDto CalcStat(IEnumerable<OperationParams> operations)
{
var section = new StatOperationsDto
{
Start = operations.First().Start,
End = operations.Max(o => (o.Start.AddHours(o.DurationHours))),
WellDepthStart = operations.Min(o => o.WellDepthReal),
WellDepthEnd = operations.Max(o => o.WellDepthReal),
RouteSpeed = CalcAvgRaceSpeed(operations),
Rop = CalcROP(operations),
BhaDownSpeed = CalcSpeedByOperation(operations, idOperationBhaDown),
BhaUpSpeed = CalcSpeedByOperation(operations, idOperationBhaUp),
CasingDownSpeed = CalcSpeedByOperation(operations, IdOperationCasingDown),
};
return section;
}
private static double CalcSpeedByOperation(IEnumerable<OperationParams> operationsProps, int idOperation)
{
var ops = operationsProps.Where(o => o.IdCategory == idOperation);
var maxDepth = 0d;
var dHours = 0d;
foreach (var operation in ops)
{
if (maxDepth > operation.WellDepthReal)
maxDepth = operation.WellDepthReal;
dHours += operation.DurationHours;
}
return maxDepth / (dHours + double.Epsilon);
}
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;
dHours += operation.DurationHours;
}
return dDepth / (dHours + double.Epsilon);
}
private static double CalcAvgRaceSpeed(IEnumerable<OperationParams> operations)
{
var races = GetCompleteRaces(operations);
var dDepth = 0d;
var dHours = 0d;
foreach (var race in races)
{
dHours += race.DeltaHoursTimeNoNpt;
dDepth += race.DeltaDepth;
}
return dDepth / (dHours + double.Epsilon);
}
private static IEnumerable<OperationParams> MakeOperationsExt(IEnumerable<WellOperation> operations)
{
var count = operations.Count();
var 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)
{
WellDepthReal = Helper.Max(wellDepth, item.WellDepth) // TODO: учесть операциии с уменьшение глубины ствола.
};
pre.DeltaDepth = current.WellDepthReal - wellDepth;
pre.DurationToNextOperationHours = (current.Start - pre.Start).TotalHours;
ops.Add(pre);
pre = current;
}
ops.Add(current);
return ops;
}
private static IEnumerable<Race> GetCompleteRaces(IEnumerable<OperationParams> operations)
{
var races = new List<Race>();
var iterator = operations.GetEnumerator();
while (iterator.MoveNext())
{
if (iterator.Current.IdCategory == idOperationBhaAssembly)
{
var race = new Race
{
StartDate = iterator.Current.Start.AddHours(iterator.Current.DurationHours),
StartWellDepth = iterator.Current.WellDepthReal
};
while (iterator.MoveNext())
{
if (iterator.Current.IdCategory == idOperationNonProductiveTime)
{
race.NonProductiveHours += iterator.Current.DurationHours;
}
if (iterator.Current.IdCategory == idOperationBhaDisassembly)
{
race.EndDate = iterator.Current.Start.AddHours(iterator.Current.DurationHours);
race.EndWellDepth = iterator.Current.WellDepthReal;
races.Add(race);
}
}
}
}
return races;
}
}
}