using AsbCloudApp.Data; using AsbCloudApp.Data.SAUB; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services; public class DataSaubStatDrillingQualityService : IDataSaubStatDrillingQualityService { private IDataSaubStatRepository dataSaubStatDrillingQualityRepository; private ITelemetryDataSaubService dataSaubService; private ITelemetryDataCache telemetryDataCache; private ITelemetryService telemetryService; private static int IdFeedRegulatorRop = 1; private static int IdFeedRegulatorPressureDelta = 2; private static int IdFeedRegulatorAxialLoad = 3; private static int IdFeedRegulatorTorque = 4; public Dictionary> QualitySettingsForFeedRegulators { get; } public DataSaubStatDrillingQualityService( IDataSaubStatRepository dataSaubStatDrillingQualityRepository, ITelemetryDataCache telemetryDataCache, ITelemetryDataSaubService dataSaubService, ITelemetryService telemetryService) { this.dataSaubStatDrillingQualityRepository = dataSaubStatDrillingQualityRepository; this.dataSaubService = dataSaubService; this.telemetryDataCache = telemetryDataCache; this.telemetryService = telemetryService; Predicate hasQualityWithIdFeedRegulator1 = (TelemetryDataSaubDto spanItem) => (spanItem.BlockSpeed >= spanItem.BlockSpeedSp * 0.95 && spanItem.BlockSpeed <= spanItem.BlockSpeedSp * 1.05); Predicate hasQualityWithIdFeedRegulator2 = (TelemetryDataSaubDto spanItem) => (spanItem.Pressure >= (spanItem.PressureSp) - 5 && spanItem.Pressure <= (spanItem.PressureSp) + 5); Predicate hasQualityWithIdFeedRegulator3 = (TelemetryDataSaubDto spanItem) => (spanItem.AxialLoad >= (spanItem.AxialLoadSp - 0.5) && spanItem.AxialLoad <= (spanItem.AxialLoadSp + 0.5)); Predicate hasQualityWithIdFeedRegulator4 = (TelemetryDataSaubDto spanItem) => (spanItem.RotorTorque >= (spanItem.RotorTorqueSp - 0.5) && spanItem.RotorTorque <= (spanItem.RotorTorqueSp + 0.5)); QualitySettingsForFeedRegulators = new Dictionary>() { { IdFeedRegulatorRop, hasQualityWithIdFeedRegulator1 }, { IdFeedRegulatorPressureDelta, hasQualityWithIdFeedRegulator2 }, { IdFeedRegulatorAxialLoad, hasQualityWithIdFeedRegulator3 }, { IdFeedRegulatorTorque, hasQualityWithIdFeedRegulator4 }, }; } public async Task CreateStatAsync(int lastDaysFilter, Action onProgressCallback, CancellationToken token) { var cacheRequest = new TelemetryDataRequest() { GeDate = DateTime.UtcNow.AddDays(-lastDaysFilter) }; var idTelemetries = telemetryDataCache.GetIds(cacheRequest).ToArray(); if (!idTelemetries.Any()) return; var stats = await dataSaubStatDrillingQualityRepository.GetLastsAsync(idTelemetries, token); for (var i = 0; i < idTelemetries.Length; i++) { var idTelemetry = idTelemetries[i]; var lastDate = stats.FirstOrDefault(s => s.IdTelemetry == idTelemetry)?.DateEnd.ToUniversalTime() ?? DateTimeOffset.UnixEpoch; var result = await CreateStatDrillingQualityForTelemetry(idTelemetry, lastDate, token); var statsCount = result.Count(); if (result.Any()) statsCount = await dataSaubStatDrillingQualityRepository.InsertRangeAsync(result, token); if (onProgressCallback != null) onProgressCallback($"Calculate stat for telemetry: {idTelemetry}; from {lastDate}; results count: {statsCount};", 1d * i / idTelemetries.Length); } } public async Task> CreateStatDrillingQualityForTelemetry( int idTelemetry, DateTimeOffset geDate, CancellationToken token) { var leDate = DateTimeOffset.UtcNow; var result = new List(); var dataSaub = await dataSaubService.Get(idTelemetry, true, geDate, leDate, 100_000, token); if (!dataSaub.Any()) return result; if (dataSaub is not TelemetryDataSaubDto[] dataSaubArray) dataSaubArray = dataSaub.ToArray(); foreach (var item in QualitySettingsForFeedRegulators) { var data = CreateDataSaubStatDrillingQuality(item.Key, item.Value, dataSaubArray); result.AddRange(data); } return result; } private static IEnumerable CreateDataSaubStatDrillingQuality( int idFeedRegulator, Predicate checkQuality, TelemetryDataSaubDto[] dataSaub) { var result = new List(); var indexStart = 0; var indexEnd = 0; while (indexEnd < dataSaub.Count() - 1) { indexStart = Array.FindIndex(dataSaub, indexEnd, t => t.IdFeedRegulator == idFeedRegulator); if (indexStart < 0 || indexStart == dataSaub.Count() - 1) break; indexEnd = FindIndexEnd(indexStart, idFeedRegulator, dataSaub); var length = indexEnd - indexStart + 1; var subset = dataSaub.AsSpan(indexStart, length); if ((subset[^1].WellDepth - subset[0].WellDepth) < 0.15) continue; // мелкие выборки не учитываем. var stats = CalcStatsDrillingQuality(idFeedRegulator, subset, checkQuality); result.Add(stats); } return result; } private static int FindIndexEnd(int indexStart, int idFeedRegulator, TelemetryDataSaubDto[] dataSaub) { var indexEnd = indexStart + 1; for (var i = indexStart + 1; i < dataSaub.Count(); i++) { if (dataSaub[i].WellDepth >= dataSaub[i - 1].WellDepth) { indexEnd = i; } if (dataSaub[i].IdFeedRegulator != idFeedRegulator) break; } return indexEnd; } private static DataSaubStatDrillingQualityDto CalcStatsDrillingQuality( int idFeedRegulator, Span dataSaub, Predicate predicate ) { var result = new DataSaubStatDrillingQualityDto(); result.IdTelemetry = dataSaub[0].IdTelemetry; result.IdFeedRegulator = idFeedRegulator; result.DepthStart = dataSaub[0].WellDepth; result.DepthEnd = dataSaub[^1].WellDepth; result.DateStart = dataSaub[0].DateTime; result.DateEnd = dataSaub[^1].DateTime; var depthDrillingQuality = 0.0; for (var i = 0; i < dataSaub.Length - 1; i++) { if (predicate(dataSaub[i])) { depthDrillingQuality += dataSaub[i + 1].WellDepth - dataSaub[i].WellDepth; } } result.DepthDrillingQuality = depthDrillingQuality; return result; } public async Task> GetStatAsync(DrillingQualityRequest request, CancellationToken token) { var telemetries = telemetryService.GetOrDefaultTelemetriesByIdsWells(request.IdsWell); var idsTelemetriesWithIdWell = telemetries .Where(t => t.IdWell.HasValue) .ToDictionary(t => t.Id, t => t.IdWell!.Value); var dataSaubStatDrillingQuality = await dataSaubStatDrillingQualityRepository.GetAsync(idsTelemetriesWithIdWell.Keys, request.GeDate, request.LeDate, token); var dtosGroupedByIdTelemetries = dataSaubStatDrillingQuality .GroupBy(s => new { s.IdTelemetry, s.IdFeedRegulator }) .Select(stat => new { IdFeedRegulator = stat.Key.IdFeedRegulator, IdTelemetry = stat.Key.IdTelemetry, Kpd = (stat.Sum(s => s.DepthDrillingQuality) / stat.Sum(s => s.DepthEnd - s.DepthStart)) * 100, }) .GroupBy(stat => stat.IdTelemetry); var result = new List(); foreach (var dtos in dtosGroupedByIdTelemetries) { var kpdValuesByIdFeedRegulator = dtos .GroupBy(d => d.IdFeedRegulator) .ToDictionary(d => d.Key, d => d.FirstOrDefault()!.Kpd); var item = new DrillingQualityDto() { IdWell = idsTelemetriesWithIdWell[dtos.Key], KpdAxialLoad = kpdValuesByIdFeedRegulator.GetValueOrDefault(IdFeedRegulatorAxialLoad), KpdPressureDelta = kpdValuesByIdFeedRegulator.GetValueOrDefault(IdFeedRegulatorPressureDelta), KpdRop = kpdValuesByIdFeedRegulator.GetValueOrDefault(IdFeedRegulatorRop), KpdTorque = kpdValuesByIdFeedRegulator.GetValueOrDefault(IdFeedRegulatorTorque) }; result.Add(item); } return result; } }