From b2181871177b123ea6807c75509abfa32ed4b41e Mon Sep 17 00:00:00 2001 From: KharchenkoVV Date: Fri, 13 Aug 2021 12:32:43 +0500 Subject: [PATCH] CS2-37: Fixed OperationsToInterval() analytics. Half done. --- AsbCloudApp/Data/TelemetryOperationInfoDto.cs | 2 +- .../Services/AnalyticsService.cs | 158 ++++++++++++++---- 2 files changed, 131 insertions(+), 29 deletions(-) diff --git a/AsbCloudApp/Data/TelemetryOperationInfoDto.cs b/AsbCloudApp/Data/TelemetryOperationInfoDto.cs index 898a215e..39a50e86 100644 --- a/AsbCloudApp/Data/TelemetryOperationInfoDto.cs +++ b/AsbCloudApp/Data/TelemetryOperationInfoDto.cs @@ -6,6 +6,6 @@ namespace AsbCloudApp.Data public class TelemetryOperationInfoDto { public DateTimeOffset IntervalBegin { get; set; } - public IEnumerable Operations { get; set; } + public IList Operations { get; set; } } } diff --git a/AsbCloudInfrastructure/Services/AnalyticsService.cs b/AsbCloudInfrastructure/Services/AnalyticsService.cs index f802e8c5..5a7813a1 100644 --- a/AsbCloudInfrastructure/Services/AnalyticsService.cs +++ b/AsbCloudInfrastructure/Services/AnalyticsService.cs @@ -178,6 +178,7 @@ namespace AsbCloudInfrastructure.Services .ConfigureAwait(false); } + // This method is not finished (only half done). It returns not correct Dtos. public async Task> GetOperationsToIntervalAsync(int idWell, int intervalSeconds, int workBeginSeconds, CancellationToken token = default) { @@ -190,36 +191,40 @@ namespace AsbCloudInfrastructure.Services var timezoneOffset = telemetryService.GetTimezoneOffsetByTelemetryId((int)telemetryId); - var operations = await (from a in db.TelemetryAnalysis - where a.IdTelemetry == telemetryId - join o in db.TelemetryOperations on a.IdOperation equals o.Id - group a by new - { - Interval = Math.Floor((a.UnixDate - workBeginSeconds + timezoneOffset) / intervalSeconds), - OperationId = a.IdOperation, - o.Name - } into g - select new - { - IntervalStart = g.Min(d => d.UnixDate), - OperationName = g.Key.Name, - OperationsDuration = g.Sum(an => an.DurationSec) - }).AsNoTracking().ToListAsync(token) - .ConfigureAwait(false); + // Get'n'Group all operations only by start date and by name (if there were several operations in interval). + // Without dividing these operations duration by given interval + var ops = await (from a in db.TelemetryAnalysis + where a.IdTelemetry == telemetryId + join o in db.TelemetryOperations on a.IdOperation equals o.Id + group a by new + { + Interval = Math.Floor((a.UnixDate - workBeginSeconds + timezoneOffset) / intervalSeconds), + o.Name + } into g + select new + { + IntervalStart = g.Min(d => d.UnixDate), + OperationName = g.Key.Name, + OperationDuration = g.Sum(an => an.DurationSec) + }).AsNoTracking() + .OrderBy(op => op.IntervalStart) + .ToListAsync(token) + .ConfigureAwait(false); - var operationsGroupedByInterval = operations.GroupBy(op => op.IntervalStart) - .Select(o => new TelemetryOperationInfoDto - { - IntervalBegin = DateTimeOffset.FromUnixTimeSeconds(o.Key), - Operations = o.Select(opr => new TelemetryOperationDetailsDto - { - OperationName = opr.OperationName, - DurationSec = opr.OperationsDuration - }).ToList() - }) - .OrderBy(ops => ops.IntervalBegin); - return operationsGroupedByInterval; + var groupedOperationsList = new List(); + + + if (operations is not null && operations.Any()) + { + var operations = ops.Select(o => (o.IntervalStart, o.OperationName, o.OperationDuration)); + + var splittedOperationsByInterval = DivideOperationsByIntervalLength(operations, intervalSeconds); // divides good + + groupedOperationsList = UniteOperationsInDto(splittedOperationsByInterval, intervalSeconds).ToList(); // unites not good + } + + return groupedOperationsList; } public void SaveAnalytics(DataSaubBaseDto dataSaubDto) @@ -251,6 +256,103 @@ namespace AsbCloudInfrastructure.Services } } + private static IEnumerable<(long IntervalStart, string OperationName, int OperationDuration)> DivideOperationsByIntervalLength( + IEnumerable<(long IntervalStart, string OperationName, int OperationDuration)> operations, int intervalSeconds) + { + var splittedOperationsByInterval = new List<(long IntervalStart, string OperationName, int OperationDuration)>(); + + var operationDurationTimeCounter = 0; + + foreach (var op in operations) + { + if (op.OperationDuration < (intervalSeconds - operationDurationTimeCounter)) + { + splittedOperationsByInterval.Add((op.IntervalStart, op.OperationName, op.OperationDuration)); + operationDurationTimeCounter += op.OperationDuration; + } + else + { // if operation duration overflows current interval it shoud be divided into 2 or more parts for this and next intervals + var remainingIntervalTime = intervalSeconds - operationDurationTimeCounter; + splittedOperationsByInterval.Add((op.IntervalStart, op.OperationName, remainingIntervalTime)); // first part of long operation + + var operationDurationAfterDividing = op.OperationDuration - remainingIntervalTime; // second part of long operation. Can be less or more than interval + + // If operation duration even after dividing is still more than interval, + // it should be divided several times to several intervals. + if (operationDurationAfterDividing > intervalSeconds) + { + var counter = 0; + var updatedIntervalStartTime = op.IntervalStart + remainingIntervalTime; + + while (operationDurationAfterDividing > intervalSeconds) + { + splittedOperationsByInterval.Add((updatedIntervalStartTime + intervalSeconds * counter, op.OperationName, intervalSeconds)); + operationDurationAfterDividing -= intervalSeconds; + counter++; + } + + splittedOperationsByInterval.Add((updatedIntervalStartTime + operationDurationAfterDividing, op.OperationName, operationDurationAfterDividing)); + + operationDurationTimeCounter = operationDurationAfterDividing; + } + else + { + splittedOperationsByInterval.Add((op.IntervalStart, op.OperationName, operationDurationAfterDividing)); + operationDurationTimeCounter = operationDurationAfterDividing; + } + } + } + + return splittedOperationsByInterval; + } + + private static IEnumerable UniteOperationsInDto( + IEnumerable<(long IntervalStart, string OperationName, int OperationDuration)> operations, int intervalSeconds) + { + var groupedOperationsList = new List(); + + var groupedOperationsObj = new TelemetryOperationInfoDto + { + IntervalBegin = DateTimeOffset.FromUnixTimeSeconds(operations.First().IntervalStart), + Operations = new List() + }; + + var intervalEndDate = operations.First().IntervalStart + intervalSeconds; + + foreach (var op in operations) + { + if (op.IntervalStart < intervalEndDate) + { + groupedOperationsObj.Operations.Add(new TelemetryOperationDetailsDto + { + OperationName = op.OperationName, + DurationSec = op.OperationDuration + }); + } + else + { + groupedOperationsList.Add(groupedOperationsObj); + + intervalEndDate = op.IntervalStart + intervalSeconds; + groupedOperationsObj = new TelemetryOperationInfoDto + { + IntervalBegin = DateTimeOffset.FromUnixTimeSeconds(op.IntervalStart), + Operations = new List() + }; + + groupedOperationsObj.Operations.Add(new TelemetryOperationDetailsDto + { + OperationName = op.OperationName, + DurationSec = op.OperationDuration + }); + } + } + + groupedOperationsList.Add(groupedOperationsObj); + + return groupedOperationsList; + } + private TelemetryAnalysisDto GetDrillingAnalysis(IEnumerable dataSaubBases) { var lastSaubDate = dataSaubBases.Last().Date;