forked from ddrilling/AsbCloudServer
Новые расчеты наработки подсистем
This commit is contained in:
parent
f300974b1d
commit
5fb9375955
@ -1,30 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
namespace AsbCloudApp.Data.Subsystems
|
||||
namespace AsbCloudApp.Data.Subsystems;
|
||||
|
||||
/// <summary>
|
||||
/// Статистика наработки подсистем по активным скважинам
|
||||
/// </summary>
|
||||
public class SubsystemActiveWellStatDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Статистика наработки подсистем по активным скважинам
|
||||
/// </summary>
|
||||
public class SubsystemActiveWellStatDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Активная скважина
|
||||
/// </summary>
|
||||
public WellInfoDto Well { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Наработки подсистемы АКБ
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemAKB { get; set; }
|
||||
/// <summary>
|
||||
/// Наработки подсистемы МСЕ
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemMSE { get; set; }
|
||||
/// <summary>
|
||||
/// Наработки подсистемы СПИН
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemSpinMaster { get; set; }
|
||||
/// <summary>
|
||||
/// Наработки подсистемы ТОРК
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemTorqueMaster { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Активная скважина
|
||||
/// </summary>
|
||||
public WellInfoDto Well { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Наработки подсистемы АПД
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemAPD { get; set; }
|
||||
/// <summary>
|
||||
/// Наработки подсистемы СПИН
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemSpinMaster { get; set; }
|
||||
/// <summary>
|
||||
/// Наработки подсистемы ТОРК
|
||||
/// </summary>
|
||||
public SubsystemStatDto? SubsystemTorqueMaster { get; set; }
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using System;
|
||||
namespace AsbCloudApp.Data.Subsystems
|
||||
{
|
||||
/// <summary>
|
||||
/// Модель информации о работе подсистемы
|
||||
/// </summary>
|
||||
public class SubsystemOperationTimeDto:IId
|
||||
{
|
||||
/// <summary>
|
||||
/// Идентификатор
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// идентификатор подсистемы
|
||||
/// </summary>
|
||||
public int IdSubsystem { get; set; }
|
||||
/// <summary>
|
||||
/// Название подсистемы
|
||||
/// </summary>
|
||||
public string SubsystemName { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// дата/время включения подсистемы
|
||||
/// </summary>
|
||||
public DateTime DateStart { get; set; }
|
||||
/// <summary>
|
||||
/// дата/время выключения подсистемы
|
||||
/// </summary>
|
||||
public DateTime DateEnd { get; set; }
|
||||
/// <summary>
|
||||
/// глубина забоя на момент включения подсистемы
|
||||
/// </summary>
|
||||
public double DepthStart { get; set; }
|
||||
/// <summary>
|
||||
/// глубина забоя на момент выключения подсистемы
|
||||
/// </summary>
|
||||
public double DepthEnd { get; set; }
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ namespace AsbCloudApp.Requests
|
||||
/// <summary>
|
||||
/// класс с фильтрами для запроса
|
||||
/// </summary>
|
||||
public class SubsystemOperationTimeRequest: RequestBase, IValidatableObject
|
||||
public class SubsystemTimeRequest: RequestBase, IValidatableObject
|
||||
{
|
||||
private static readonly DateTime validationMinDate = new DateTime(2020,01,01,0,0,0,DateTimeKind.Utc);
|
||||
|
||||
@ -26,7 +26,7 @@ namespace AsbCloudApp.Requests
|
||||
/// <summary>
|
||||
/// Больше или равно дате
|
||||
/// </summary>
|
||||
public DateTime? GtDate { get; set; }//TODO: its Ge*
|
||||
public DateTime? GeDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Меньше или равно дате
|
||||
@ -43,41 +43,20 @@ namespace AsbCloudApp.Requests
|
||||
/// </summary>
|
||||
public double? LtDepth { get; set; }
|
||||
|
||||
//TODO: Replace modes by DateTimeOffset LeDateStart, LeDateEnd
|
||||
/// <summary>
|
||||
/// информация попадает в выборку, если интервал выборки частично или полностью пересекается с запрашиваемым интервалом
|
||||
/// </summary>
|
||||
public const int SelectModeOuter = 0;
|
||||
|
||||
/// <summary>
|
||||
/// информация попадает в выборку, если интервал выборки строго полностью пересекается с запрашиваемым интервалом.
|
||||
/// </summary>
|
||||
public const int SelectModeInner = 1;
|
||||
|
||||
/// <summary>
|
||||
/// аналогично outer, но интервалы в частично пересекающиеся укорачиваются по границам интервала выборки.
|
||||
/// </summary>
|
||||
public const int SelectModeTrim = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Режим выборки элементов
|
||||
/// </summary>
|
||||
public int SelectMode { get; set; } = SelectModeOuter;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (GtDate.HasValue && GtDate < validationMinDate)
|
||||
if (GeDate.HasValue && GeDate < validationMinDate)
|
||||
yield return new ValidationResult(
|
||||
$"Должно быть больше {validationMinDate:O})",
|
||||
new[] { nameof(GtDate) });
|
||||
new[] { nameof(GeDate) });
|
||||
|
||||
if (LtDate.HasValue && GtDate.HasValue)
|
||||
if (LtDate.HasValue && GeDate.HasValue)
|
||||
{
|
||||
if (LtDate < GtDate)
|
||||
if (LtDate < GeDate)
|
||||
yield return new ValidationResult(
|
||||
$"{nameof(LtDate)} должно быть больше {nameof(GtDate)}. ({LtDate:O} < {GtDate:O})",
|
||||
new[] { nameof(LtDate), nameof(GtDate) });
|
||||
$"{nameof(LtDate)} должно быть больше {nameof(GeDate)}. ({LtDate:O} < {GeDate:O})",
|
||||
new[] { nameof(LtDate), nameof(GeDate) });
|
||||
}
|
||||
|
||||
if (LtDepth.HasValue && GtDepth.HasValue)
|
40
AsbCloudApp/Services/ISubsystemService.cs
Normal file
40
AsbCloudApp/Services/ISubsystemService.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using AsbCloudApp.Data.Subsystems;
|
||||
using AsbCloudApp.Requests;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudApp.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Получение инфо о наработке подсистем
|
||||
/// </summary>
|
||||
public interface ISubsystemService
|
||||
{
|
||||
/// <summary>
|
||||
/// Статистика о наработке подсистем
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<SubsystemStatDto>> GetStatAsync(SubsystemTimeRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение статистики по наработке подсистем по активным скважинам
|
||||
/// </summary>
|
||||
/// <param name="idCompany"></param>
|
||||
/// <param name="gtDate"></param>
|
||||
/// <param name="ltDate"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение статистики по наработке подсистем по активным скважинам
|
||||
/// </summary>
|
||||
/// <param name="wellIds"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(IEnumerable<int> wellIds, CancellationToken token);
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.Subsystems;
|
||||
using AsbCloudApp.Requests;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudApp.Services.Subsystems
|
||||
{
|
||||
/// <summary>
|
||||
/// Получение инфо о наработке подсистем
|
||||
/// </summary>
|
||||
public interface ISubsystemOperationTimeService
|
||||
{
|
||||
/// <summary>
|
||||
/// Статистика о наработке подсистем
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<SubsystemStatDto>> GetStatAsync(SubsystemOperationTimeRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Удаление наработки по подсистемам.
|
||||
/// Если удаляется конец, то фоновый сервис подсчета наработки восстановит эти данные.
|
||||
/// Может потребоваться для запуска повторного расчета по новому алгоритму.
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> DeleteAsync(SubsystemOperationTimeRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Интервалы работы подсистем
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<SubsystemOperationTimeDto>> GetOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Временной диапазон за который есть статистика работы подсистем
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<DatesRangeDto?> GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение статистики по наработке подсистем по активным скважинам
|
||||
/// </summary>
|
||||
/// <param name="idCompany"></param>
|
||||
/// <param name="gtDate"></param>
|
||||
/// <param name="ltDate"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Получение статистики по наработке подсистем по активным скважинам
|
||||
/// </summary>
|
||||
/// <param name="wellIds"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(IEnumerable<int> wellIds, CancellationToken token);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ namespace AsbCloudDb.Model.DefaultData
|
||||
{ typeof(WellType), new EntityFillerWellType()},
|
||||
{ typeof(MeasureCategory), new EntityFillerMeasureCategory()},
|
||||
{ typeof(CompanyType), new EntityFillerCompanyType()},
|
||||
{ typeof(Subsystems.Subsystem), new EntityFillerSubsystem() },
|
||||
{ typeof(Subsystem), new EntityFillerSubsystem() },
|
||||
{ typeof(NotificationCategory), new EntityNotificationCategory()},
|
||||
};
|
||||
return fillers;
|
||||
|
@ -1,127 +0,0 @@
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudDb.Model.Subsystems;
|
||||
using AsbCloudInfrastructure.Services.Subsystems;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
|
||||
|
||||
public class WorkSubsystemAbfOperationTimeCalc : WorkSubsystemOperationTimeCalcAbstract
|
||||
{
|
||||
public WorkSubsystemAbfOperationTimeCalc()
|
||||
: base("Subsystem automated bit feeding operation time calc")
|
||||
{
|
||||
Timeout = TimeSpan.FromMinutes(30);
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token)
|
||||
{
|
||||
static bool isSubsytemAkbRotor(short? mode) => mode == 1;
|
||||
|
||||
static bool isSubsytemAkbSlide(short? mode) => mode == 3;
|
||||
|
||||
static bool IsSubsystemMse(short? state) => (state & 1) > 0;
|
||||
|
||||
var query =
|
||||
$"select tt.date, tt.mode, tt.well_depth, tt.mse_state " +
|
||||
$"from ( " +
|
||||
$" select " +
|
||||
$" date, " +
|
||||
$" mode, " +
|
||||
$" mse_state, " +
|
||||
$" well_depth, " +
|
||||
$" lag(mode,1) over (order by date) as mode_lag, " +
|
||||
$" lead(mode,1) over (order by date) as mode_lead " +
|
||||
$" from t_telemetry_data_saub " +
|
||||
$" where id_telemetry = {idTelemetry} and well_depth is not null and well_depth > 0 " +
|
||||
$" order by date ) as tt " +
|
||||
$"where (tt.mode_lag is null or (tt.mode != tt.mode_lag and tt.mode_lead != tt.mode_lag)) and tt.date >= '{geDate:u}' " +
|
||||
$"order by tt.date;";
|
||||
|
||||
using var result = await ExecuteReaderAsync(db, query, token);
|
||||
|
||||
var subsystemsOperationTimes = new List<SubsystemOperationTime>();
|
||||
var detectorRotor = new SubsystemDetector(idTelemetry, idSubsystemAPDRotor, isSubsytemAkbRotor, IsValid);
|
||||
var detectorSlide = new SubsystemDetector(idTelemetry, idSubsystemAPDSlide, isSubsytemAkbSlide, IsValid);
|
||||
var detectorMse = new SubsystemDetector(idTelemetry, idSubsystemMse, IsSubsystemMse, IsValid);
|
||||
|
||||
while (result.Read())
|
||||
{
|
||||
var mode = result.GetFieldValue<short?>(1);
|
||||
var state = result.GetFieldValue<short?>(3);
|
||||
|
||||
var isAkbRotorEnable = isSubsytemAkbRotor(mode);
|
||||
var isAkbSlideEnable = isSubsytemAkbSlide(mode);
|
||||
var isMseEnable = IsSubsystemMse(state);
|
||||
var date = result.GetFieldValue<DateTimeOffset>(0);
|
||||
var depth = result.GetFieldValue<float>(2);
|
||||
|
||||
if (detectorRotor.TryDetect(mode, date, depth, out var detectedRotor))
|
||||
subsystemsOperationTimes.Add(detectedRotor!);
|
||||
|
||||
if (detectorSlide.TryDetect(mode, date, depth, out var detectedSlide))
|
||||
subsystemsOperationTimes.Add(detectedSlide!);
|
||||
|
||||
if (detectorMse.TryDetect(mode, date, depth, out var detectedMse))
|
||||
subsystemsOperationTimes.Add(detectedMse!);
|
||||
}
|
||||
|
||||
return subsystemsOperationTimes;
|
||||
}
|
||||
|
||||
private static async Task<DbDataReader> ExecuteReaderAsync(IAsbCloudDbContext db, string query, CancellationToken token)
|
||||
{
|
||||
var connection = db.Database.GetDbConnection();
|
||||
if (
|
||||
connection?.State is null ||
|
||||
connection.State == ConnectionState.Broken ||
|
||||
connection.State == ConnectionState.Closed)
|
||||
{
|
||||
await db.Database.OpenConnectionAsync(token);
|
||||
connection = db.Database.GetDbConnection();
|
||||
}
|
||||
using var command = connection.CreateCommand();
|
||||
command.CommandText = query;
|
||||
|
||||
var result = await command.ExecuteReaderAsync(token);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool IsValid(SubsystemOperationTime item)
|
||||
{
|
||||
var validateCode = GetValidateErrorCode(item);
|
||||
if (validateCode != 0)
|
||||
{
|
||||
var str = System.Text.Json.JsonSerializer.Serialize(item);
|
||||
Trace.TraceWarning($"Wrong({validateCode}) SubsystemOperationTime: {str}");
|
||||
}
|
||||
return validateCode == 0;
|
||||
}
|
||||
|
||||
private static int GetValidateErrorCode(SubsystemOperationTime item)
|
||||
{
|
||||
if (item.DateStart > item.DateEnd)
|
||||
return -1;
|
||||
if ((item.DateEnd - item.DateStart).TotalHours > 48)
|
||||
return -2;
|
||||
if (item.DepthEnd < item.DepthStart)
|
||||
return -3;
|
||||
if (item.DepthEnd - item.DepthStart > 2000d)
|
||||
return -4;
|
||||
if (item.DepthEnd < 0d)
|
||||
return -5;
|
||||
if (item.DepthStart < 0d)
|
||||
return -6;
|
||||
if (item.DepthEnd > 24_0000d)
|
||||
return -7;
|
||||
if (item.DepthStart > 24_0000d)
|
||||
return -8;
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
using AsbCloudApp.Data.SAUB;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudDb.Model.Subsystems;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
|
||||
|
||||
public abstract class WorkSubsystemOperationTimeCalcAbstract : Work
|
||||
{
|
||||
protected const int idSubsystemTorqueMaster = 65537;
|
||||
protected const int idSubsystemSpinMaster = 65536;
|
||||
protected const int idSubsystemAPDRotor = 11;
|
||||
protected const int idSubsystemAPDSlide = 12;
|
||||
protected const int idSubsystemMse = 2;
|
||||
|
||||
private static TimeSpan obsoleteTime = TimeSpan.FromDays(365 * 100);
|
||||
|
||||
public WorkSubsystemOperationTimeCalcAbstract(string workId)
|
||||
: base(workId)
|
||||
{
|
||||
Timeout = TimeSpan.FromMinutes(30);
|
||||
}
|
||||
|
||||
protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token)
|
||||
{
|
||||
var db = services.GetRequiredService<IAsbCloudDbContext>();
|
||||
db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));
|
||||
|
||||
var telemetryLastDetectedDates = await GetTelemetryLastDetectedDates(services, db, token);
|
||||
|
||||
var count = telemetryLastDetectedDates.Count();
|
||||
var i = 0d;
|
||||
|
||||
foreach (var item in telemetryLastDetectedDates)
|
||||
{
|
||||
onProgressCallback($"Start handling telemetry: {item.IdTelemetry} from {item.DateDetectedLast}", i++ / count);
|
||||
var newOperationsSaub = await OperationTimeAsync(item.IdTelemetry, item.DateDetectedLast, db, token);
|
||||
if (newOperationsSaub.Any())
|
||||
{
|
||||
db.SubsystemOperationTimes.AddRange(newOperationsSaub);
|
||||
await db.SaveChangesAsync(token);
|
||||
}
|
||||
}
|
||||
|
||||
obsoleteTime = TimeSpan.FromDays(3);
|
||||
}
|
||||
|
||||
protected abstract Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token);
|
||||
|
||||
private static async Task<IEnumerable<TelemetryDateLast>> GetTelemetryLastDetectedDates(IServiceProvider services, IAsbCloudDbContext db, CancellationToken token)
|
||||
{
|
||||
var telemetryDataCache = services.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>();
|
||||
|
||||
var updatingTelemetries = telemetryDataCache.GetStat()
|
||||
.Where(tstat => (DateTimeOffset.Now - tstat.DateLast) < obsoleteTime);
|
||||
|
||||
var telemetryIds = updatingTelemetries
|
||||
.Select(t => t.IdTelemetry)
|
||||
.ToArray();
|
||||
|
||||
IEnumerable<TelemetryDateLast> lastDetectedDates = await GetLastSubsystemOperationTimeAsync(db, token);
|
||||
lastDetectedDates = lastDetectedDates
|
||||
.Where(s => telemetryIds.Contains(s.IdTelemetry));
|
||||
|
||||
var result = updatingTelemetries.Select(tstat => new TelemetryDateLast
|
||||
{
|
||||
IdTelemetry = tstat.IdTelemetry,
|
||||
DateDetectedLast = lastDetectedDates.FirstOrDefault(ldd => ldd.IdTelemetry == tstat.IdTelemetry)?.DateDetectedLast
|
||||
?? DateTimeOffset.UnixEpoch,
|
||||
DateTelemetryLast = tstat.DateLast
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<TelemetryDateLast>> GetLastSubsystemOperationTimeAsync(IAsbCloudDbContext db, CancellationToken token)
|
||||
{
|
||||
var result = await db.SubsystemOperationTimes
|
||||
.GroupBy(o => o.IdTelemetry)
|
||||
.Select(g => new TelemetryDateLast
|
||||
{
|
||||
IdTelemetry = g.Key,
|
||||
DateDetectedLast = g.Max(o => o.DateEnd)
|
||||
})
|
||||
.ToArrayAsync(token);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected class TelemetryDateLast
|
||||
{
|
||||
public int IdTelemetry { get; set; }
|
||||
public DateTimeOffset DateDetectedLast { get; set; }
|
||||
public DateTimeOffset DateTelemetryLast { get; internal set; }
|
||||
}
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudDb.Model.Subsystems;
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using AsbCloudInfrastructure.Services.Subsystems;
|
||||
using AsbCloudInfrastructure.Services.Subsystems.Utils;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudInfrastructure.Background.PeriodicWorks;
|
||||
|
||||
public class WorkSubsystemOscillationOperationTimeCalc : WorkSubsystemOperationTimeCalcAbstract
|
||||
{
|
||||
public WorkSubsystemOscillationOperationTimeCalc()
|
||||
: base("Subsystem operation time calc")
|
||||
{
|
||||
Timeout = TimeSpan.FromMinutes(30);
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token)
|
||||
{
|
||||
static int? GetSubsytemId(short? mode, int? state)
|
||||
{
|
||||
// При изменении следующего кода сообщи в Vladimir.Sobolev@nedra.digital
|
||||
if (state == 7 && (mode & 2) > 0)
|
||||
return idSubsystemTorqueMaster;// демпфер
|
||||
|
||||
if (state != 0 && state != 5 && state != 6 && state != 7)
|
||||
return idSubsystemSpinMaster;// осцилляция
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var querySpin =
|
||||
$"select " +
|
||||
$" tspin.date, " +
|
||||
$" tspin.mode, " +
|
||||
$" tspin.state " +
|
||||
$"from ( " +
|
||||
$" select " +
|
||||
$" date, " +
|
||||
$" mode, " +
|
||||
$" lag(mode, 1) over (order by date) as mode_lag, " +
|
||||
$" lead(mode, 1) over (order by date) as mode_lead, " +
|
||||
$" state, " +
|
||||
$" lag(state, 1) over (order by date) as state_lag " +
|
||||
$" from t_telemetry_data_spin " +
|
||||
$" where id_telemetry = {idTelemetry} and date >= '{geDate:u}'" +
|
||||
$" order by date ) as tspin " +
|
||||
$"where mode_lag is null or state_lag is null or (mode != mode_lag and mode_lead != mode_lag) or state != state_lag " +
|
||||
$"order by date;";
|
||||
|
||||
var rows = new List<(int? IdSubsystem, DateTimeOffset Date)>(32);
|
||||
|
||||
using var resultSpin = await ExecuteReaderAsync(db, querySpin, token);
|
||||
int? idSubsystemLast = null;
|
||||
while (resultSpin.Read())
|
||||
{
|
||||
var mode = resultSpin.GetFieldValue<short?>(1);
|
||||
var state = resultSpin.GetFieldValue<short?>(2);
|
||||
var idSubsystem = GetSubsytemId(mode, state);
|
||||
if (idSubsystemLast != idSubsystem)
|
||||
{
|
||||
idSubsystemLast = idSubsystem;
|
||||
var date = resultSpin.GetFieldValue<DateTimeOffset>(0);
|
||||
rows.Add((idSubsystem, date));
|
||||
}
|
||||
}
|
||||
await resultSpin.DisposeAsync();
|
||||
|
||||
if (rows.Count < 2)
|
||||
return Enumerable.Empty<SubsystemOperationTime>();
|
||||
|
||||
var minSpinDate = rows.Min(i => i.Date);
|
||||
var maxSpinDate = rows.Max(i => i.Date);
|
||||
var depthInterpolation = await GetInterpolation(db, idTelemetry, minSpinDate, maxSpinDate, token);
|
||||
|
||||
if (depthInterpolation is null)
|
||||
return Enumerable.Empty<SubsystemOperationTime>();
|
||||
|
||||
var subsystemsOperationTimes = new List<SubsystemOperationTime>(32);
|
||||
|
||||
for (int i = 1; i < rows.Count; i++)
|
||||
{
|
||||
var r0 = rows[i - 1];
|
||||
var r1 = rows[i];
|
||||
if (r0.IdSubsystem is not null && r0.IdSubsystem != r1.IdSubsystem)
|
||||
{
|
||||
var subsystemOperationTime = new SubsystemOperationTime()
|
||||
{
|
||||
IdTelemetry = idTelemetry,
|
||||
IdSubsystem = r0.IdSubsystem.Value,
|
||||
DateStart = r0.Date,
|
||||
DateEnd = r1.Date,
|
||||
DepthStart = depthInterpolation.GetDepth(r0.Date),
|
||||
DepthEnd = depthInterpolation.GetDepth(r1.Date),
|
||||
};
|
||||
|
||||
if (IsValid(subsystemOperationTime))
|
||||
subsystemsOperationTimes.Add(subsystemOperationTime);
|
||||
}
|
||||
}
|
||||
|
||||
return subsystemsOperationTimes;
|
||||
}
|
||||
|
||||
private static async Task<DbDataReader> ExecuteReaderAsync(IAsbCloudDbContext db, string query, CancellationToken token)
|
||||
{
|
||||
var connection = db.Database.GetDbConnection();
|
||||
if (
|
||||
connection?.State is null ||
|
||||
connection.State == ConnectionState.Broken ||
|
||||
connection.State == ConnectionState.Closed)
|
||||
{
|
||||
await db.Database.OpenConnectionAsync(token);
|
||||
connection = db.Database.GetDbConnection();
|
||||
}
|
||||
using var command = connection.CreateCommand();
|
||||
command.CommandText = query;
|
||||
|
||||
var result = await command.ExecuteReaderAsync(token);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool IsValid(SubsystemOperationTime item)
|
||||
{
|
||||
var validateCode = GetValidateErrorCode(item);
|
||||
if (validateCode != 0)
|
||||
{
|
||||
var str = System.Text.Json.JsonSerializer.Serialize(item);
|
||||
Trace.TraceWarning($"Wrong({validateCode}) SubsystemOperationTime: {str}");
|
||||
}
|
||||
return validateCode == 0;
|
||||
}
|
||||
|
||||
private static int GetValidateErrorCode(SubsystemOperationTime item)
|
||||
{
|
||||
if (item.DateStart > item.DateEnd)
|
||||
return -1;
|
||||
if ((item.DateEnd - item.DateStart).TotalHours > 48)
|
||||
return -2;
|
||||
if (item.DepthEnd < item.DepthStart)
|
||||
return -3;
|
||||
if (item.DepthEnd - item.DepthStart > 2000d)
|
||||
return -4;
|
||||
if (item.DepthEnd < 0d)
|
||||
return -5;
|
||||
if (item.DepthStart < 0d)
|
||||
return -6;
|
||||
if (item.DepthEnd > 24_0000d)
|
||||
return -7;
|
||||
if (item.DepthStart > 24_0000d)
|
||||
return -8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static async Task<DepthInterpolation?> GetInterpolation(IAsbCloudDbContext db, int idTelemetry, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
|
||||
{
|
||||
var dataDepthFromSaub = await db.TelemetryDataSaub
|
||||
.Where(d => d.IdTelemetry == idTelemetry)
|
||||
.Where(d => d.DateTime >= dateBegin)
|
||||
.Where(d => d.DateTime <= dateEnd)
|
||||
.Where(d => d.WellDepth > 0)
|
||||
.GroupBy(d => Math.Ceiling(d.WellDepth * 10))
|
||||
.Select(g => new
|
||||
{
|
||||
DateMin = g.Min(d => d.DateTime),
|
||||
DepthMin = g.Min(d => d.WellDepth),
|
||||
})
|
||||
.OrderBy(i => i.DateMin)
|
||||
.ToArrayAsync(token);
|
||||
|
||||
if (!dataDepthFromSaub.Any())
|
||||
return null;
|
||||
|
||||
var depthInterpolation = new DepthInterpolation(dataDepthFromSaub.Select(i => (i.DateMin, i.DepthMin)));
|
||||
return depthInterpolation;
|
||||
}
|
||||
}
|
@ -12,12 +12,10 @@ using AsbCloudApp.Services;
|
||||
using AsbCloudApp.Services.Notifications;
|
||||
using AsbCloudApp.Services.ProcessMaps;
|
||||
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
|
||||
using AsbCloudApp.Services.Subsystems;
|
||||
using AsbCloudApp.Services.WellOperationImport;
|
||||
using AsbCloudDb.Model;
|
||||
using AsbCloudDb.Model.Manuals;
|
||||
using AsbCloudDb.Model.ProcessMaps;
|
||||
using AsbCloudDb.Model.Subsystems;
|
||||
using AsbCloudDb.Model.Trajectory;
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using AsbCloudInfrastructure.Repository;
|
||||
@ -209,7 +207,7 @@ namespace AsbCloudInfrastructure
|
||||
services.AddTransient<IWellOperationRepository, WellOperationRepository>();
|
||||
services.AddTransient<IDailyReportService, DailyReportService>();
|
||||
services.AddTransient<IDetectedOperationService, DetectedOperationService>();
|
||||
services.AddTransient<ISubsystemOperationTimeService, SubsystemOperationTimeService>();
|
||||
services.AddTransient<ISubsystemService, SubsystemService>();
|
||||
services.AddTransient<IScheduleRepository, ScheduleRepository>();
|
||||
services.AddTransient<IRepositoryWellRelated<OperationValueDto>, CrudWellRelatedRepositoryBase<OperationValueDto, OperationValue>>();
|
||||
services.AddTransient<IUserSettingsRepository, UserSettingsRepository>();
|
||||
|
@ -16,7 +16,6 @@ using AsbCloudApp.Data.DailyReport.Blocks.WellOperation;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services.DailyReport;
|
||||
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
|
||||
using AsbCloudApp.Services.Subsystems;
|
||||
using AsbCloudDb.Model;
|
||||
using Mapster;
|
||||
|
||||
@ -29,7 +28,7 @@ public class DailyReportService : IDailyReportService
|
||||
private readonly IDailyReportRepository dailyReportRepository;
|
||||
private readonly IScheduleRepository scheduleRepository;
|
||||
private readonly IWellOperationRepository wellOperationRepository;
|
||||
private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
|
||||
private readonly ISubsystemService subsystemService;
|
||||
private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingService;
|
||||
private readonly IDetectedOperationService detectedOperationService;
|
||||
|
||||
@ -38,7 +37,7 @@ public class DailyReportService : IDailyReportService
|
||||
IDailyReportRepository dailyReportRepository,
|
||||
IScheduleRepository scheduleRepository,
|
||||
IWellOperationRepository wellOperationRepository,
|
||||
ISubsystemOperationTimeService subsystemOperationTimeService,
|
||||
ISubsystemService subsystemService,
|
||||
IProcessMapReportWellDrillingService processMapReportWellDrillingService,
|
||||
IDetectedOperationService detectedOperationService)
|
||||
{
|
||||
@ -47,7 +46,7 @@ public class DailyReportService : IDailyReportService
|
||||
this.dailyReportRepository = dailyReportRepository;
|
||||
this.scheduleRepository = scheduleRepository;
|
||||
this.wellOperationRepository = wellOperationRepository;
|
||||
this.subsystemOperationTimeService = subsystemOperationTimeService;
|
||||
this.subsystemService = subsystemService;
|
||||
this.processMapReportWellDrillingService = processMapReportWellDrillingService;
|
||||
this.detectedOperationService = detectedOperationService;
|
||||
}
|
||||
@ -311,24 +310,24 @@ public class DailyReportService : IDailyReportService
|
||||
|
||||
async Task<IEnumerable<SubsystemRecordDto>> GetSubsystemsAsync()
|
||||
{
|
||||
var subsystemOperationTimesPerWell = await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest
|
||||
var subsystemsStatPerWell = await subsystemService.GetStatAsync(new SubsystemTimeRequest
|
||||
{
|
||||
IdWell = dailyReport.IdWell
|
||||
}, cancellationToken);
|
||||
|
||||
var subsystemOperationTimesPerDay = await subsystemOperationTimeService.GetStatAsync(new SubsystemOperationTimeRequest
|
||||
var subsystemsStatPerDay = await subsystemService.GetStatAsync(new SubsystemTimeRequest
|
||||
{
|
||||
IdWell = dailyReport.IdWell,
|
||||
GtDate = dailyReport.Date,
|
||||
GeDate = dailyReport.Date,
|
||||
LtDate = dailyReport.Date.AddHours(24)
|
||||
}, cancellationToken);
|
||||
|
||||
var subsystems = subsystemOperationTimesPerWell
|
||||
.Select(subsystemOperationTime => new SubsystemRecordDto
|
||||
var subsystems = subsystemsStatPerWell
|
||||
.Select(subsystemStatPerWell => new SubsystemRecordDto
|
||||
{
|
||||
Name = subsystemOperationTime.SubsystemName,
|
||||
UsagePerDay = subsystemOperationTimesPerDay.FirstOrDefault(s => s.IdSubsystem == subsystemOperationTime.IdSubsystem)?.Adapt<SubsystemParametersDto>(),
|
||||
UsagePerWell = subsystemOperationTime.Adapt<SubsystemParametersDto>()
|
||||
Name = subsystemStatPerWell.SubsystemName,
|
||||
UsagePerDay = subsystemsStatPerDay.FirstOrDefault(s => s.IdSubsystem == subsystemStatPerWell.IdSubsystem)?.Adapt<SubsystemParametersDto>(),
|
||||
UsagePerWell = subsystemStatPerWell.Adapt<SubsystemParametersDto>()
|
||||
}).ToList();
|
||||
|
||||
if (dailyReport.SubsystemBlock?.Subsystems != null && dailyReport.SubsystemBlock.Subsystems.Any())
|
||||
|
@ -1,397 +0,0 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.DetectedOperation;
|
||||
using AsbCloudApp.Data.Subsystems;
|
||||
using AsbCloudApp.Exceptions;
|
||||
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;
|
||||
|
||||
/// Todo: Выделить репозиторий
|
||||
internal class SubsystemOperationTimeService : ISubsystemOperationTimeService
|
||||
{
|
||||
private readonly IAsbCloudDbContext db;
|
||||
private readonly IWellService wellService;
|
||||
private readonly ICrudRepository<SubsystemDto> 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<SubsystemDto> subsystemService, IDetectedOperationService detectedOperationService)
|
||||
{
|
||||
this.db = db;
|
||||
this.wellService = wellService;
|
||||
this.subsystemService = subsystemService;
|
||||
this.detectedOperationService = detectedOperationService;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<int> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<SubsystemOperationTimeDto>> 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<IEnumerable<SubsystemOperationTimeDto>> GetOperationTimeAsync(SubsystemOperationTimeRequest request, WellDto well, CancellationToken token)
|
||||
{
|
||||
var query = BuildQuery(request, well);
|
||||
IEnumerable<SubsystemOperationTime> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<SubsystemStatDto>> 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<SubsystemStatDto>();
|
||||
|
||||
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<SubsystemStatDto>();
|
||||
|
||||
var statList = CalcStat(subsystemsTimes, operationsSummaries);
|
||||
return statList;
|
||||
}
|
||||
|
||||
private static IEnumerable<SubsystemOperationTime> TrimOperation(IEnumerable<SubsystemOperationTime> data, DateTimeOffset? gtDate, DateTimeOffset? ltDate)
|
||||
{
|
||||
if (!ltDate.HasValue && !gtDate.HasValue)
|
||||
return data.Select(d => d.Adapt<SubsystemOperationTime>());
|
||||
|
||||
var items = data.Select((item) =>
|
||||
{
|
||||
var operationTime = item.Adapt<SubsystemOperationTime>();
|
||||
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<SubsystemStatDto> CalcStat(
|
||||
IEnumerable<SubsystemOperationTimeDto> subsystemsTimes,
|
||||
IEnumerable<OperationsSummaryDto> operationsSummaries)
|
||||
{
|
||||
var groupedSubsystemsTimes = subsystemsTimes
|
||||
.OrderBy(o => o.Id)
|
||||
.GroupBy(o => o.IdSubsystem);
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
operationsSummaries = operationsSummaries.Where(o => o.EnabledSubsystems == 1);
|
||||
|
||||
var (_, sumOprationsDurationHours) = AggregateOperationsSummaries(IdSubsystemAKBRotor, operationsSummaries);
|
||||
apdSum.KUsage = sumOprationsDurationHours / apdSum.SumOperationDurationHours;
|
||||
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<OperationsSummaryDto> 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<OperationsSummaryDto> 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<SubsystemActiveWellStatDto>> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(IEnumerable<int> 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<IEnumerable<SubsystemActiveWellStatDto>> GetStatAsync(IEnumerable<WellDto> wells, DateTime? gtDate, DateTime? ltDate, CancellationToken token)
|
||||
{
|
||||
if (!wells.Any())
|
||||
return Enumerable.Empty<SubsystemActiveWellStatDto>();
|
||||
|
||||
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<int> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<DatesRangeDto?> 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<SubsystemOperationTime> 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<SubsystemOperationTimeDto>();
|
||||
var hours = timezoneHours ?? operationTime.Telemetry.TimeZone.Hours;
|
||||
dto.DateStart = operationTime.DateStart.ToRemoteDateTime(hours);
|
||||
dto.DateEnd = operationTime.DateEnd.ToRemoteDateTime(hours);
|
||||
return dto;
|
||||
}
|
||||
}
|
218
AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs
Normal file
218
AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs
Normal file
@ -0,0 +1,218 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.DetectedOperation;
|
||||
using AsbCloudApp.Data.Subsystems;
|
||||
using AsbCloudApp.Exceptions;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudDb.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Subsystems;
|
||||
|
||||
internal class SubsystemService : ISubsystemService
|
||||
{
|
||||
private const int IdEnabledSubsystemSpinMaster = 128;
|
||||
|
||||
private const int IdSubsystemAPD = 1;
|
||||
private const int IdSubsystemAPDRotor = 11;
|
||||
private const int IdSubsystemAPDSlide = 12;
|
||||
private const int IdSubsystemSpinMaster = 65536;
|
||||
|
||||
private readonly ICrudRepository<SubsystemDto> subsystemRepository;
|
||||
|
||||
private readonly IWellService wellService;
|
||||
private readonly IDetectedOperationService detectedOperationService;
|
||||
private readonly ITelemetryDataSaubService telemetryDataSaubService;
|
||||
|
||||
public SubsystemService(ICrudRepository<SubsystemDto> subsystemRepository,
|
||||
IWellService wellService,
|
||||
IDetectedOperationService detectedOperationService,
|
||||
ITelemetryDataSaubService telemetryDataSaubService)
|
||||
{
|
||||
this.wellService = wellService;
|
||||
this.subsystemRepository = subsystemRepository;
|
||||
this.detectedOperationService = detectedOperationService;
|
||||
this.telemetryDataSaubService = telemetryDataSaubService;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SubsystemStatDto>> GetStatAsync(SubsystemTimeRequest 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 detectedOperationSummaryRequest = new DetectedOperationSummaryRequest
|
||||
{
|
||||
IdsTelemetries = new[] { well.IdTelemetry!.Value },
|
||||
IdsOperationCategories = WellOperationCategory.MechanicalDrillingSubIds,
|
||||
|
||||
GeDateStart = request.GeDate,
|
||||
LeDateStart = request.LtDate,
|
||||
|
||||
GeDepthStart = request.GtDepth,
|
||||
LeDepthStart = request.LtDepth,
|
||||
};
|
||||
|
||||
var operations = await detectedOperationService.GetOperationSummaryAsync(detectedOperationSummaryRequest,
|
||||
token);
|
||||
|
||||
if (!operations.Any())
|
||||
return Enumerable.Empty<SubsystemStatDto>();
|
||||
|
||||
var stat = await CalcStatAsync(operations, token);
|
||||
return stat;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SubsystemActiveWellStatDto>> 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<IEnumerable<SubsystemActiveWellStatDto>> GetStatByActiveWells(IEnumerable<int> 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<IEnumerable<SubsystemStatDto>> CalcStatAsync(IEnumerable<OperationsSummaryDto> operations, CancellationToken token)
|
||||
{
|
||||
var subsystems = await subsystemRepository.GetAllAsync(token);
|
||||
|
||||
var groupedOperations = operations
|
||||
.GroupBy(o => o.IdCategory);
|
||||
|
||||
var stat = groupedOperations.Select(groupOperations =>
|
||||
{
|
||||
var idSubsystem = groupOperations.Key switch
|
||||
{
|
||||
WellOperationCategory.IdRotor => IdSubsystemAPDRotor,
|
||||
WellOperationCategory.IdSlide => IdSubsystemAPDSlide,
|
||||
_ => throw new ArgumentException($"IdCategory: {groupOperations.Key} does not supported in this method",
|
||||
nameof(groupOperations.Key)),
|
||||
};
|
||||
|
||||
var operationsWithEnableSubsystems = groupOperations.Where(o => o.EnabledSubsystems >= 1);
|
||||
|
||||
var subsystemStat = new SubsystemStatDto
|
||||
{
|
||||
IdSubsystem = idSubsystem,
|
||||
SubsystemName = subsystems.FirstOrDefault(s => s.Id == idSubsystem)?.Name ?? "unknown",
|
||||
UsedTimeHours = operationsWithEnableSubsystems.Sum(o => o.SumDurationHours),
|
||||
SumOperationDepthInterval = groupOperations.Sum(o => o.SumDepthIntervals),
|
||||
SumOperationDurationHours = groupOperations.Sum(o => o.SumDurationHours),
|
||||
SumDepthInterval = operationsWithEnableSubsystems.Sum(o => o.SumDepthIntervals),
|
||||
OperationCount = operationsWithEnableSubsystems.Sum(o => o.Count),
|
||||
};
|
||||
|
||||
subsystemStat.KUsage = subsystemStat.SumDepthInterval / subsystemStat.SumOperationDepthInterval;
|
||||
|
||||
return subsystemStat;
|
||||
});
|
||||
|
||||
var apdSlidePart = stat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAPDSlide);
|
||||
|
||||
if (apdSlidePart is not null)
|
||||
{
|
||||
var operationsWithSpinMaster = operations.Where(s => s.EnabledSubsystems == IdEnabledSubsystemSpinMaster);
|
||||
|
||||
var spin = new SubsystemStatDto
|
||||
{
|
||||
IdSubsystem = IdSubsystemSpinMaster,
|
||||
SubsystemName = subsystems.FirstOrDefault(s => s.Id == IdSubsystemSpinMaster)?.Name ?? "unknown",
|
||||
UsedTimeHours = operationsWithSpinMaster.Sum(o => o.SumDurationHours),
|
||||
SumOperationDepthInterval = apdSlidePart.SumOperationDepthInterval,
|
||||
SumOperationDurationHours = apdSlidePart.SumOperationDurationHours,
|
||||
SumDepthInterval = operationsWithSpinMaster.Sum(o => o.SumDepthIntervals),
|
||||
OperationCount = operationsWithSpinMaster.Sum(s => s.Count)
|
||||
};
|
||||
|
||||
spin.KUsage = spin.SumDepthInterval / spin.SumOperationDepthInterval;
|
||||
|
||||
stat = stat.Append(spin);
|
||||
}
|
||||
|
||||
var apdParts = stat.Where(s => s.IdSubsystem is IdSubsystemAPDRotor or IdSubsystemAPDSlide);
|
||||
|
||||
if (!apdParts.Any())
|
||||
return stat;
|
||||
|
||||
var apdSum = new SubsystemStatDto
|
||||
{
|
||||
IdSubsystem = IdSubsystemAPD,
|
||||
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;
|
||||
|
||||
stat = stat.Append(apdSum).OrderBy(m => m.IdSubsystem);
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<SubsystemActiveWellStatDto>> GetStatAsync(IEnumerable<WellDto> wells, DateTime? gtDate, DateTime? ltDate,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (!wells.Any())
|
||||
return Enumerable.Empty<SubsystemActiveWellStatDto>();
|
||||
|
||||
var idsTelemetries = wells
|
||||
.Where(w => w.IdTelemetry is not null)
|
||||
.Select(w => w.IdTelemetry!.Value)
|
||||
.Distinct();
|
||||
|
||||
var wellsStat = new List<SubsystemActiveWellStatDto>();
|
||||
|
||||
foreach (var well in wells)
|
||||
{
|
||||
var hoursOffset = well.Timezone.Hours;
|
||||
|
||||
var dateRange = telemetryDataSaubService.GetRange(well.Id);
|
||||
|
||||
var beginUTC = gtDate.HasValue
|
||||
? gtDate.Value.ToUtcDateTimeOffset(hoursOffset)
|
||||
: dateRange.From.ToUtcDateTimeOffset(hoursOffset);
|
||||
|
||||
var endUTC = ltDate.HasValue
|
||||
? ltDate.Value.ToUtcDateTimeOffset(hoursOffset)
|
||||
: dateRange.To.ToUtcDateTimeOffset(hoursOffset);
|
||||
|
||||
var operations = await detectedOperationService
|
||||
.GetOperationSummaryAsync(new()
|
||||
{
|
||||
IdsTelemetries = idsTelemetries,
|
||||
IdsOperationCategories = WellOperationCategory.MechanicalDrillingSubIds,
|
||||
GeDateStart = beginUTC,
|
||||
LeDateEnd = endUTC,
|
||||
}, token);
|
||||
|
||||
var wellStat = new SubsystemActiveWellStatDto { Well = well };
|
||||
|
||||
var telemetryOperations = operations.Where(o => o.IdTelemetry == well.IdTelemetry);
|
||||
|
||||
if (!telemetryOperations.Any())
|
||||
continue;
|
||||
|
||||
var subsystemStat = await CalcStatAsync(telemetryOperations, token);
|
||||
|
||||
if (!subsystemStat.Any())
|
||||
continue;
|
||||
|
||||
wellStat.SubsystemAPD = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemAPD);
|
||||
wellStat.SubsystemSpinMaster = subsystemStat.FirstOrDefault(s => s.IdSubsystem == IdSubsystemSpinMaster);
|
||||
}
|
||||
|
||||
return wellsStat;
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
using AsbCloudDb.Model.Subsystems;
|
||||
using System;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services.Subsystems
|
||||
{
|
||||
public class SubsystemDetector
|
||||
{
|
||||
private readonly int idTelemetry;
|
||||
private readonly int idSubsystem;
|
||||
private readonly Func<short?, bool> isEnable;
|
||||
private readonly Func<SubsystemOperationTime, bool> isValid;
|
||||
(bool isEnable, DateTimeOffset date, float depth) pre = default;
|
||||
|
||||
public SubsystemDetector(
|
||||
int idTelemetry,
|
||||
int idSubsystem,
|
||||
Func<short?, bool> isEnable,
|
||||
Func<SubsystemOperationTime, bool> isValid)
|
||||
{
|
||||
this.idTelemetry = idTelemetry;
|
||||
this.idSubsystem = idSubsystem;
|
||||
this.isEnable = isEnable;
|
||||
this.isValid = isValid;
|
||||
}
|
||||
|
||||
public bool TryDetect(short? mode, DateTimeOffset date, float depth, out SubsystemOperationTime? subsystemOperationTime)
|
||||
{
|
||||
var isEnable = this.isEnable(mode);
|
||||
|
||||
if (!pre.isEnable && isEnable)
|
||||
{
|
||||
pre = (true, date, depth);
|
||||
}
|
||||
else if (pre.isEnable && !isEnable)
|
||||
{
|
||||
var detected = new SubsystemOperationTime
|
||||
{
|
||||
IdTelemetry = idTelemetry,
|
||||
IdSubsystem = idSubsystem,
|
||||
DateStart = pre.date,
|
||||
DateEnd = date,
|
||||
DepthStart = pre.depth,
|
||||
DepthEnd = depth,
|
||||
};
|
||||
pre.isEnable = false;
|
||||
if (isValid(detected))
|
||||
{
|
||||
subsystemOperationTime = detected;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
subsystemOperationTime = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,9 +4,7 @@ using AsbCloudApp.Data.WITS;
|
||||
using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudApp.Services.Subsystems;
|
||||
using AsbCloudInfrastructure.Background;
|
||||
using AsbCloudInfrastructure.Services.SAUB;
|
||||
using Mapster;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
@ -35,7 +33,7 @@ public class WellInfoService
|
||||
var wellService = services.GetRequiredService<IWellService>();
|
||||
var operationsStatService = services.GetRequiredService<IOperationsStatService>();
|
||||
var processMapPlanWellDrillingRepository = services.GetRequiredService<IProcessMapPlanRepository<ProcessMapPlanWellDrillingDto>>();
|
||||
var subsystemOperationTimeService = services.GetRequiredService<ISubsystemOperationTimeService>();
|
||||
var subsystemService = services.GetRequiredService<ISubsystemService>();
|
||||
var telemetryDataSaubCache = services.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>();
|
||||
var messageHub = services.GetRequiredService<IIntegrationEventHandler<UpdateWellInfoEvent>>();
|
||||
|
||||
@ -57,7 +55,7 @@ public class WellInfoService
|
||||
|
||||
var operationsStat = await operationsStatService.GetWellsStatAsync(wellsIds, token);
|
||||
|
||||
var subsystemStat = await subsystemOperationTimeService
|
||||
var subsystemStat = await subsystemService
|
||||
.GetStatByActiveWells(wellsIds, token);
|
||||
subsystemStat = subsystemStat.ToArray();
|
||||
|
||||
@ -157,7 +155,7 @@ public class WellInfoService
|
||||
};
|
||||
|
||||
var wellSubsystemStat = subsystemStat.FirstOrDefault(s => s.Well.Id == well.Id);
|
||||
wellMapInfo.SaubUsage = wellSubsystemStat?.SubsystemAKB?.KUsage ?? 0d;
|
||||
wellMapInfo.SaubUsage = wellSubsystemStat?.SubsystemAPD?.KUsage ?? 0d;
|
||||
wellMapInfo.SpinUsage = wellSubsystemStat?.SubsystemSpinMaster?.KUsage ?? 0d;
|
||||
wellMapInfo.TorqueKUsage = wellSubsystemStat?.SubsystemTorqueMaster?.KUsage ?? 0d;
|
||||
wellMapInfo.TvdLagDays = wellOperationsStat?.TvdLagDays;
|
||||
|
@ -32,8 +32,6 @@ namespace AsbCloudInfrastructure
|
||||
backgroundWorker.Add<WorkToDeleteOldReports>(TimeSpan.FromDays(1));
|
||||
backgroundWorker.Add<WellInfoService.WorkWellInfoUpdate>(TimeSpan.FromMinutes(30));
|
||||
backgroundWorker.Add<WorkOperationDetection>(TimeSpan.FromMinutes(15));
|
||||
backgroundWorker.Add<WorkSubsystemAbfOperationTimeCalc>(TimeSpan.FromMinutes(30));
|
||||
backgroundWorker.Add<WorkSubsystemOscillationOperationTimeCalc>(TimeSpan.FromMinutes(30));
|
||||
backgroundWorker.Add<WorkLimitingParameterCalc>(TimeSpan.FromMinutes(30));
|
||||
backgroundWorker.Add(MakeMemoryMonitoringWork(), TimeSpan.FromMinutes(1));
|
||||
|
||||
|
@ -17,7 +17,6 @@ using AsbCloudApp.Repositories;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudApp.Services.ProcessMaps.WellDrilling;
|
||||
using AsbCloudApp.Services.Subsystems;
|
||||
using AsbCloudInfrastructure.Services.DailyReport;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
@ -191,7 +190,7 @@ public class DailyReportServiceTest
|
||||
}
|
||||
};
|
||||
|
||||
private readonly SubsystemStatDto fakeStatSubsystemOperationTime = new()
|
||||
private readonly SubsystemStatDto fakeSubsystemsStat = new()
|
||||
{
|
||||
SubsystemName = "АПД",
|
||||
SumDepthInterval = 250,
|
||||
@ -204,7 +203,7 @@ public class DailyReportServiceTest
|
||||
private readonly IDailyReportRepository dailyReportRepositoryMock = Substitute.For<IDailyReportRepository>();
|
||||
private readonly IScheduleRepository scheduleRepositoryMock = Substitute.For<IScheduleRepository>();
|
||||
private readonly IWellOperationRepository wellOperationRepositoryMock = Substitute.For<IWellOperationRepository>();
|
||||
private readonly ISubsystemOperationTimeService subsystemOperationTimeServiceMock = Substitute.For<ISubsystemOperationTimeService>();
|
||||
private readonly ISubsystemService subsystemServiceMock = Substitute.For<ISubsystemService>();
|
||||
private readonly IProcessMapReportWellDrillingService processMapReportWellDrillingServiceMock = Substitute.For<IProcessMapReportWellDrillingService>();
|
||||
private readonly IDetectedOperationService detectedOperationServiceMock = Substitute.For<IDetectedOperationService>();
|
||||
|
||||
@ -245,7 +244,7 @@ public class DailyReportServiceTest
|
||||
dailyReportRepositoryMock,
|
||||
scheduleRepositoryMock,
|
||||
wellOperationRepositoryMock,
|
||||
subsystemOperationTimeServiceMock,
|
||||
subsystemServiceMock,
|
||||
processMapReportWellDrillingServiceMock,
|
||||
detectedOperationServiceMock);
|
||||
|
||||
@ -276,8 +275,8 @@ public class DailyReportServiceTest
|
||||
detectedOperationServiceMock.GetAsync(Arg.Any<DetectedOperationRequest>(), Arg.Any<CancellationToken>())
|
||||
.ReturnsForAnyArgs(fakeWellOperationSlipsTime);
|
||||
|
||||
subsystemOperationTimeServiceMock.GetStatAsync(Arg.Any<SubsystemOperationTimeRequest>(), Arg.Any<CancellationToken>())
|
||||
.ReturnsForAnyArgs(new[] { fakeStatSubsystemOperationTime });
|
||||
subsystemServiceMock.GetStatAsync(Arg.Any<SubsystemTimeRequest>(), Arg.Any<CancellationToken>())
|
||||
.ReturnsForAnyArgs(new[] { fakeSubsystemsStat });
|
||||
|
||||
scheduleRepositoryMock.GetAsync(idWell, dateDailyReport, Arg.Any<CancellationToken>())
|
||||
.ReturnsForAnyArgs(new[] { fakeShedule });
|
||||
|
92
AsbCloudWebApi/Controllers/Subsystems/SubsystemController.cs
Normal file
92
AsbCloudWebApi/Controllers/Subsystems/SubsystemController.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.Subsystems;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers.Subsystems
|
||||
{
|
||||
/// <summary>
|
||||
/// Наработка подсистем
|
||||
/// </summary>
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
public class SubsystemController : ControllerBase
|
||||
{
|
||||
private readonly ISubsystemService subsystemService;
|
||||
private readonly ITelemetryDataSaubService telemetryDataSaubService;
|
||||
private readonly IWellService wellService;
|
||||
|
||||
public SubsystemController(
|
||||
ISubsystemService subsystemService,
|
||||
IWellService wellService,
|
||||
ITelemetryDataSaubService telemetryDataSaubService)
|
||||
{
|
||||
this.subsystemService = subsystemService;
|
||||
this.wellService = wellService;
|
||||
this.telemetryDataSaubService = telemetryDataSaubService;
|
||||
}
|
||||
/// <summary>
|
||||
/// получить статистику
|
||||
/// </summary>
|
||||
[HttpGet("stat")]
|
||||
[ProducesResponseType(typeof(IEnumerable<SubsystemStatDto>), (int)System.Net.HttpStatusCode.OK)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
|
||||
public async Task<IActionResult> GetStatAsync([FromQuery] SubsystemTimeRequest request, CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccessToWellAsync(request.IdWell, token))
|
||||
return Forbid();
|
||||
var subsystemResult = await subsystemService.GetStatAsync(request, token);
|
||||
return Ok(subsystemResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// получить период, за который будет рассчитываться статистика
|
||||
/// </summary>
|
||||
[HttpGet("operationsPeriod/{idWell}")]
|
||||
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetStatDateRangeAsync([FromRoute] int idWell, CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccessToWellAsync(idWell, token))
|
||||
return Forbid();
|
||||
|
||||
var dateRange = telemetryDataSaubService.GetRange(idWell);
|
||||
|
||||
return Ok(dateRange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// получить статистику по активным скважинам
|
||||
/// </summary>
|
||||
/// <param name="gtDate"> Больше или равно дате </param>
|
||||
/// <param name="ltDate"> Меньше или равно дате </param>
|
||||
/// <param name="token"> Токен </param>
|
||||
/// <returns> </returns>
|
||||
[HttpGet("statByActiveWell")]
|
||||
[ProducesResponseType(typeof(IEnumerable<SubsystemActiveWellStatDto>), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetStatByWellAsync(DateTime? gtDate, DateTime? ltDate, CancellationToken token)
|
||||
{
|
||||
var idCompany = User.GetCompanyId();
|
||||
if (!idCompany.HasValue)
|
||||
return Forbid();
|
||||
var subsystemResult = await subsystemService.GetStatByActiveWells(idCompany.Value, gtDate, ltDate, token);
|
||||
return Ok(subsystemResult);
|
||||
}
|
||||
|
||||
private async Task<bool> UserHasAccessToWellAsync(int idWell, CancellationToken token)
|
||||
{
|
||||
var idCompany = User.GetCompanyId();
|
||||
if (idCompany is not null &&
|
||||
await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, token)
|
||||
.ConfigureAwait(false))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
using AsbCloudApp.Data;
|
||||
using AsbCloudApp.Data.Subsystems;
|
||||
using AsbCloudApp.Requests;
|
||||
using AsbCloudApp.Services;
|
||||
using AsbCloudApp.Services.Subsystems;
|
||||
using AsbCloudDb.Model;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudWebApi.Controllers.Subsystems
|
||||
{
|
||||
/// <summary>
|
||||
/// Наработка подсистем
|
||||
/// </summary>
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
public class SubsystemOperationTimeController : ControllerBase
|
||||
{
|
||||
private readonly ISubsystemOperationTimeService subsystemOperationTimeService;
|
||||
private readonly ITelemetryDataSaubService telemetryDataSaubService;
|
||||
private readonly IWellService wellService;
|
||||
|
||||
private readonly Dictionary<int, string> subsystemNames = new()
|
||||
{
|
||||
{ 1, "SAUB" },
|
||||
{ 65537, "Torque Master" },
|
||||
{ 65536, "Spin Master" }
|
||||
};
|
||||
|
||||
public SubsystemOperationTimeController(
|
||||
ISubsystemOperationTimeService subsystemOperationTimeService,
|
||||
IWellService wellService,
|
||||
ITelemetryDataSaubService telemetryDataSaubService)
|
||||
{
|
||||
this.subsystemOperationTimeService = subsystemOperationTimeService;
|
||||
this.wellService = wellService;
|
||||
this.telemetryDataSaubService = telemetryDataSaubService;
|
||||
}
|
||||
/// <summary>
|
||||
/// получить статистику
|
||||
/// </summary>
|
||||
[HttpGet("stat")]
|
||||
[ProducesResponseType(typeof(IEnumerable<SubsystemStatDto>), (int)System.Net.HttpStatusCode.OK)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
|
||||
public async Task<IActionResult> GetStatAsync([FromQuery] SubsystemOperationTimeRequest request, CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccessToWellAsync(request.IdWell, token))
|
||||
return Forbid();
|
||||
var subsystemResult = await subsystemOperationTimeService.GetStatAsync(request, token);
|
||||
return Ok(subsystemResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// получить период, за который будет рассчитываться статистика
|
||||
/// </summary>
|
||||
[HttpGet("operationsPeriod/{idWell}")]
|
||||
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetStatDateRangeAsync([FromRoute] int idWell, CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccessToWellAsync(idWell, token))
|
||||
return Forbid();
|
||||
|
||||
var dateRange = telemetryDataSaubService.GetRange(idWell);
|
||||
|
||||
return Ok(dateRange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// получить статистику по активным скважинам
|
||||
/// </summary>
|
||||
/// <param name="GtDate"> Больше или равно дате </param>
|
||||
/// <param name="LtDate"> Меньше или равно дате </param>
|
||||
/// <param name="token"> Токен </param>
|
||||
/// <returns> </returns>
|
||||
[HttpGet("statByActiveWell")]
|
||||
[ProducesResponseType(typeof(IEnumerable<SubsystemActiveWellStatDto>), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetStatByWellAsync(DateTime? GtDate, DateTime? LtDate, CancellationToken token)
|
||||
{
|
||||
var idCompany = User.GetCompanyId();
|
||||
if (!idCompany.HasValue)
|
||||
return Forbid();
|
||||
var subsystemResult = await subsystemOperationTimeService.GetStatByActiveWells(idCompany.Value, GtDate, LtDate, token);
|
||||
return Ok(subsystemResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// получить доступный диапазон дат наработки подсистемы.
|
||||
/// </summary>
|
||||
[HttpGet("datesRange")]
|
||||
[ProducesResponseType(typeof(DatesRangeDto), (int)System.Net.HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> GetDateRangeOperationTimeAsync([FromQuery] SubsystemOperationTimeRequest request, CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccessToWellAsync(request.IdWell, token))
|
||||
return Forbid();
|
||||
var result = await subsystemOperationTimeService.GetDateRangeOperationTimeAsync(request, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// получить список наработок подсистем
|
||||
/// </summary>
|
||||
[HttpGet("operationTime")]
|
||||
[ProducesResponseType(typeof(IEnumerable<SubsystemOperationTimeDto>), (int)System.Net.HttpStatusCode.OK)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
|
||||
public async Task<IActionResult> GetOperationTimeAsync(
|
||||
[FromQuery] SubsystemOperationTimeRequest request,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccessToWellAsync(request.IdWell, token))
|
||||
return Forbid();
|
||||
|
||||
var result = await subsystemOperationTimeService.GetOperationTimeAsync(request, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удалить наработки.
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete]
|
||||
[Permission]
|
||||
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
|
||||
public async Task<IActionResult> DeleteAsync(
|
||||
[FromQuery] SubsystemOperationTimeRequest request,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (!await UserHasAccessToWellAsync(request.IdWell, token))
|
||||
return Forbid();
|
||||
var result = await subsystemOperationTimeService.DeleteAsync(request, token);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение словаря названий подсистем
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("names")]
|
||||
[ProducesResponseType(typeof(Dictionary<int, string>), (int)System.Net.HttpStatusCode.OK)]
|
||||
public IActionResult GetSubsystemsNames()
|
||||
{
|
||||
return Ok(subsystemNames);
|
||||
}
|
||||
|
||||
protected async Task<bool> UserHasAccessToWellAsync(int idWell, CancellationToken token)
|
||||
{
|
||||
var idCompany = User.GetCompanyId();
|
||||
if (idCompany is not null &&
|
||||
await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, idWell, token)
|
||||
.ConfigureAwait(false))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user