forked from ddrilling/AsbCloudServer
277 lines
11 KiB
C#
277 lines
11 KiB
C#
|
using AsbCloudApp.Data;
|
|||
|
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;
|
|||
|
StartDate = operation.StartDate;
|
|||
|
DurationHours = operation.DurationHours;
|
|||
|
|
|||
|
WellDepthReal = operation.WellDepth;
|
|||
|
DeltaDepth = 0;
|
|||
|
DurationToNextOperationHours = operation.DurationHours;
|
|||
|
}
|
|||
|
|
|||
|
public int Id { get; }
|
|||
|
public int IdWellSectionType { get; }
|
|||
|
public int IdCategory { get; }
|
|||
|
public double WellDepth { get; }
|
|||
|
public DateTime StartDate { 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);
|
|||
|
}
|
|||
|
|
|||
|
class SectionStat
|
|||
|
{
|
|||
|
public DateTime StartDate { get; set; }
|
|||
|
public double StartWellDepth { get; set; }
|
|||
|
public DateTime EndDate { get; set; }
|
|||
|
public double EndWellDepth { get; set; }
|
|||
|
public double AvgRaceSpeed { get; set; }
|
|||
|
public double Rop { get; set; }
|
|||
|
public double BhaDownSpeed { get; set; }
|
|||
|
public double BhaUpSpeed { get; set; }
|
|||
|
public double CasingDownSpeed { get; set; }
|
|||
|
public int IdSectionType { get; internal set; }
|
|||
|
public double Hours => (EndDate - StartDate).TotalHours;
|
|||
|
}
|
|||
|
|
|||
|
public class WellOperationsStatService
|
|||
|
{
|
|||
|
private readonly IAsbCloudDbContext db;
|
|||
|
private readonly CacheTable<WellSectionType> cachedSectionsTypes;
|
|||
|
const int idOperationBhaAssembly = 1025;
|
|||
|
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, Cache.CacheDb cache)
|
|||
|
{
|
|||
|
this.db = db;
|
|||
|
cachedSectionsTypes = cache.GetCachedTable<WellSectionType>((DbContext)db);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<IEnumerable<WellSectionDto>> GetSectionsByWellIdAsync(int idWell,
|
|||
|
CancellationToken token = default)
|
|||
|
{
|
|||
|
var operationsAll = await db.WellOperations
|
|||
|
.Where(o => o.IdWell == idWell)
|
|||
|
.OrderBy(o => o.StartDate) // ускорит дальнейшие сортировки
|
|||
|
.AsNoTracking()
|
|||
|
.ToListAsync(token);
|
|||
|
|
|||
|
var operationsPlan = operationsAll
|
|||
|
.Where(o => o.IdType == 0);
|
|||
|
|
|||
|
var sectionsPlan = CalcSectionsStats(operationsPlan);
|
|||
|
|
|||
|
var operationsFact = operationsAll
|
|||
|
.Where(o => o.IdType == 1);
|
|||
|
|
|||
|
var sectionsFact = CalcSectionsStats(operationsFact);
|
|||
|
var sectionTypesIds = operationsAll.Select(o => o.IdWellSectionType).Distinct();
|
|||
|
var sections = new List<WellSectionDto>(sectionTypesIds.Count());
|
|||
|
foreach (var idSectionType in sectionTypesIds)
|
|||
|
{
|
|||
|
var statPlan = sectionsPlan.FirstOrDefault(s => s.IdSectionType == idSectionType);
|
|||
|
var statFact = sectionsFact.FirstOrDefault(s => s.IdSectionType == idSectionType);
|
|||
|
WellSectionDto section = MakeWellSectionDto(idSectionType, statPlan, statFact);
|
|||
|
sections.Add(section);
|
|||
|
}
|
|||
|
return sections;
|
|||
|
}
|
|||
|
|
|||
|
private WellSectionDto MakeWellSectionDto(int idSectionType, SectionStat statPlan, SectionStat statFact)
|
|||
|
{
|
|||
|
return new WellSectionDto
|
|||
|
{
|
|||
|
Id = idSectionType,
|
|||
|
SectionType = cachedSectionsTypes.FirstOrDefault(s => s.Id == idSectionType)?.Caption,
|
|||
|
|
|||
|
BhaDownSpeedPlan = statPlan?.BhaDownSpeed ?? double.NaN,
|
|||
|
BhaUpSpeedPlan = statPlan?.BhaUpSpeed ?? double.NaN,
|
|||
|
MechSpeedPlan = statPlan?.Rop ?? double.NaN,
|
|||
|
CasingDownSpeedPlan = statPlan?.CasingDownSpeed ?? double.NaN,
|
|||
|
RouteSpeedPlan = statPlan?.AvgRaceSpeed ?? double.NaN,
|
|||
|
WellDepthPlan = statPlan?.EndWellDepth ?? double.NaN,
|
|||
|
DurationPlan = statPlan?.Hours ?? double.NaN,
|
|||
|
|
|||
|
BhaDownSpeedFact = statFact?.BhaDownSpeed ?? double.NaN,
|
|||
|
BhaUpSpeedFact = statFact?.BhaUpSpeed ?? double.NaN,
|
|||
|
MechSpeedFact = statFact?.Rop ?? double.NaN,
|
|||
|
CasingDownSpeedFact = statFact?.CasingDownSpeed ?? double.NaN,
|
|||
|
RouteSpeedFact = statFact?.AvgRaceSpeed ?? double.NaN,
|
|||
|
WellDepthFact = statFact?.EndWellDepth ?? double.NaN,
|
|||
|
DurationFact = statFact?.Hours ?? double.NaN,
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
private static IEnumerable<SectionStat> CalcSectionsStats(IEnumerable<WellOperation> wellOperations)
|
|||
|
{
|
|||
|
var operations = MakeOperationsExt(wellOperations);
|
|||
|
var sectionTypesIds = operations.Select(o => o.IdWellSectionType).Distinct();
|
|||
|
var sectionsStats = new List<SectionStat>(sectionTypesIds.Count());
|
|||
|
foreach (var idSection in sectionTypesIds)
|
|||
|
{
|
|||
|
var section = CalcSectionStat(operations, idSection);
|
|||
|
sectionsStats.Add(section);
|
|||
|
}
|
|||
|
return sectionsStats;
|
|||
|
}
|
|||
|
|
|||
|
private static SectionStat CalcSectionStat(IEnumerable<OperationParams> allOperations, int idSectionType)
|
|||
|
{
|
|||
|
var sectionOperations = allOperations
|
|||
|
.Where(o => o.IdWellSectionType == idSectionType)
|
|||
|
.OrderBy(o => o.StartDate)
|
|||
|
.ThenBy(o => o.WellDepth);
|
|||
|
var section = new SectionStat
|
|||
|
{
|
|||
|
IdSectionType = idSectionType,
|
|||
|
StartDate = sectionOperations.First().StartDate,
|
|||
|
EndDate = sectionOperations.Max(o => (o.StartDate.AddHours(o.DurationHours))),
|
|||
|
StartWellDepth = sectionOperations.Min(o => o.WellDepthReal),
|
|||
|
EndWellDepth = sectionOperations.Max(o => o.WellDepthReal),
|
|||
|
AvgRaceSpeed = CalcAvgRaceSpeed(sectionOperations),
|
|||
|
Rop = CalcROP(sectionOperations),
|
|||
|
BhaDownSpeed = CalcSpeedByOperation(sectionOperations, idOperationBhaDown),
|
|||
|
BhaUpSpeed = CalcSpeedByOperation(sectionOperations, idOperationBhaUp),
|
|||
|
CasingDownSpeed = CalcSpeedByOperation(sectionOperations, 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.StartDate - pre.StartDate).TotalHours;
|
|||
|
ops.Add(pre);
|
|||
|
pre = current;
|
|||
|
}
|
|||
|
ops.Add(current);
|
|||
|
return ops;
|
|||
|
}
|
|||
|
|
|||
|
private static IEnumerable<Race> GetCompleteRaces(IEnumerable<OperationParams> operations)
|
|||
|
{
|
|||
|
var races = new List<Race>(4);
|
|||
|
var iterator = operations.GetEnumerator();
|
|||
|
while (iterator.MoveNext())
|
|||
|
{
|
|||
|
if (iterator.Current.IdCategory == idOperationBhaAssembly)
|
|||
|
{
|
|||
|
var race = new Race
|
|||
|
{
|
|||
|
StartDate = iterator.Current.StartDate.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.StartDate.AddHours(iterator.Current.DurationHours);
|
|||
|
race.EndWellDepth = iterator.Current.WellDepthReal;
|
|||
|
races.Add(race);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return races;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|