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 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((DbContext)db); } public async Task> 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(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 CalcSectionsStats(IEnumerable wellOperations) { var operations = MakeOperationsExt(wellOperations); var sectionTypesIds = operations.Select(o => o.IdWellSectionType).Distinct(); var sectionsStats = new List(sectionTypesIds.Count()); foreach (var idSection in sectionTypesIds) { var section = CalcSectionStat(operations, idSection); sectionsStats.Add(section); } return sectionsStats; } private static SectionStat CalcSectionStat(IEnumerable 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 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 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 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 MakeOperationsExt(IEnumerable operations) { var count = operations.Count(); var ops = new List(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 GetCompleteRaces(IEnumerable operations) { var races = new List(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; } } }