diff --git a/AsbCloudApp/Data/DetectedOperation/OperationsSummaryDto.cs b/AsbCloudApp/Data/DetectedOperation/OperationsSummaryDto.cs
new file mode 100644
index 00000000..74f755cc
--- /dev/null
+++ b/AsbCloudApp/Data/DetectedOperation/OperationsSummaryDto.cs
@@ -0,0 +1,32 @@
+namespace AsbCloudApp.Data.DetectedOperation;
+
+///
+/// Статистика по операциям
+///
+public class OperationsSummaryDto
+{
+ ///
+ /// Id телеметрии
+ ///
+ public int IdTelemetry { get; set; }
+
+ ///
+ /// Id названия/описания операции
+ ///
+ public int IdCategory { get; set; }
+
+ ///
+ /// Количество операций
+ ///
+ public int Count { get; set; }
+
+ ///
+ /// Cумма проходок операций
+ ///
+ public double SumDepthIntervals { get; set; }
+
+ ///
+ /// Cумма продолжительностей операций
+ ///
+ public double SumDurationHours { get; set; }
+}
diff --git a/AsbCloudApp/Data/Subsystems/SubsystemStatDto.cs b/AsbCloudApp/Data/Subsystems/SubsystemStatDto.cs
index aec6c6b8..da9c98cb 100644
--- a/AsbCloudApp/Data/Subsystems/SubsystemStatDto.cs
+++ b/AsbCloudApp/Data/Subsystems/SubsystemStatDto.cs
@@ -23,13 +23,20 @@ namespace AsbCloudApp.Data.Subsystems
///
public double KUsage { get; set; }
///
- /// сумма изменения глубин
+ /// сумма изменения глубин при включеной подсистеме
///
public double SumDepthInterval { get; set; }
///
- /// количество операций
+ /// сумма проходок автоопределенных операций выполняемых подсистемой
///
- public int OperationCount { get; set; }
-
+ public double SumOperationDepthInterval { get; set; }
+ ///
+ /// сумма продолжительности автоопределенных операций выполняемых подсистемой
+ ///
+ public double SumOperationDurationHours { get; set; }
+ ///
+ /// количество включений подсистемы
+ ///
+ public int OperationCount { get; set; }
}
}
diff --git a/AsbCloudApp/Requests/DetectedOperationSummaryRequest.cs b/AsbCloudApp/Requests/DetectedOperationSummaryRequest.cs
new file mode 100644
index 00000000..2b362f28
--- /dev/null
+++ b/AsbCloudApp/Requests/DetectedOperationSummaryRequest.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AsbCloudApp.Requests;
+
+///
+/// Запрос на получение обобщенных данных по операцим
+///
+public class DetectedOperationSummaryRequest
+{
+ ///
+ /// Список id телеметрий
+ /// пустой список - нет фильтрации
+ ///
+ public IEnumerable IdsTelemetries { get;set;} = Enumerable.Empty();
+
+ ///
+ /// Список id категорий операций
+ /// пустой список - нет фильтрации
+ ///
+ public IEnumerable IdsOperationCategories { get; set; } = Enumerable.Empty();
+
+ ///
+ /// Больше или равно даты начала операции
+ ///
+ public DateTimeOffset? GeDateStart {get;set;}
+
+ ///
+ /// Меньше или равно даты начала операции
+ ///
+ public DateTimeOffset? LeDateStart { get; set; }
+
+ ///
+ /// Меньше или равно даты окончания операции
+ ///
+ public DateTimeOffset? LeDateEnd { get; set; }
+
+ ///
+ /// Больше или равно глубины начала операции
+ ///
+ public double? GeDepthStart { get; set; }
+
+ ///
+ /// Меньше или равно глубины начала операции
+ ///
+ public double? LeDepthStart { get; set; }
+
+ ///
+ /// Меньше или равно глубины окончания операции
+ ///
+ public double? LeDepthEnd { get; set; }
+}
diff --git a/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs b/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs
index 9080b596..02db1042 100644
--- a/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs
+++ b/AsbCloudApp/Requests/SubsystemOperationTimeRequest.cs
@@ -26,7 +26,7 @@ namespace AsbCloudApp.Requests
///
/// Больше или равно дате
///
- public DateTime? GtDate { get; set; }
+ public DateTime? GtDate { get; set; }//TODO: its Ge*
///
/// Меньше или равно дате
@@ -43,6 +43,7 @@ namespace AsbCloudApp.Requests
///
public double? LtDepth { get; set; }
+ //TODO: Replace modes by DateTimeOffset LeDateStart, LeDateEnd
///
/// информация попадает в выборку, если интервал выборки частично или полностью пересекается с запрашиваемым интервалом
///
diff --git a/AsbCloudApp/Services/IDetectedOperationService.cs b/AsbCloudApp/Services/IDetectedOperationService.cs
index 901f7b3f..120cfef8 100644
--- a/AsbCloudApp/Services/IDetectedOperationService.cs
+++ b/AsbCloudApp/Services/IDetectedOperationService.cs
@@ -42,12 +42,10 @@ namespace AsbCloudApp.Services
///
/// Получить интервалы глубин по всем скважинам
///
- /// список ИД телеметрий активных скважин
- ///
- ///
+ ///
///
/// кортеж - ид телеметрии, интервалы глубины забоя (ротор,слайд)
- Task> GetDepthIntervalAllOperationsAsync(IEnumerable telemetryIds,DateTimeOffset gtDate, DateTimeOffset ltDate, CancellationToken token);
+ Task> GetOperationSummaryAsync(DetectedOperationSummaryRequest request, CancellationToken token);
///
/// Удалить операции
diff --git a/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs b/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs
index 774b8754..67b29673 100644
--- a/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs
+++ b/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs
@@ -19,7 +19,7 @@ namespace AsbCloudApp.Services.Subsystems
///
///
///
- Task?> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token);
+ Task> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token);
///
/// Удаление наработки по подсистемам.
@@ -37,7 +37,7 @@ namespace AsbCloudApp.Services.Subsystems
///
///
///
- Task?> GetOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token);
+ Task> GetOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token);
///
/// Временной диапазон за который есть статистика работы подсистем
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
index 701cfd08..c3246e05 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
@@ -67,27 +67,58 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return dtos;
}
- public async Task> GetDepthIntervalAllOperationsAsync(IEnumerable telemetryIds, DateTimeOffset gtDate, DateTimeOffset ltDate, CancellationToken token)
+ public async Task> GetOperationSummaryAsync(DetectedOperationSummaryRequest request, CancellationToken token)
{
var query = db.Set()
- .Include(o => o.OperationCategory)
- .Where(o => o.DateStart >= gtDate)
- .Where(o => o.DateEnd <= ltDate)
- .Where(o => telemetryIds.Contains(o.IdTelemetry))
- .GroupBy(g => g.IdTelemetry)
- .Select(g => new
- {
- IdTelemetry = g.Key,
- RotorDepthInterval = g.Where(o => o.IdCategory == WellOperationCategory.IdRotor).Sum(o => o.DepthEnd - o.DepthStart),
- SlideDepthInterval = g.Where(o => o.IdCategory == WellOperationCategory.IdSlide).Sum(o => o.DepthEnd - o.DepthStart)
- });
- var data = await query.ToArrayAsync(token);
- var result = data.Select(g =>
- (
- g.IdTelemetry,
- g.RotorDepthInterval,
- g.SlideDepthInterval
- ));
+ .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;
}
diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs
index f3b079eb..e40c1a4d 100644
--- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs
+++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs
@@ -16,385 +16,379 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-namespace AsbCloudInfrastructure.Services.Subsystems
+namespace AsbCloudInfrastructure.Services.Subsystems;
+
+internal class SubsystemOperationTimeService : ISubsystemOperationTimeService
{
- 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 IdSubsystemAKBRotor = 11;
+ public const int IdSubsystemAKBSlide = 12;
+ 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)
{
- private readonly IAsbCloudDbContext db;
- private readonly IWellService wellService;
- private readonly ICrudRepository subsystemService;
- private readonly IDetectedOperationService detectedOperationService;
- public const int IdSubsystemAKB = 1;
- public const int IdSubsystemAKBRotor = 11;
- public const int IdSubsystemAKBSlide = 12;
- 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 = TrimOperation(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 TrimOperation(IEnumerable data, DateTimeOffset? gtDate, DateTimeOffset? ltDate)
- {
- if (!ltDate.HasValue && !gtDate.HasValue)
- return data.Select(d => d.Adapt());
-
- var items = data.Select((item) =>
- {
- var operationTime = item.Adapt();
- if (!(item.DepthStart.HasValue && item.DepthEnd.HasValue))
- return operationTime;
-
- var dateDiff = (item.DateEnd - item.DateStart).TotalSeconds;
- var depthDiff = item.DepthEnd.Value - item.DepthStart.Value;
- var a = depthDiff / dateDiff;
- var b = item.DepthStart.Value;
-
- if (gtDate.HasValue && item.DateStart < gtDate.Value)
- {
- operationTime.DateStart = gtDate.Value;
- var x = (gtDate.Value - item.DateStart).TotalSeconds;
- operationTime.DepthStart = (float)(a * x + b);
- }
- if (ltDate.HasValue && item.DateEnd > ltDate.Value)
- {
- operationTime.DateEnd = ltDate.Value;
- var x = (ltDate.Value - item.DateStart).TotalSeconds;
- operationTime.DepthEnd = (float)(a * x + b);
- }
- return operationTime;
- });
-
- return items;
- }
-
- private IEnumerable CalcStat(
- IEnumerable dtos,
- (double depthIntervalRotor, double depthIntervalSlide) depthInterval)
- {
- var groupedDataSubsystems = dtos
- .OrderBy(o => o.Id)
- .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;
- });
-
- var apdParts = result.Where(x => x.IdSubsystem == 11 || x.IdSubsystem == 12);
- if (apdParts.Any())
- {
- var apdSum = new SubsystemStatDto()
- {
- IdSubsystem = IdSubsystemAKB,
- SubsystemName = "АПД",
- UsedTimeHours = apdParts.Sum(part => part.UsedTimeHours),
- KUsage = apdParts.Sum(part => part.SumDepthInterval) / GetDepthIntervalSubsystem(IdSubsystemAKB, depthInterval),
- SumDepthInterval = apdParts.Sum(part => part.SumDepthInterval),
- OperationCount = apdParts.Sum(part => part.OperationCount),
- };
- result = result.Append(apdSum).OrderBy(m => m.IdSubsystem);
- }
-
- 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;
- }
- //AKB - Rotor
- if (idSubsystem == IdSubsystemAKBRotor)
- {
- depthIntervalSubsystem = depthInterval.depthIntervalRotor;
- }
- //AKB - Slide
- if (idSubsystem == IdSubsystemAKBSlide)
- {
- depthIntervalSubsystem = depthInterval.depthIntervalSlide;
- }
- //Spin
- if (idSubsystem == IdSubsystemSpin)
- {
- depthIntervalSubsystem = depthInterval.depthIntervalSlide;
- }
- //Torque
- if (idSubsystem == IdSubsystemTorque)
- {
- depthIntervalSubsystem = depthInterval.depthIntervalRotor;
- }
- return depthIntervalSubsystem;
-
- }
-
- ///
- public async Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token)
- {
- var activeWells = await wellService.GetAsync(new() { IdCompany = idCompany, IdState = 1 }, token);
- var result = await GetStatAsync(activeWells, gtDate, ltDate, token);
- return result;
- }
-
- ///
- public async Task> GetStatByActiveWells(IEnumerable wellIds, CancellationToken token)
- {
- var activeWells = await wellService.GetAsync(new() { Ids = wellIds, IdState = 1 }, token);
- var result = await GetStatAsync(activeWells, null, null, token);
- return result;
- }
-
- private async Task> GetStatAsync(IEnumerable wells, DateTime? gtDate, DateTime? ltDate, CancellationToken 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)
- : db.SubsystemOperationTimes.Min(s => s.DateStart)
- .DateTime
- .ToUtcDateTimeOffset(hoursOffset);
-
- var endUTC = ltDate.HasValue
- ? ltDate.Value.ToUtcDateTimeOffset(hoursOffset)
- : db.SubsystemOperationTimes.Max(s => s.DateEnd)
- .DateTime
- .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)
- ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Not valid IdWell = {request.IdWell}");
-
- var query = db.SubsystemOperationTimes
- .Include(o => o.Subsystem)
- .Where(o => o.IdTelemetry == well.IdTelemetry)
- .AsNoTracking();
-
- if (request.IdsSubsystems.Any())
- 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);
-
- 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;
- }
+ 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)
+ ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist");
+
+ var query = BuildQuery(request, well);
+ 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)
+ ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist");
+
+ var dtos = await GetOperationTimeAsync(request, well, token);
+ return dtos;
+ }
+
+ private async Task> GetOperationTimeAsync(SubsystemOperationTimeRequest request, WellDto well, CancellationToken token)
+ {
+ var query = BuildQuery(request, well);
+ 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 = TrimOperation(data, begin, end);
+ }
+
+ var dtos = data.Select(o => Convert(o, well.Timezone.Hours));
+ return dtos;
+ }
+
+ ///
+ public async Task> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token)
+ {
+ var well = await wellService.GetOrDefaultAsync(request.IdWell, token)
+ ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist");
+
+ request.SelectMode = SubsystemOperationTimeRequest.SelectModeTrim;
+ var subsystemsTimes = await GetOperationTimeAsync(request, well, token);
+ if (subsystemsTimes is null)
+ return Enumerable.Empty();
+
+ var detectedOperationSummaryRequest = new DetectedOperationSummaryRequest()
+ {
+ IdsTelemetries = new[] {well.IdTelemetry!.Value},
+ IdsOperationCategories = WellOperationCategory.MechanicalDrillingSubIds,
+
+ GeDateStart = request.GtDate,
+ LeDateStart = request.LtDate,
+
+ GeDepthStart = request.GtDepth,
+ LeDepthStart = request.LtDepth,
+ };
+ var operationsSummaries = await detectedOperationService.GetOperationSummaryAsync(detectedOperationSummaryRequest, token);
+ if(!operationsSummaries.Any())
+ return Enumerable.Empty();
+
+ var statList = CalcStat(subsystemsTimes, operationsSummaries);
+ return statList;
+ }
+
+ private static IEnumerable TrimOperation(IEnumerable data, DateTimeOffset? gtDate, DateTimeOffset? ltDate)
+ {
+ if (!ltDate.HasValue && !gtDate.HasValue)
+ return data.Select(d => d.Adapt());
+
+ var items = data.Select((item) =>
+ {
+ var operationTime = item.Adapt();
+ if (!(item.DepthStart.HasValue && item.DepthEnd.HasValue))
+ return operationTime;
+
+ var dateDiff = (item.DateEnd - item.DateStart).TotalSeconds;
+ var depthDiff = item.DepthEnd.Value - item.DepthStart.Value;
+ var a = depthDiff / dateDiff;
+ var b = item.DepthStart.Value;
+
+ if (gtDate.HasValue && item.DateStart < gtDate.Value)
+ {
+ operationTime.DateStart = gtDate.Value;
+ var x = (gtDate.Value - item.DateStart).TotalSeconds;
+ operationTime.DepthStart = (float)(a * x + b);
+ }
+ if (ltDate.HasValue && item.DateEnd > ltDate.Value)
+ {
+ operationTime.DateEnd = ltDate.Value;
+ var x = (ltDate.Value - item.DateStart).TotalSeconds;
+ operationTime.DepthEnd = (float)(a * x + b);
+ }
+ return operationTime;
+ });
+
+ return items;
+ }
+
+ private IEnumerable CalcStat(
+ IEnumerable subsystemsTimes,
+ IEnumerable operationsSummaries)
+ {
+ var groupedSubsystemsTimes = subsystemsTimes
+ .OrderBy(o => o.Id)
+ .GroupBy(o => o.IdSubsystem);
+
+ var periodGroupTotal = subsystemsTimes.Sum(o => (o.DateEnd - o.DateStart).TotalHours);
+
+ var result = groupedSubsystemsTimes.Select(g =>
+ {
+ var periodGroup = g.Sum(o => (o.DateEnd - o.DateStart).TotalHours);
+ var periodGroupDepth = g.Sum(o => o.DepthEnd - o.DepthStart);
+ var (sumOprationsDepth, sumOprationsDurationHours) = AggregateOperationsSummaries(g.Key, operationsSummaries);
+ var subsystemStat = new SubsystemStatDto()
+ {
+ IdSubsystem = g.Key,
+ SubsystemName = subsystemService.GetOrDefault(g.Key)?.Name ?? "unknown",
+ UsedTimeHours = periodGroup,
+ SumOperationDepthInterval = sumOprationsDepth,
+ SumOperationDurationHours = sumOprationsDurationHours,
+ SumDepthInterval = periodGroupDepth,
+ KUsage = periodGroupDepth / sumOprationsDepth,
+ OperationCount = g.Count(),
+ };
+ if (subsystemStat.KUsage > 1)
+ subsystemStat.KUsage = 1;
+ return subsystemStat;
+ });
+
+ var apdParts = result.Where(x => x.IdSubsystem == 11 || x.IdSubsystem == 12);
+ if (apdParts.Any())
+ {
+ var apdSum = new SubsystemStatDto()
+ {
+ IdSubsystem = IdSubsystemAKB,
+ SubsystemName = "АПД",
+ UsedTimeHours = apdParts.Sum(part => part.UsedTimeHours),
+ SumOperationDepthInterval = apdParts.Sum(part => part.SumOperationDepthInterval),
+ SumOperationDurationHours = apdParts.Sum(part => part.SumOperationDurationHours),
+ SumDepthInterval = apdParts.Sum(part => part.SumDepthInterval),
+ OperationCount = apdParts.Sum(part => part.OperationCount),
+ };
+ apdSum.KUsage = apdSum.SumDepthInterval / apdSum.SumOperationDepthInterval;
+ if (apdSum.KUsage > 1)
+ apdSum.KUsage = 1;
+ result = result.Append(apdSum).OrderBy(m => m.IdSubsystem);
+ }
+
+ return result;
+ }
+
+ private static (double SumDepth, double SumDurationHours) AggregateOperationsSummaries(int idSubsystem, IEnumerable operationsSummaries)
+ => idSubsystem switch
+ {
+ IdSubsystemAKBRotor or IdSubsystemTorque => CalcOperationSummariesByCategories(operationsSummaries, WellOperationCategory.IdRotor),
+ IdSubsystemAKBSlide or IdSubsystemSpin => CalcOperationSummariesByCategories(operationsSummaries, WellOperationCategory.IdSlide),
+ IdSubsystemAKB or IdSubsystemMSE => CalcOperationSummariesByCategories(operationsSummaries, WellOperationCategory.IdRotor, WellOperationCategory.IdSlide),
+ _ => throw new ArgumentException($"idSubsystem: {idSubsystem} does not supported in this method", nameof(idSubsystem)),
+ };
+
+ private static (double SumDepth, double SumDurationHours) CalcOperationSummariesByCategories(
+ IEnumerable operationsSummaries,
+ params int[] idsOperationCategories)
+ {
+ var filtered = operationsSummaries.Where(sum => idsOperationCategories.Contains(sum.IdCategory));
+ var sumDepth = filtered.Sum(summ => summ.SumDepthIntervals);
+ var sumDurationHours = filtered.Sum(summ => summ.SumDurationHours);
+ return (sumDepth, sumDurationHours);
+ }
+
+ ///
+ public async Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token)
+ {
+ var activeWells = await wellService.GetAsync(new() { IdCompany = idCompany, IdState = 1 }, token);
+ var result = await GetStatAsync(activeWells, gtDate, ltDate, token);
+ return result;
+ }
+
+ ///
+ public async Task> GetStatByActiveWells(IEnumerable wellIds, CancellationToken token)
+ {
+ var activeWells = await wellService.GetAsync(new() { Ids = wellIds, IdState = 1 }, token);
+ var result = await GetStatAsync(activeWells, null, null, token);
+ return result;
+ }
+
+ private async Task> GetStatAsync(IEnumerable wells, DateTime? gtDate, DateTime? ltDate, CancellationToken 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)
+ : db.SubsystemOperationTimes.Min(s => s.DateStart)
+ .DateTime
+ .ToUtcDateTimeOffset(hoursOffset);
+
+ var endUTC = ltDate.HasValue
+ ? ltDate.Value.ToUtcDateTimeOffset(hoursOffset)
+ : db.SubsystemOperationTimes.Max(s => s.DateEnd)
+ .DateTime
+ .ToUtcDateTimeOffset(hoursOffset);
+
+ IEnumerable idsTelemetries = wells
+ .Where(w => w.IdTelemetry is not null)
+ .Select(w => w.IdTelemetry!.Value)
+ .Distinct();
+
+ var query = db.SubsystemOperationTimes
+ .Where(o => idsTelemetries.Contains(o.IdTelemetry) &&
+ o.DateStart >= beginUTC &&
+ o.DateEnd <= endUTC)
+ .AsNoTracking();
+
+ var subsystemsOperationTime = await query.ToArrayAsync(token);
+
+ var operationSummaries = await detectedOperationService
+ .GetOperationSummaryAsync(new ()
+ {
+ IdsTelemetries = idsTelemetries,
+ IdsOperationCategories = WellOperationCategory.MechanicalDrillingSubIds,
+ GeDateStart = beginUTC,
+ LeDateEnd = endUTC,
+ }, token);
+
+ var result = wells
+ .Select(well => {
+ var dtos = subsystemsOperationTime
+ .Where(s => s.IdTelemetry == well.IdTelemetry)
+ .Select(s => Convert(s, well.Timezone.Hours));
+
+ var wellStat = new SubsystemActiveWellStatDto{ Well = well };
+
+ var telemetryOperationSummaries = operationSummaries.Where(summ => summ.IdTelemetry == well.IdTelemetry);
+ if (telemetryOperationSummaries.Any())
+ {
+ var subsystemStat = CalcStat(dtos, telemetryOperationSummaries);
+ if (subsystemStat.Any())
+ {
+ wellStat.SubsystemAKB = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAKB);
+ wellStat.SubsystemMSE = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemMSE);
+ wellStat.SubsystemSpinMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemSpin);
+ wellStat.SubsystemTorqueMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemTorque);
+ }
+ }
+
+ return wellStat;
+ });
+
+ return result;
+ }
+
+ ///
+ public async Task GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token)
+ {
+ var well = await wellService.GetOrDefaultAsync(request.IdWell, token)
+ ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} does not exist");
+
+ var query = BuildQuery(request, well);
+ 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, WellDto well)
+ {
+ var idTelemetry = well.IdTelemetry
+ ?? throw new ArgumentInvalidException(nameof(request.IdWell), $"Well Id: {request.IdWell} has no telemetry");
+
+ var query = db.SubsystemOperationTimes
+ .Include(o => o.Subsystem)
+ .Where(o => o.IdTelemetry == idTelemetry)
+ .AsNoTracking();
+
+ if (request.IdsSubsystems.Any())
+ 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);
+
+ 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;
+ }
}