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 cachedSectionsTypes; private readonly CacheTable cachedWell; private readonly CacheTable 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((DbContext)db); cachedWell = cache.GetCachedTable((DbContext)db); cacheCluster = cache.GetCachedTable((DbContext)db); } public async Task 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(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 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 CalcStatWell(IEnumerable 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 CalcSectionsStats(IEnumerable 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(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 GetStat(IEnumerable operations) { var operationsPlan = MakeOperationsExt(operations.Where(o => o.IdType == 0)); var operationsFact = MakeOperationsExt(operations.Where(o => o.IdType == 0)); var section = new PlanFactBase { Plan = CalcStat(operationsPlan), Fact = CalcStat(operationsFact), }; return section; } private static StatOperationsDto CalcSectionStat(IEnumerable 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 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 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.Start - pre.Start).TotalHours; ops.Add(pre); pre = current; } ops.Add(current); return ops; } private static IEnumerable GetCompleteRaces(IEnumerable operations) { var races = new List(); 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; } } }