Замена базовых классов. Уточнения в задании по не заполненным целевым значениям.

This commit is contained in:
ngfrolov 2022-06-14 15:35:31 +05:00
parent 805bb4f3ae
commit 674a5e0e71
18 changed files with 157 additions and 90 deletions

View File

@ -2,6 +2,7 @@
namespace AsbCloudApp.Data namespace AsbCloudApp.Data
{ {
#nullable enable
/// <summary> /// <summary>
/// Автоматически определяемая операция /// Автоматически определяемая операция
/// </summary> /// </summary>
@ -61,13 +62,17 @@ namespace AsbCloudApp.Data
/// <summary> /// <summary>
/// Бурильщик /// Бурильщик
/// </summary> /// </summary>
public DrillerDto Driller { get; set; } public DrillerDto? Driller { get; set; }
/// <summary> /// <summary>
/// Целевые/нормативные показатели /// Целевые/нормативные показатели
/// </summary> /// </summary>
public OperationValueDto OperationValue { get; set; } public OperationValueDto? OperationValue { get; set; }
/// <summary>
/// Ключевой параметр операции
/// </summary>
public double Value { get; set; } public double Value { get; set; }
} }
#nullable disable
} }

View File

@ -4,12 +4,16 @@ using System.Linq;
namespace AsbCloudApp.Data namespace AsbCloudApp.Data
{ {
#nullable enable
/// <summary>
/// Статистика по операциям бурильщика
/// </summary>
public class DetectedOperationStatDto public class DetectedOperationStatDto
{ {
/// <summary> /// <summary>
/// Бурильцик /// Бурильщик
/// </summary> /// </summary>
public DrillerDto Driller { get; set; } public DrillerDto? Driller { get; set; }
/// <summary> /// <summary>
/// Количество операции /// Количество операции
@ -17,24 +21,24 @@ namespace AsbCloudApp.Data
public int Count { get; set; } public int Count { get; set; }
/// <summary> /// <summary>
/// Среднее по целевым показателям /// Среднее по ключевому показателю
/// </summary> /// </summary>
public double Average { get; set; } public double AverageValue { get; set; }
/// <summary>
/// Среднее целевого показателя
/// </summary>
public double? AverageTargetValue { get; set; }
/// <summary> /// <summary>
/// Коэффициент эффективности /// Коэффициент эффективности
/// </summary> /// </summary>
public double Efficiency { get; set; } public double? Efficiency { get; set; }
/// <summary>
/// Среднее по ключевому показателю
/// </summary>
public double AverageByParam { get; set; }
/// <summary> /// <summary>
/// Коэффициент потерь /// Коэффициент потерь
/// </summary> /// </summary>
public double Loss { get; set; } public double? Loss { get; set; }
} }
/// <summary> /// <summary>
@ -45,8 +49,9 @@ namespace AsbCloudApp.Data
/// <summary> /// <summary>
/// Список всех операций /// Список всех операций
/// </summary> /// </summary>
public IEnumerable<DetectedOperationDto> List { get; set; } public IEnumerable<DetectedOperationDto> Operations { get; set; }
public ICollection<DetectedOperationStatDto> Stats { get; set; } public IEnumerable<DetectedOperationStatDto> Stats { get; set; }
} }
#nullable disable
} }

View File

@ -5,7 +5,7 @@ namespace AsbCloudApp.Data
/// <summary> /// <summary>
/// Описание целевых/нормативных показателей операций /// Описание целевых/нормативных показателей операций
/// </summary> /// </summary>
public class OperationValueDto : IId public class OperationValueDto : IId, IWellRelated
{ {
/// <summary> /// <summary>
/// Идентификатор в БД /// Идентификатор в БД

View File

@ -37,5 +37,10 @@ namespace AsbCloudApp.Data
/// Конец бурения /// Конец бурения
/// </summary> /// </summary>
public DateTime DrillEnd { get; set; } public DateTime DrillEnd { get; set; }
/// <summary>
/// Бурильщик
/// </summary>
public DrillerDto Driller { get; set; }
} }
} }

View File

@ -5,7 +5,7 @@ namespace AsbCloudApp.Data
/// <summary> /// <summary>
/// DTO времени /// DTO времени
/// </summary> /// </summary>
public class TimeDto public class TimeDto: IComparable<TimeDto>
{ {
private int hour = 0; private int hour = 0;
private int minute = 0; private int minute = 0;
@ -51,6 +51,11 @@ namespace AsbCloudApp.Data
} }
} }
/// <summary>
/// Кол-во секунд с начала суток
/// </summary>
public int TotalSeconds => (Hour * 60 + minute) * 60 + second;
/// <inheritdoc/> /// <inheritdoc/>
public TimeDto() public TimeDto()
{ } { }
@ -71,6 +76,14 @@ namespace AsbCloudApp.Data
second = time.Second; second = time.Second;
} }
/// <inheritdoc/>
public TimeDto(DateTime fullDate)
{
hour = fullDate.Hour;
minute = fullDate.Minute;
second = fullDate.Second;
}
/// <summary> /// <summary>
/// Makes System.TimeOnly /// Makes System.TimeOnly
/// </summary> /// </summary>
@ -83,5 +96,27 @@ namespace AsbCloudApp.Data
var str = $"{Hour:00}:{Minute:00}:{Second:00}"; var str = $"{Hour:00}:{Minute:00}:{Second:00}";
return str; return str;
} }
/// <inheritdoc/>
public static bool operator ==(TimeDto a, TimeDto b) => a?.TotalSeconds == b?.TotalSeconds;
/// <inheritdoc/>
public static bool operator !=(TimeDto a, TimeDto b) => !(a == b);
/// <inheritdoc/>
public static bool operator <=(TimeDto a, TimeDto b) => a.TotalSeconds <= b.TotalSeconds;
/// <inheritdoc/>
public static bool operator >=(TimeDto a, TimeDto b) => a.TotalSeconds >= b.TotalSeconds;
/// <inheritdoc/>
public static bool operator <(TimeDto a, TimeDto b) => a.TotalSeconds < b.TotalSeconds;
/// <inheritdoc/>
public static bool operator >(TimeDto a, TimeDto b) => a.TotalSeconds > b.TotalSeconds;
/// <inheritdoc/>
public int CompareTo(TimeDto other)
=> TotalSeconds - other.TotalSeconds;
} }
} }

View File

@ -19,7 +19,7 @@ namespace AsbCloudApp.Services
/// <param name="idWell">id скважины</param> /// <param name="idWell">id скважины</param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns>emptyList if nothing found</returns> /// <returns>emptyList if nothing found</returns>
Task<IEnumerable<Tdto>> GetAllAsync(int idWell, CancellationToken token); Task<IEnumerable<Tdto>> GetByIdWellAsync(int idWell, CancellationToken token);
/// <summary> /// <summary>
/// Получение всех записей по нескольким скважинам /// Получение всех записей по нескольким скважинам
@ -27,7 +27,7 @@ namespace AsbCloudApp.Services
/// <param name="idsWells">id скважин</param> /// <param name="idsWells">id скважин</param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns>emptyList if nothing found</returns> /// <returns>emptyList if nothing found</returns>
Task<IEnumerable<Tdto>> GetAllAsync(IEnumerable<int> idsWells, CancellationToken token); Task<IEnumerable<Tdto>> GetByIdWellAsync(IEnumerable<int> idsWells, CancellationToken token);
} }
#nullable disable #nullable disable
} }

View File

@ -2,7 +2,7 @@
namespace AsbCloudApp.Services namespace AsbCloudApp.Services
{ {
public interface IOperationValueService : ICrudService<OperationValueDto> public interface IOperationValueService : ICrudWellRelatedService<OperationValueDto>
{ {
} }
} }

View File

@ -6,9 +6,8 @@ using System.Threading.Tasks;
namespace AsbCloudApp.Services namespace AsbCloudApp.Services
{ {
public interface IScheduleService : ICrudService<ScheduleDto> public interface IScheduleService : ICrudWellRelatedService<ScheduleDto>
{ {
Task<IEnumerable<ScheduleDto>> GetByIdWellAsync(int idWell, CancellationToken token = default); Task<DrillerDto> GetDrillerAsync(int idWell, DateTime workTime, CancellationToken token);
Task<DrillerDto> GetDrillerAsync(int idWell, DateTime workTime, CancellationToken token = default);
} }
} }

View File

@ -1,16 +1,11 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AsbCloudDb.Model namespace AsbCloudDb.Model
{ {
[Table("t_operationvalue"), Comment("Целевые/нормативные показатели операции")] [Table("t_operationvalue"), Comment("Целевые/нормативные показатели операции")]
public class OperationValue:IId public class OperationValue: IId, IWellRelated
{ {
[Key] [Key]
[Column("id"), Comment("Идентификатор")] [Column("id"), Comment("Идентификатор")]

View File

@ -6,7 +6,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model namespace AsbCloudDb.Model
{ {
[Table("t_schedule"), Comment("График работы бурильщика")] [Table("t_schedule"), Comment("График работы бурильщика")]
public class Schedule: IId public class Schedule: IId, IWellRelated
{ {
[Key] [Key]
[Column("id"),Comment("Идентификатор")] [Column("id"),Comment("Идентификатор")]

View File

@ -23,7 +23,7 @@ namespace AsbCloudInfrastructure.Services
public CrudWellRelatedServiceBase(IAsbCloudDbContext context, Func<DbSet<TEntity>, IQueryable<TEntity>> makeQuery) public CrudWellRelatedServiceBase(IAsbCloudDbContext context, Func<DbSet<TEntity>, IQueryable<TEntity>> makeQuery)
: base(context, makeQuery) { } : base(context, makeQuery) { }
public async Task<IEnumerable<TDto>> GetAllAsync(int idWell, CancellationToken token) public async Task<IEnumerable<TDto>> GetByIdWellAsync(int idWell, CancellationToken token)
{ {
var entities = await GetQuery() var entities = await GetQuery()
.Where(e => e.IdWell == idWell) .Where(e => e.IdWell == idWell)
@ -32,7 +32,7 @@ namespace AsbCloudInfrastructure.Services
return dtos; return dtos;
} }
public async Task<IEnumerable<TDto>> GetAllAsync(IEnumerable<int> idsWells, CancellationToken token) public async Task<IEnumerable<TDto>> GetByIdWellAsync(IEnumerable<int> idsWells, CancellationToken token)
{ {
if (!idsWells.Any()) if (!idsWells.Any())
return Enumerable.Empty<TDto>(); return Enumerable.Empty<TDto>();

View File

@ -15,6 +15,10 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
{ {
public class DetectedOperationService: IDetectedOperationService public class DetectedOperationService: IDetectedOperationService
{ {
public const int IdOperationRotor = 1;
public const int IdOperationSlide = 3;
public const int IdOperationSlipsTime = 14;
private readonly IAsbCloudDbContext db; private readonly IAsbCloudDbContext db;
private readonly IWellService wellService; private readonly IWellService wellService;
private readonly IOperationValueService operationValueService; private readonly IOperationValueService operationValueService;
@ -35,38 +39,43 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
if (well?.IdTelemetry is null || well.Timezone is null) if (well?.IdTelemetry is null || well.Timezone is null)
return null; return null;
var res = new DetectedOperationListDto();
var query = BuildQuery(well, request) var query = BuildQuery(well, request)
.AsNoTracking(); .AsNoTracking();
var data = await query.ToListAsync(token); var data = await query.ToListAsync(token);
var operationValues = await operationValueService.GetAllAsync(token); var operationValues = await operationValueService.GetByIdWellAsync(idWell, token);
operationValues = operationValues.Where(o => o.IdWell == idWell); var schedules = await scheduleService.GetByIdWellAsync(idWell, token);
var dtos = data.Select(o => Convert(o, well, operationValues)); var dtos = data.Select(o => Convert(o, well, operationValues, schedules));
foreach (var item in dtos) var groups = dtos.GroupBy(o => o.Driller);
var stats = new List<DetectedOperationStatDto>(groups.Count());
foreach (var group in groups)
{ {
item.Driller = await scheduleService.GetDrillerAsync(idWell, item.DateStart); var itemsWithTarget = group.Where(i => i.OperationValue is not null);
var stat = new DetectedOperationStatDto
{
Driller = group.Key,
AverageValue = group.Sum(e => e.Value) / group.Count(),
Count = group.Count(),
};
if (itemsWithTarget.Any())
{
var itemsOutOfTarget = itemsWithTarget.Where(o => !IsTargetOk(o));
stat.AverageTargetValue = itemsWithTarget.Average(e => e.OperationValue.TargetValue);
stat.Efficiency = 100d * itemsOutOfTarget.Count() / itemsWithTarget.Count();
stat.Loss = itemsOutOfTarget.Sum(DeltaToTarget);
}
stats.Add(stat);
} }
var group = dtos.GroupBy(o => o.Driller == null ? 0 : o.Driller.Id,
p => p, var result = new DetectedOperationListDto
(key, gr) => (key, gr.ToList())).ToDictionary(e => e.key, e => e.Item2);
res.List = dtos;
res.Stats = new List<DetectedOperationStatDto>();
foreach (var item in group)
{ {
var obj = new DetectedOperationStatDto(); Operations = dtos,
obj.Driller = item.Value.FirstOrDefault()?.Driller; Stats = stats
obj.Count = item.Value.Count(); };
obj.Average = item.Value.Sum(e=>e.OperationValue?.TargetValue ?? 0)/obj.Count; return result;
obj.Efficiency = 100d * item.Value.Count(e => PredicateTarget(e)(e.Value)) / obj.Count;
obj.AverageByParam = item.Value.Sum(e => e.Value) / obj.Count;
obj.Loss = item.Value
.Where(e => !PredicateTarget(e)(e.Value))
.Sum(p => Math.Abs(p.Value - p.OperationValue?.TargetValue ?? 0));
res.Stats.Add(obj);
}
return res;
} }
public async Task<int> DeleteAsync(int idWell, DetectedOperationRequest request, CancellationToken token) public async Task<int> DeleteAsync(int idWell, DetectedOperationRequest request, CancellationToken token)
@ -80,17 +89,25 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return await db.SaveChangesAsync(token); return await db.SaveChangesAsync(token);
} }
/// <summary> private static bool IsTargetOk(DetectedOperationDto op)
/// Определение применяемого предикат по типц операции
/// </summary>
/// <returns>Предикат для использования</returns>
private static Predicate<double> PredicateTarget(DetectedOperationDto op)
{ {
return op.OperationValue.IdOperationCategory switch return (op.IdCategory) switch
{ {
1 => (x) => false, IdOperationRotor => op.Value > op.OperationValue.TargetValue,
11 => (x) => x > op.OperationValue.TargetValue, IdOperationSlide => op.Value > op.OperationValue.TargetValue,
_ => (x) => true IdOperationSlipsTime => op.Value > op.OperationValue.TargetValue,
_ => op.Value > op.OperationValue.TargetValue,
};
}
private static double DeltaToTarget(DetectedOperationDto op)
{
return (op.IdCategory) switch
{
IdOperationRotor => 0,
IdOperationSlide => 0,
IdOperationSlipsTime => op.Value - op.OperationValue.TargetValue,
_ => 0,
}; };
} }
@ -142,14 +159,26 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
return query; return query;
} }
private static DetectedOperationDto Convert(DetectedOperation operation, WellDto well, IEnumerable<OperationValueDto> operationValues) private DetectedOperationDto Convert(DetectedOperation operation, WellDto well, IEnumerable<OperationValueDto> operationValues, IEnumerable<ScheduleDto> schedules)
{ {
var dto = operation.Adapt<DetectedOperationDto>(); var dto = operation.Adapt<DetectedOperationDto>();
dto.IdWell = well.Id; dto.IdWell = well.Id;
dto.DateStart = operation.DateStart.ToRemoteDateTime(well.Timezone.Hours); var dateStart = operation.DateStart.ToRemoteDateTime(well.Timezone.Hours);
dto.DateStart = dateStart;
dto.DateEnd = operation.DateEnd.ToRemoteDateTime(well.Timezone.Hours); dto.DateEnd = operation.DateEnd.ToRemoteDateTime(well.Timezone.Hours);
dto.OperationValue = operationValues.FirstOrDefault(e => e.IdOperationCategory == dto.IdCategory dto.OperationValue = operationValues.FirstOrDefault(e => e.IdOperationCategory == dto.IdCategory
&& e.DepthStart <= dto.DepthStart); && e.DepthStart <= dto.DepthStart);
var timeStart = new TimeDto(dateStart);
var driller = schedules.FirstOrDefault(s =>
s.DrillStart <= dateStart &&
s.DrillEnd > dateStart && (
s.ShiftStart > s.ShiftEnd
) ^ (s.ShiftStart <= timeStart &&
s.ShiftEnd > timeStart
))
?.Driller;
dto.Driller = driller;
return dto; return dto;
} }

View File

@ -97,15 +97,17 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
IdTelemetry = outer, IdTelemetry = outer,
LastDate = inner.SingleOrDefault()?.LastDate , LastDate = inner.SingleOrDefault()?.LastDate ,
}); });
var affected = 0;
foreach (var item in JounedlastDetectedDates) foreach (var item in JounedlastDetectedDates)
{ {
var newOperations = await DetectOperationsAsync(item.IdTelemetry, item.LastDate??DateTimeOffset.MinValue, db, token); var newOperations = await DetectOperationsAsync(item.IdTelemetry, item.LastDate??DateTimeOffset.MinValue, db, token);
if (newOperations.Any()) if (newOperations.Any())
{
db.DetectedOperations.AddRange(newOperations); db.DetectedOperations.AddRange(newOperations);
affected += await db.SaveChangesAsync(token);
}
} }
return affected;
return await db.SaveChangesAsync(token);
} }
private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) private async Task<IEnumerable<DetectedOperation>> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token)
@ -131,7 +133,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
var dbRequests_ = 0; var dbRequests_ = 0;
var dbTime_ = 0d; var dbTime_ = 0d;
var sw_ = new System.Diagnostics.Stopwatch(); var sw_ = new Stopwatch();
var otherTime_ = 0d; var otherTime_ = 0d;
while (true) while (true)

View File

@ -4,7 +4,7 @@ using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services namespace AsbCloudInfrastructure.Services
{ {
public class OperationValueService : CrudServiceBase<OperationValueDto, OperationValue>, IOperationValueService public class OperationValueService : CrudWellRelatedServiceBase<OperationValueDto, OperationValue>, IOperationValueService
{ {
public OperationValueService(IAsbCloudDbContext context) : base(context) public OperationValueService(IAsbCloudDbContext context) : base(context)
{ {

View File

@ -12,7 +12,7 @@ using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services namespace AsbCloudInfrastructure.Services
{ {
#nullable enable #nullable enable
public class ScheduleService : CrudServiceBase<ScheduleDto, Schedule>, IScheduleService public class ScheduleService : CrudWellRelatedServiceBase<ScheduleDto, Schedule>, IScheduleService
{ {
private readonly IWellService wellService; private readonly IWellService wellService;
@ -22,16 +22,7 @@ namespace AsbCloudInfrastructure.Services
this.wellService = wellService; this.wellService = wellService;
} }
public async Task<IEnumerable<ScheduleDto>> GetByIdWellAsync(int idWell, CancellationToken token = default) public async Task<DrillerDto?> GetDrillerAsync(int idWell, DateTime workTime, CancellationToken token)
{
var entities = await GetQuery()
.Where(s => s.IdWell == idWell)
.ToListAsync(token);
var dtos = entities.Select(Convert);
return dtos;
}
public async Task<DrillerDto?> GetDrillerAsync(int idWell, DateTime workTime, CancellationToken token = default)
{ {
var hoursOffset = wellService.GetTimezone(idWell).Hours; var hoursOffset = wellService.GetTimezone(idWell).Hours;
var date = workTime.ToUtcDateTimeOffset(hoursOffset); var date = workTime.ToUtcDateTimeOffset(hoursOffset);

View File

@ -48,7 +48,7 @@ namespace AsbCloudWebApi.Controllers
return NoContent(); return NoContent();
var idsWells = wells.Select(w => w.Id); var idsWells = wells.Select(w => w.Id);
var result = await service.GetAllAsync(idsWells, token); var result = await service.GetByIdWellAsync(idsWells, token);
return Ok(result); return Ok(result);
} }
@ -59,12 +59,12 @@ namespace AsbCloudWebApi.Controllers
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet("well/{idWell}")] [HttpGet("well/{idWell}")]
public async Task<ActionResult<IEnumerable<T>>> GetAllAsync(int idWell, CancellationToken token) public async Task<ActionResult<IEnumerable<T>>> GetByIdWellAsync(int idWell, CancellationToken token)
{ {
if (!await UserHasAccesToWellAsync(idWell, token)) if (!await UserHasAccesToWellAsync(idWell, token))
return Forbid(); return Forbid();
var result = await service.GetAllAsync(idWell, token); var result = await service.GetByIdWellAsync(idWell, token);
return Ok(result); return Ok(result);
} }
@ -108,6 +108,7 @@ namespace AsbCloudWebApi.Controllers
return await base.UpdateAsync(value, token); return await base.UpdateAsync(value, token);
} }
/// <inheritdoc/>
[HttpDelete("{id}")] [HttpDelete("{id}")]
public override async Task<ActionResult<int>> DeleteAsync(int id, CancellationToken token) public override async Task<ActionResult<int>> DeleteAsync(int id, CancellationToken token)
{ {

View File

@ -5,12 +5,12 @@ using Microsoft.AspNetCore.Mvc;
namespace AsbCloudWebApi.Controllers namespace AsbCloudWebApi.Controllers
{ {
[Route("api/operationvalue")] [Route("api/operationValue")]
[ApiController] [ApiController]
[Authorize] [Authorize]
public class OperationValueController : CrudController<OperationValueDto, IOperationValueService> public class OperationValueController : CrudWellRelatedController<OperationValueDto, IOperationValueService>
{ {
public OperationValueController(IOperationValueService service) : base(service) public OperationValueController(IOperationValueService service, IWellService wellService) : base(wellService, service)
{ {
} }
} }

View File

@ -45,7 +45,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(IEnumerable<DetectedOperationDto>), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(DetectedOperationListDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetAsync( public async Task<IActionResult> GetAsync(
int idWell, int idWell,
[FromQuery] DetectedOperationRequest request, [FromQuery] DetectedOperationRequest request,