using AsbCloudApp.Data; using AsbCloudApp.Data.DetectedOperation; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb; using AsbCloudDb.Model; using Mapster; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.DetectOperations { public class DetectedOperationService : IDetectedOperationService { private readonly IAsbCloudDbContext db; private readonly IWellService wellService; private readonly IRepositoryWellRelated operationValueService; private readonly IScheduleRepository scheduleService; public DetectedOperationService(IAsbCloudDbContext db, IWellService wellService, IRepositoryWellRelated operationValueService, IScheduleRepository scheduleService) { this.db = db; this.wellService = wellService; this.operationValueService = operationValueService; this.scheduleService = scheduleService; } public async Task GetAsync(DetectedOperationRequest request, CancellationToken token) { var dtos = await GetOperationsAsync(request, token); if (dtos?.Any() != true) return null; var stats = GetOperationsDrillersStat(dtos); var result = new DetectedOperationListDto { Operations = dtos, Stats = stats }; return result; } public async Task?> GetOperationsAsync(DetectedOperationRequest request, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(request.IdWell, token); if (well?.IdTelemetry is null || well.Timezone is null) return null; var query = BuildQuery(well, request) ?.AsNoTracking(); if (query is null) return null; var data = await query.ToListAsync(token); var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token); var schedules = await scheduleService.GetByIdWellAsync(request.IdWell, token); var dtos = data.Select(o => Convert(o, well, operationValues, schedules)); return dtos; } public async Task> GetOperationSummaryAsync(DetectedOperationSummaryRequest request, CancellationToken token) { var query = db.Set() .AsNoTracking(); if (request.IdsTelemetries.Any()) query = query.Where(operation => request.IdsTelemetries.Contains(operation.IdTelemetry)); if (request.IdsOperationCategories.Any()) query = query.Where(operation => request.IdsOperationCategories.Contains(operation.IdCategory)); if (request.GeDateStart.HasValue) { var geDateStart = request.GeDateStart.Value.ToUniversalTime(); query = query.Where(operation => operation.DateStart >= geDateStart); } if (request.LeDateStart.HasValue) { var leDateStart = request.LeDateStart.Value.ToUniversalTime(); query = query.Where(operation => operation.DateStart <= leDateStart); } if (request.LeDateEnd.HasValue) { var leDateEnd = request.LeDateEnd.Value.ToUniversalTime(); query = query.Where(operation => operation.DateEnd <= leDateEnd); } if (request.GeDepthStart.HasValue) query = query.Where(operation => operation.DepthStart >= request.GeDepthStart.Value); if (request.LeDepthStart.HasValue) query = query.Where(operation => operation.DepthStart <= request.LeDepthStart.Value); if (request.LeDepthEnd.HasValue) query = query.Where(operation => operation.DepthEnd <= request.LeDepthEnd.Value); var queryGroup = query .GroupBy(operation => new { operation.IdTelemetry, operation.IdCategory }) .Select(group => new OperationsSummaryDto { IdTelemetry = group.Key.IdTelemetry, IdCategory = group.Key.IdCategory, Count = group.Count(), SumDepthIntervals = group.Sum(operation => operation.DepthEnd - operation.DepthStart), SumDurationHours = group.Sum(operation => (operation.DateEnd - operation.DateStart).TotalHours) }) .OrderBy(summ => summ.IdTelemetry) .ThenBy(summ => summ.IdCategory); var result = await queryGroup.ToArrayAsync(token); return result; } private static IEnumerable GetOperationsDrillersStat(IEnumerable operations) { var groups = operations.GroupBy(o => o.Driller); var stats = new List(groups.Count()); foreach (var group in groups) { var itemsWithTarget = group.Where(i => i.OperationValue is not null); var stat = new DetectedOperationDrillersStatDto { Driller = group.Key, AverageValue = group.Sum(e => e.Value) / group.Count(), Count = group.Count(), }; if (itemsWithTarget.Any()) { var itemsOutOfTarget = itemsWithTarget.Where(o => !IsTargetOk(o)); stat.AverageTargetValue = itemsWithTarget.Average(e => e.OperationValue?.TargetValue); stat.Efficiency = 100d * itemsOutOfTarget.Count() / itemsWithTarget.Count(); stat.Loss = itemsOutOfTarget.Sum(DeltaToTarget); } stats.Add(stat); } return stats; } public async Task?> GetOperationsStatAsync(DetectedOperationRequest request, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(request.IdWell, token); if (well?.IdTelemetry is null || well.Timezone is null) return null; var query = BuildQuery(well, request) ?.AsNoTracking(); if (query is null) return null; var entities = await query .Select(o => new { o.IdCategory, DurationMinutes = (o.DateEnd - o.DateStart).TotalMinutes, o.Value, }) .ToListAsync(token); if (!entities.Any()) return null; var operationValues = await operationValueService.GetByIdWellAsync(request.IdWell, token); var categories = await query .Select(o => new {o.IdCategory, o.OperationCategory.Name }) .Distinct() .ToDictionaryAsync(c=>c.IdCategory, c=>c.Name, token); var dtos = entities .GroupBy(o => o.IdCategory) .OrderBy(g => g.Key) .Select(g => new DetectedOperationStatDto{ IdCategory = g.Key, Category = categories[g.Key], Count = g.Count(), MinutesAverage = g.Average(o => o.DurationMinutes), MinutesMin = g.Min(o => o.DurationMinutes), MinutesMax = g.Max(o => o.DurationMinutes), MinutesTotal = g.Sum(o => o.DurationMinutes), ValueAverage = g.Average(o => o.Value), ValueMax = g.Max(o => o.Value), ValueMin = g.Min(o => o.Value), }); return dtos; } public async Task DeleteAsync(DetectedOperationRequest request, CancellationToken token) { var well = await wellService.GetOrDefaultAsync(request.IdWell, token); if (well?.IdTelemetry is null || well.Timezone is null) return 0; var query = BuildQuery(well, request); if (query is null) return 0; db.DetectedOperations.RemoveRange(query); return await db.SaveChangesAsync(token); } private static bool IsTargetOk(DetectedOperationDto op) { return (op.IdCategory) switch { WellOperationCategory.IdRotor => op.Value > op.OperationValue?.TargetValue, WellOperationCategory.IdSlide => op.Value > op.OperationValue?.TargetValue, WellOperationCategory.IdSlipsTime => op.Value > op.OperationValue?.TargetValue, _ => op.Value > op.OperationValue?.TargetValue, }; } private static double DeltaToTarget(DetectedOperationDto op) { return (op.IdCategory) switch { WellOperationCategory.IdRotor => 0, WellOperationCategory.IdSlide => 0, WellOperationCategory.IdSlipsTime => op.Value - op.OperationValue?.TargetValue??0, _ => 0, }; } private IQueryable? BuildQuery(WellDto well, DetectedOperationRequest request) { if (well?.IdTelemetry is null || well.Timezone is null) return null; var query = db.Set() .Include(o => o.OperationCategory) .Where(o => o.IdTelemetry == well.IdTelemetry); if (request is not null) { if (request.IdsCategories?.Any() == true) query = query.Where(o => request.IdsCategories.Contains(o.IdCategory)); if (request.GtDate is not null) query = query.Where(o => o.DateStart >= request.GtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours)); if (request.LtDate is not null) query = query.Where(o => o.DateEnd <= request.LtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours)); if (request.GtDepth is not null) query = query.Where(o => o.DepthStart >= request.GtDepth); if (request.LtDepth is not null) query = query.Where(o => o.DepthEnd <= request.LtDepth); if (request.EqIdTelemetryUser is not null) query = query.Where(o => o.IdUsersAtStart == request.EqIdTelemetryUser); } if (request?.SortFields?.Any() == true) { query = query.SortBy(request.SortFields); } else query = query .OrderBy(o => o.DateStart) .ThenBy(o => o.DepthStart); if (request?.Skip > 0) query = query.Skip((int)request.Skip); if (request?.Take > 0) query = query.Take((int)request.Take); return query; } private static DetectedOperationDto Convert(DetectedOperation operation, WellDto well, IEnumerable operationValues, IEnumerable schedules) { var dto = operation.Adapt(); dto.IdWell = well.Id; var dateStart = operation.DateStart.ToRemoteDateTime(well.Timezone.Hours); dto.DateStart = dateStart; dto.DateEnd = operation.DateEnd.ToRemoteDateTime(well.Timezone.Hours); dto.OperationValue = operationValues.FirstOrDefault(v => v.IdOperationCategory == dto.IdCategory && v.DepthStart <= dto.DepthStart && v.DepthEnd > dto.DepthStart); var timeStart = new TimeDto(dateStart); var driller = schedules.FirstOrDefault(s => s.DrillStart <= dateStart && s.DrillEnd > dateStart && ( s.ShiftStart > s.ShiftEnd ) ^ (s.ShiftStart <= timeStart && s.ShiftEnd > timeStart )) ?.Driller; dto.Driller = driller; return dto; } public async Task?> GetCategoriesAsync(int? idWell, CancellationToken token) { IQueryable query; if(idWell is null) { query = db.WellOperationCategories; } else { var well = await wellService.GetOrDefaultAsync((int )idWell, token); if (well?.IdTelemetry is null) return null; var idTelemetry = (int)well.IdTelemetry; query = db.DetectedOperations .Include(o => o.OperationCategory) .Where(o => o.IdTelemetry == idTelemetry) .Select(o => o.OperationCategory) .Distinct(); } var result = await query .Where(c => c.Id < 1000) .AsNoTracking() .Select(c => c.Adapt()) .ToArrayAsync(token); return result; } } }