forked from ddrilling/AsbCloudServer
94b7dd3ef7
Stats reorganized
281 lines
11 KiB
C#
281 lines
11 KiB
C#
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 CacheTable<WellSectionType> cachedSectionsTypes;
|
||
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, Cache.CacheDb cache)
|
||
{
|
||
this.db = db;
|
||
cachedSectionsTypes = cache.GetCachedTable<WellSectionType>((DbContext)db);
|
||
}
|
||
|
||
public async Task<StatClusterDto> GetOperationStatByClusterAsync(int idCluster, CancellationToken token = default)
|
||
{
|
||
var operations = await db.WellOperations
|
||
.Include(o => o.Well)
|
||
.Where(o => o.Well.IdCluster == idCluster)
|
||
.OrderBy(o => o.StartDate)
|
||
.AsNoTracking()
|
||
.ToListAsync(token);
|
||
|
||
var wellsIds = operations.Select(o => o.IdWell).Distinct();
|
||
var sectionsStats = new List<StatOperationDto>(wellsIds.Count() * 3);
|
||
var wellsTotals = new List<StatOperationDto>(wellsIds.Count());
|
||
|
||
foreach(var idWell in wellsIds)
|
||
{
|
||
var wellOperations = operations.Where(o => o.IdWell == idWell);
|
||
sectionsStats.AddRange(GetWellSectionsStats(wellOperations));
|
||
wellsTotals.Add(GetWellStats(wellOperations));
|
||
}
|
||
|
||
var statClusterDto = new StatClusterDto
|
||
{
|
||
Sections = sectionsStats,
|
||
WellsTotals = wellsTotals,
|
||
};
|
||
return statClusterDto;
|
||
}
|
||
|
||
public async Task<StatWellDto> GetOperationStatByWellAsync(int idWell,
|
||
CancellationToken token = default)
|
||
{
|
||
var operations = await db.WellOperations
|
||
.Where(o => o.IdWell == idWell)
|
||
.OrderBy(o => o.StartDate) // ускорит дальнейшие сортировки
|
||
.AsNoTracking()
|
||
.ToListAsync(token);
|
||
|
||
var statWellDto = new StatWellDto
|
||
{
|
||
Sections = GetWellSectionsStats(operations),
|
||
Total = GetWellStats(operations),
|
||
};
|
||
return statWellDto;
|
||
}
|
||
|
||
private IEnumerable<StatOperationDto> GetWellSectionsStats(IEnumerable<WellOperation> operations)
|
||
{
|
||
var sectionTypesIds = operations.Select(o => o.IdWellSectionType).Distinct();
|
||
var sections = new List<StatOperationDto>(sectionTypesIds.Count());
|
||
var operationsPlan = MakeOperationsExt(operations.Where(o => o.IdType == 0));
|
||
var operationsFact = MakeOperationsExt(operations.Where(o => o.IdType == 0));
|
||
|
||
foreach (var idSectionType in sectionTypesIds)
|
||
{
|
||
var statPlan = CalcSectionStat(operationsPlan, idSectionType);
|
||
var statFact = CalcSectionStat(operationsFact, idSectionType);
|
||
StatOperationDto section = MakeWellSectionDto(idSectionType, statPlan, statFact);
|
||
sections.Add(section);
|
||
}
|
||
return sections;
|
||
}
|
||
|
||
private static StatOperationDto GetWellStats(IEnumerable<WellOperation> operations)
|
||
{
|
||
var operationsPlan = MakeOperationsExt(operations.Where(o => o.IdType == 0));
|
||
var operationsFact = MakeOperationsExt(operations.Where(o => o.IdType == 0));
|
||
var section = new StatOperationDto
|
||
{
|
||
Plan = CalcStat(operationsPlan),
|
||
Fact = CalcStat(operationsFact),
|
||
};
|
||
return section;
|
||
}
|
||
|
||
private StatOperationDto MakeWellSectionDto(int idSectionType, StatOperationBase statPlan, StatOperationBase statFact)
|
||
{
|
||
return new StatOperationDto
|
||
{
|
||
Id = idSectionType,
|
||
Caption = cachedSectionsTypes.FirstOrDefault(s => s.Id == idSectionType)?.Caption,
|
||
Plan = statPlan,
|
||
Fact = statFact,
|
||
};
|
||
}
|
||
|
||
private static StatOperationBase 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 StatOperationBase CalcStat(IEnumerable<OperationParams> operations)
|
||
{
|
||
var section = new StatOperationBase
|
||
{
|
||
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;
|
||
}
|
||
}
|
||
}
|