using AsbCloudApp.Data; using AsbCloudApp.Data.DetectedOperation; using AsbCloudApp.Data.Subsystems; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudApp.Services.Subsystems; using AsbCloudDb; using AsbCloudDb.Model; using AsbCloudDb.Model.Subsystems; using Mapster; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.Subsystems { #nullable enable internal class SubsystemOperationTimeService : ISubsystemOperationTimeService { private readonly IAsbCloudDbContext db; private readonly IWellService wellService; private readonly ICrudRepository subsystemService; private readonly IDetectedOperationService detectedOperationService; public const int IdSubsystemAKB = 1; public const int IdSubsystemMSE = 2; public const int IdSubsystemSpin = 65536; public const int IdSubsystemTorque = 65537; public SubsystemOperationTimeService(IAsbCloudDbContext db, IWellService wellService, ICrudRepository subsystemService, IDetectedOperationService detectedOperationService) { this.db = db; this.wellService = wellService; this.subsystemService = subsystemService; this.detectedOperationService = detectedOperationService; } /// public async Task DeleteAsync(SubsystemOperationTimeRequest 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(request); if (query is null) return 0; db.SubsystemOperationTimes.RemoveRange(query); return await db.SaveChangesAsync(token); } /// public async Task?> GetOperationTimeAsync(SubsystemOperationTimeRequest 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(request); if (query is null) return null; IEnumerable data = await query.ToListAsync(token); if (request.SelectMode == SubsystemOperationTimeRequest.SelectModeInner) { if (request.GtDate is not null) data = data.Where(o => o.DateStart >= request.GtDate.Value); if (request.LtDate is not null) data = data.Where(o => o.DateEnd <= request.LtDate.Value); } else if (request.SelectMode == SubsystemOperationTimeRequest.SelectModeTrim) { var begin = request.GtDate?.ToUtcDateTimeOffset(well.Timezone.Hours); var end = request.LtDate?.ToUtcDateTimeOffset(well.Timezone.Hours); data = Trim(data, begin, end); } var dtos = data.Select(o => Convert(o, well.Timezone.Hours)); return dtos; } /// public async Task?> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token) { request.SelectMode = SubsystemOperationTimeRequest.SelectModeTrim; var data = await GetOperationTimeAsync(request, token); if (data is null) return null; var detectedOperationsRequest = new DetectedOperationRequest() { IdWell = request.IdWell, IdsCategories = new int[] { WellOperationCategory.IdRotor, WellOperationCategory.IdSlide, }, LtDate = request.LtDate, GtDate = request.GtDate, }; var detectedOperations = await detectedOperationService.GetOperationsAsync(detectedOperationsRequest, token); if(detectedOperations?.Any() != true) return null; var depthInterval = GetDepthInterval(detectedOperations); var statList = CalcStat(data,depthInterval); return statList; } private static IEnumerable Trim(IEnumerable data, DateTimeOffset? gtDate, DateTimeOffset? ltDate) { var items = data.Select((item) => { if (gtDate.HasValue && item.DateStart < gtDate.Value) { item.DateStart = gtDate.Value; item.DepthStart = null; } if (ltDate.HasValue && item.DateEnd > ltDate.Value) { item.DateEnd = ltDate.Value; item.DepthEnd = null; } return item; }); return items; } private IEnumerable CalcStat(IEnumerable dtos, (double depthIntervalRotor, double depthIntervalSlide) depthInterval) { var groupedDataSubsystems = dtos .GroupBy(o => o.IdSubsystem); var periodGroupTotal = dtos.Sum(o => (o.DateEnd - o.DateStart).TotalHours); var result = groupedDataSubsystems.Select(g => { var depthIntervalSubsystem = GetDepthIntervalSubsystem(g.Key, depthInterval); var periodGroup = g.Sum(o => (o.DateEnd - o.DateStart).TotalHours); var periodGroupDepth = g.Sum(o => o.DepthEnd - o.DepthStart); var subsystemStat = new SubsystemStatDto() { IdSubsystem = g.Key, SubsystemName = subsystemService.GetOrDefault(g.Key)?.Name ?? "unknown", UsedTimeHours = periodGroup, KUsage = periodGroupDepth / depthIntervalSubsystem, SumDepthInterval = periodGroupDepth, OperationCount = g.Count() }; if(subsystemStat.KUsage > 1) subsystemStat.KUsage = 1; return subsystemStat; }); return result; } private static (double depthIntervalRotor, double depthIntervalSlide) GetDepthInterval (IEnumerable detectedOperations) { var depthIntervalRotor = detectedOperations.Where(o => o.IdCategory == WellOperationCategory.IdRotor) .Sum(o => o.DepthEnd - o.DepthStart); var depthIntervalSlide = detectedOperations.Where(o => o.IdCategory == WellOperationCategory.IdSlide) .Sum(o => o.DepthEnd - o.DepthStart); var depthInterval = (depthIntervalRotor, depthIntervalSlide); return depthInterval; } private static double GetDepthIntervalSubsystem(int idSubsystem, (double depthIntervalRotor, double depthIntervalSlide) depthInterval) { var depthIntervalSubsystem = 0d; //AKB - MSE if (idSubsystem == IdSubsystemAKB | idSubsystem == IdSubsystemMSE) { depthIntervalSubsystem = depthInterval.depthIntervalRotor + depthInterval.depthIntervalSlide; } //Spin if (idSubsystem == IdSubsystemSpin) { depthIntervalSubsystem = depthInterval.depthIntervalSlide; } //Torque if (idSubsystem == IdSubsystemTorque) { depthIntervalSubsystem = depthInterval.depthIntervalRotor; } return depthIntervalSubsystem; } private async Task> GetActiveWellsByCompany(int idCompany, CancellationToken token) { var listWell = await wellService.GetAsync(new() { IdCompany = idCompany }, token); var active = listWell.Where(w => w.IdState == 1); return active; } /// public async Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token) { var wells = await GetActiveWellsByCompany(idCompany, token); if (!wells.Any()) return Enumerable.Empty(); var hoursOffset = wells .FirstOrDefault(well => well.Timezone is not null) ?.Timezone.Hours ?? 5d; var beginUTC = gtDate.HasValue ? gtDate.Value.ToUtcDateTimeOffset(hoursOffset) :DateTime.Today.AddDays(-1).ToUtcDateTimeOffset(hoursOffset); var endUTC = ltDate.HasValue ? ltDate.Value.ToUtcDateTimeOffset(hoursOffset) : DateTime.Today.ToUtcDateTimeOffset(hoursOffset); var telemetryIds = wells .Where(w => w.IdTelemetry is not null) .Select(w => w.IdTelemetry) .Distinct(); var query = db.SubsystemOperationTimes .Where(o => telemetryIds.Contains(o.IdTelemetry) && o.DateStart >= beginUTC && o.DateEnd <= endUTC) .AsNoTracking(); var subsystemsOperationTime = await query.ToListAsync(token); var depthIntervals = await detectedOperationService .GetDepthIntervalAllOperationsAsync(telemetryIds, beginUTC, endUTC, token); var result = wells .Select(well => { var dtos = subsystemsOperationTime .Where(s => s.IdTelemetry == well.IdTelemetry) .Select(s => Convert(s, well.Timezone.Hours)); var (idTelemetry, depthIntervalRotor, depthIntervalSlide) = depthIntervals .FirstOrDefault(i => i.idTelemetry == well.IdTelemetry); var subsystemStat = idTelemetry > 0 && dtos.Any() ? CalcStat(dtos, (depthIntervalRotor, depthIntervalSlide)) : Enumerable.Empty(); return new SubsystemActiveWellStatDto { Well = well, SubsystemAKB = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAKB), SubsystemMSE = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemMSE), SubsystemSpinMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemSpin), SubsystemTorqueMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemTorque), }; }); return result; } /// public async Task GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token) { var query = BuildQuery(request); if (query is null) { return null; } var result = await query .GroupBy(o => o.IdTelemetry) .Select(g => new DatesRangeDto { From = g.Min(o => o.DateStart).DateTime, To = g.Max(o => o.DateEnd).DateTime }) .FirstOrDefaultAsync(token); return result; } private IQueryable? BuildQuery(SubsystemOperationTimeRequest request) { var well = wellService.GetOrDefault(request.IdWell); if (well?.IdTelemetry is null || well.Timezone is null) return null; var query = db.SubsystemOperationTimes .Include(o => o.Subsystem) .Where(o => o.IdTelemetry == well.IdTelemetry) .AsNoTracking(); if (request.IdsSubsystems?.Any() == true) query = query.Where(o => request.IdsSubsystems.Contains(o.IdSubsystem)); // # Dates range condition // [GtDate LtDate] // [DateStart DateEnd] [DateStart DateEnd] if (request.GtDate.HasValue) { DateTimeOffset gtDate = request.GtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours); query = query.Where(o => o.DateEnd >= gtDate); } if (request.LtDate.HasValue) { DateTimeOffset ltDate = request.LtDate.Value.ToUtcDateTimeOffset(well.Timezone.Hours); query = query.Where(o => o.DateStart <= ltDate); } if (request.GtDepth.HasValue) query = query.Where(o => o.DepthEnd >= request.GtDepth.Value); if (request.LtDepth.HasValue) query = query.Where(o => o.DepthStart <= request.LtDepth.Value); 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); else query = query.Take(3000); return query; } private static SubsystemOperationTimeDto Convert(SubsystemOperationTime operationTime, double? timezoneHours = null) { var dto = operationTime.Adapt(); var hours = timezoneHours ?? operationTime.Telemetry.TimeZone.Hours; dto.DateStart = operationTime.DateStart.ToRemoteDateTime(hours); dto.DateEnd = operationTime.DateEnd.ToRemoteDateTime(hours); return dto; } } #nullable disable }