2023-08-15 17:08:33 +05:00
|
|
|
|
using AsbCloudApp.Data;
|
2023-10-31 19:01:51 +05:00
|
|
|
|
using AsbCloudApp.Exceptions;
|
2023-10-24 09:23:07 +05:00
|
|
|
|
using AsbCloudApp.Repositories;
|
2023-08-15 17:08:33 +05:00
|
|
|
|
using AsbCloudApp.Services;
|
2021-11-15 14:56:11 +05:00
|
|
|
|
using AsbCloudDb;
|
2022-04-11 18:00:34 +05:00
|
|
|
|
using AsbCloudDb.Model;
|
2021-09-17 16:24:01 +05:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2021-11-13 18:47:11 +05:00
|
|
|
|
using System.Diagnostics;
|
2021-09-17 16:24:01 +05:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2022-04-11 18:00:34 +05:00
|
|
|
|
namespace AsbCloudInfrastructure.Services.SAUB
|
2021-09-17 16:24:01 +05:00
|
|
|
|
{
|
2022-11-15 17:44:48 +05:00
|
|
|
|
public abstract class TelemetryDataBaseService<TDto, TEntity> : ITelemetryDataService<TDto>
|
2021-09-17 16:24:01 +05:00
|
|
|
|
where TDto : AsbCloudApp.Data.ITelemetryData
|
2023-08-15 17:08:33 +05:00
|
|
|
|
where TEntity : class, AsbCloudDb.Model.ITelemetryData
|
2021-09-17 16:24:01 +05:00
|
|
|
|
{
|
2022-10-06 13:49:20 +05:00
|
|
|
|
protected readonly IAsbCloudDbContext db;
|
2023-01-13 14:34:26 +05:00
|
|
|
|
protected readonly ITelemetryService telemetryService;
|
2023-10-24 09:23:07 +05:00
|
|
|
|
protected readonly ITelemetryDataCache<TDto> telemetryDataCache;
|
2021-09-17 16:24:01 +05:00
|
|
|
|
|
2021-09-23 10:53:48 +05:00
|
|
|
|
public TelemetryDataBaseService(
|
2021-09-17 16:24:01 +05:00
|
|
|
|
IAsbCloudDbContext db,
|
|
|
|
|
ITelemetryService telemetryService,
|
2023-10-24 09:23:07 +05:00
|
|
|
|
ITelemetryDataCache<TDto> telemetryDataCache)
|
2021-09-17 16:24:01 +05:00
|
|
|
|
{
|
|
|
|
|
this.db = db;
|
|
|
|
|
this.telemetryService = telemetryService;
|
2022-11-15 17:44:48 +05:00
|
|
|
|
this.telemetryDataCache = telemetryDataCache;
|
2021-09-17 16:24:01 +05:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-15 17:44:48 +05:00
|
|
|
|
/// <inheritdoc/>
|
2021-11-15 14:56:11 +05:00
|
|
|
|
public virtual async Task<int> UpdateDataAsync(string uid, IEnumerable<TDto> dtos, CancellationToken token = default)
|
2021-09-17 16:24:01 +05:00
|
|
|
|
{
|
|
|
|
|
if (dtos == default || !dtos.Any())
|
|
|
|
|
return 0;
|
2021-11-15 14:56:11 +05:00
|
|
|
|
|
2022-04-08 13:10:06 +05:00
|
|
|
|
var dtosList = dtos.OrderBy(d => d.DateTime).ToList();
|
2021-10-28 10:56:18 +05:00
|
|
|
|
|
2022-04-08 13:10:06 +05:00
|
|
|
|
var dtoMinDate = dtosList.First().DateTime;
|
|
|
|
|
var dtoMaxDate = dtosList.Last().DateTime;
|
2021-11-13 18:47:11 +05:00
|
|
|
|
|
|
|
|
|
if (dtosList.Count > 1)
|
|
|
|
|
{
|
|
|
|
|
var duplicates = new List<TDto>(8);
|
|
|
|
|
for (int i = 1; i < dtosList.Count; i++)
|
2022-04-11 18:00:34 +05:00
|
|
|
|
if (dtosList[i].DateTime - dtosList[i - 1].DateTime < TimeSpan.FromMilliseconds(100))
|
2021-11-13 18:47:11 +05:00
|
|
|
|
duplicates.Add(dtosList[i - 1]);
|
2021-11-15 14:56:11 +05:00
|
|
|
|
foreach (var duplicate in duplicates)
|
2021-11-13 18:47:11 +05:00
|
|
|
|
dtosList.Remove(duplicate);
|
|
|
|
|
}
|
2022-01-10 18:12:31 +05:00
|
|
|
|
|
2023-06-20 14:41:19 +05:00
|
|
|
|
var telemetry = telemetryService.GetOrCreateTelemetryByUid(uid);
|
|
|
|
|
var timezone = telemetryService.GetTimezone(telemetry.Id);
|
2021-12-30 17:05:44 +05:00
|
|
|
|
|
2023-06-20 14:41:19 +05:00
|
|
|
|
telemetryDataCache.AddRange(telemetry.Id, dtos);
|
2022-11-15 17:44:48 +05:00
|
|
|
|
|
2022-04-11 18:00:34 +05:00
|
|
|
|
var entities = dtosList.Select(dto =>
|
|
|
|
|
{
|
|
|
|
|
var entity = Convert(dto, timezone.Hours);
|
2023-06-20 14:41:19 +05:00
|
|
|
|
entity.IdTelemetry = telemetry.Id;
|
2022-04-11 18:00:34 +05:00
|
|
|
|
return entity;
|
|
|
|
|
});
|
2021-09-17 16:24:01 +05:00
|
|
|
|
|
2021-12-24 13:00:16 +05:00
|
|
|
|
var stopwatch = Stopwatch.StartNew();
|
2023-09-25 13:20:11 +05:00
|
|
|
|
var dbset = db.Set<TEntity>();
|
2021-11-13 18:47:11 +05:00
|
|
|
|
try
|
2021-09-17 16:24:01 +05:00
|
|
|
|
{
|
2021-11-17 15:54:01 +05:00
|
|
|
|
return await db.Database.ExecInsertOrUpdateAsync(dbset, entities, token).ConfigureAwait(false);
|
2021-11-13 18:47:11 +05:00
|
|
|
|
}
|
2022-04-11 18:00:34 +05:00
|
|
|
|
catch (Exception ex)
|
2021-11-13 18:47:11 +05:00
|
|
|
|
{
|
2021-12-24 13:00:16 +05:00
|
|
|
|
stopwatch.Stop();
|
2021-12-30 17:05:44 +05:00
|
|
|
|
Trace.WriteLine($"Fail to save data telemetry " +
|
2021-12-24 13:00:16 +05:00
|
|
|
|
$"uid: {uid}, " +
|
2023-06-20 14:41:19 +05:00
|
|
|
|
$"idTelemetry {telemetry.Id}, " +
|
2021-12-24 13:00:16 +05:00
|
|
|
|
$"count: {entities.Count()}, " +
|
2022-04-01 17:55:44 +05:00
|
|
|
|
$"dataDate: {entities.FirstOrDefault()?.DateTime}, " +
|
2021-12-24 13:00:16 +05:00
|
|
|
|
$"dbSaveDurationTime:{stopwatch.ElapsedMilliseconds}ms. " +
|
|
|
|
|
$"Message: {ex.Message}");
|
2021-11-15 14:56:11 +05:00
|
|
|
|
return 0;
|
2021-09-17 16:24:01 +05:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-15 17:44:48 +05:00
|
|
|
|
|
2022-11-08 17:49:04 +05:00
|
|
|
|
/// <inheritdoc/>
|
2023-03-27 09:40:51 +05:00
|
|
|
|
public virtual async Task<IEnumerable<TDto>> GetAsync(int idWell,
|
2021-09-17 16:24:01 +05:00
|
|
|
|
DateTime dateBegin = default, double intervalSec = 600d,
|
2022-01-05 17:50:45 +05:00
|
|
|
|
int approxPointsCount = 1024, CancellationToken token = default)
|
2021-09-17 16:24:01 +05:00
|
|
|
|
{
|
2023-03-07 09:12:49 +05:00
|
|
|
|
var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell);
|
|
|
|
|
if (telemetry is null)
|
2023-03-27 09:40:51 +05:00
|
|
|
|
return Enumerable.Empty<TDto>();
|
2021-11-22 11:30:08 +05:00
|
|
|
|
|
2023-03-07 09:12:49 +05:00
|
|
|
|
var timezone = telemetryService.GetTimezone(telemetry.Id);
|
2021-12-30 17:05:44 +05:00
|
|
|
|
|
2021-12-03 17:34:24 +05:00
|
|
|
|
var filterByDateEnd = dateBegin != default;
|
2021-12-30 17:05:44 +05:00
|
|
|
|
DateTimeOffset dateBeginUtc;
|
2021-10-27 17:48:19 +05:00
|
|
|
|
if (dateBegin == default)
|
2021-10-28 11:12:03 +05:00
|
|
|
|
{
|
2023-05-19 16:32:37 +05:00
|
|
|
|
var dateRange = telemetryDataCache.GetOrDefaultDataDateRange(telemetry.Id);
|
|
|
|
|
dateBeginUtc = (dateRange?.To.ToUtcDateTimeOffset(timezone.Hours) ?? DateTime.UtcNow)
|
|
|
|
|
.AddSeconds(-intervalSec);
|
2021-12-30 17:05:44 +05:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2022-01-05 17:50:45 +05:00
|
|
|
|
dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timezone.Hours);
|
2021-12-30 17:05:44 +05:00
|
|
|
|
}
|
2021-09-17 16:24:01 +05:00
|
|
|
|
|
2023-03-07 09:12:49 +05:00
|
|
|
|
var cacheData = telemetryDataCache.GetOrDefault(telemetry.Id, dateBeginUtc.ToRemoteDateTime(timezone.Hours), intervalSec, approxPointsCount);
|
2022-11-15 17:44:48 +05:00
|
|
|
|
if (cacheData is not null)
|
|
|
|
|
return cacheData;
|
|
|
|
|
|
2021-12-30 17:05:44 +05:00
|
|
|
|
var dateEnd = dateBeginUtc.AddSeconds(intervalSec);
|
2022-11-15 17:44:48 +05:00
|
|
|
|
var dbSet = db.Set<TEntity>();
|
2021-09-17 16:24:01 +05:00
|
|
|
|
|
2021-11-22 11:30:08 +05:00
|
|
|
|
var query = dbSet
|
2023-03-07 09:12:49 +05:00
|
|
|
|
.Where(d => d.IdTelemetry == telemetry.Id
|
2022-04-01 17:55:44 +05:00
|
|
|
|
&& d.DateTime >= dateBeginUtc);
|
2021-12-03 17:34:24 +05:00
|
|
|
|
|
|
|
|
|
if (filterByDateEnd)
|
2022-04-01 17:55:44 +05:00
|
|
|
|
query = query.Where(d => d.DateTime <= dateEnd);
|
2021-09-17 16:24:01 +05:00
|
|
|
|
|
|
|
|
|
var fullDataCount = await query.CountAsync(token)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
if (fullDataCount == 0)
|
2023-03-27 09:40:51 +05:00
|
|
|
|
return Enumerable.Empty<TDto>();
|
2021-09-17 16:24:01 +05:00
|
|
|
|
|
|
|
|
|
if (fullDataCount > 1.75 * approxPointsCount)
|
|
|
|
|
{
|
|
|
|
|
var m = (int)Math.Round(1d * fullDataCount / approxPointsCount);
|
|
|
|
|
if (m > 1)
|
2022-04-11 18:00:34 +05:00
|
|
|
|
query = query.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % m == 0);
|
2021-09-17 16:24:01 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-29 17:05:27 +05:00
|
|
|
|
var entities = await query
|
|
|
|
|
.AsNoTracking()
|
2023-09-11 12:27:48 +05:00
|
|
|
|
.ToArrayAsync(token);
|
|
|
|
|
|
|
|
|
|
var dtos = entities.Select(e => Convert(e, timezone.Hours));
|
|
|
|
|
|
|
|
|
|
return dtos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
public virtual async Task<IEnumerable<TDto>> GetAsync(int idWell, AsbCloudApp.Requests.TelemetryDataRequest request, CancellationToken token)
|
|
|
|
|
{
|
|
|
|
|
var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell);
|
|
|
|
|
if (telemetry is null)
|
|
|
|
|
return Enumerable.Empty<TDto>();
|
|
|
|
|
|
|
|
|
|
var timezone = telemetryService.GetTimezone(telemetry.Id);
|
|
|
|
|
|
2023-09-11 13:48:47 +05:00
|
|
|
|
var cache = telemetryDataCache.GetOrDefault(telemetry.Id, request);
|
|
|
|
|
if(cache is not null)
|
|
|
|
|
return cache;
|
|
|
|
|
|
2023-09-11 12:27:48 +05:00
|
|
|
|
var dbSet = db.Set<TEntity>();
|
|
|
|
|
|
|
|
|
|
var query = dbSet
|
|
|
|
|
.Where(d => d.IdTelemetry == telemetry.Id)
|
|
|
|
|
.AsNoTracking();
|
|
|
|
|
|
|
|
|
|
if (request.GeDate.HasValue)
|
2023-09-11 15:21:36 +05:00
|
|
|
|
{
|
|
|
|
|
var geDate = request.GeDate.Value.UtcDateTime;
|
|
|
|
|
query = query.Where(d => d.DateTime >= geDate);
|
|
|
|
|
}
|
2023-09-11 12:27:48 +05:00
|
|
|
|
|
|
|
|
|
if (request.LeDate.HasValue)
|
2023-09-11 15:21:36 +05:00
|
|
|
|
{
|
|
|
|
|
var leDate = request.LeDate.Value.UtcDateTime;
|
|
|
|
|
query = query.Where(d => d.DateTime <= leDate);
|
|
|
|
|
}
|
2023-09-11 12:27:48 +05:00
|
|
|
|
|
|
|
|
|
if (request.Divider > 1)
|
|
|
|
|
query = query.Where((d) => (((d.DateTime.DayOfYear * 24 + d.DateTime.Hour) * 60 + d.DateTime.Minute) * 60 + d.DateTime.Second) % request.Divider == 0);
|
|
|
|
|
|
|
|
|
|
switch (request.Order)
|
|
|
|
|
{
|
|
|
|
|
case 1:// Поздние вперед
|
|
|
|
|
query = query
|
|
|
|
|
.OrderByDescending(d => d.DateTime)
|
|
|
|
|
.Skip(request.Skip)
|
|
|
|
|
.Take(request.Take)
|
|
|
|
|
.OrderBy(d => d.DateTime);
|
|
|
|
|
break;
|
|
|
|
|
default:// Ранние вперед
|
|
|
|
|
query = query
|
|
|
|
|
.OrderBy(d => d.DateTime)
|
|
|
|
|
.Skip(request.Skip)
|
|
|
|
|
.Take(request.Take);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var entities = await query
|
|
|
|
|
.ToArrayAsync(token);
|
2021-09-17 16:24:01 +05:00
|
|
|
|
|
2022-01-05 17:50:45 +05:00
|
|
|
|
var dtos = entities.Select(e => Convert(e, timezone.Hours));
|
2022-04-11 18:00:34 +05:00
|
|
|
|
|
2021-09-17 16:24:01 +05:00
|
|
|
|
return dtos;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-15 17:08:33 +05:00
|
|
|
|
/// <inheritdoc/>
|
2023-11-01 17:45:25 +05:00
|
|
|
|
public async Task<DatesRangeDto?> GetRangeAsync(int idWell, DateTimeOffset geDate, DateTimeOffset? leDate, CancellationToken token)
|
2023-08-15 17:08:33 +05:00
|
|
|
|
{
|
2023-10-31 19:01:51 +05:00
|
|
|
|
var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell)
|
|
|
|
|
?? throw new ArgumentInvalidException(nameof(idWell), $"По скважине id:{idWell} нет телеметрии");
|
2023-08-15 17:08:33 +05:00
|
|
|
|
|
2023-10-31 19:01:51 +05:00
|
|
|
|
if ((DateTimeOffset.UtcNow - geDate) < TimeSpan.FromHours(12))
|
|
|
|
|
{
|
|
|
|
|
// пробуем обойтись кешем
|
2023-12-05 14:48:56 +05:00
|
|
|
|
var cechedRange = telemetryDataCache.GetOrDefaultCachedDateRange(telemetry.Id);
|
2023-10-31 19:01:51 +05:00
|
|
|
|
if (cechedRange?.From <= geDate)
|
|
|
|
|
{
|
|
|
|
|
var datesRange = new DatesRangeDto
|
|
|
|
|
{
|
|
|
|
|
From = geDate.DateTime,
|
|
|
|
|
To = cechedRange.To
|
|
|
|
|
};
|
|
|
|
|
if (leDate.HasValue && leDate > geDate)
|
|
|
|
|
datesRange.To = leDate.Value.Date;
|
|
|
|
|
return datesRange;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var query = db.Set<TEntity>()
|
|
|
|
|
.Where(entity => entity.IdTelemetry == telemetry.Id)
|
|
|
|
|
.Where(entity => entity.DateTime >= geDate.ToUniversalTime());
|
2023-08-15 17:08:33 +05:00
|
|
|
|
|
2023-10-31 19:01:51 +05:00
|
|
|
|
if(leDate.HasValue)
|
|
|
|
|
query = query.Where(entity => entity.DateTime <= leDate.Value.ToUniversalTime());
|
2023-10-17 15:24:50 +05:00
|
|
|
|
|
2023-10-31 19:01:51 +05:00
|
|
|
|
var gquery = query
|
|
|
|
|
.GroupBy(entity => entity.IdTelemetry)
|
|
|
|
|
.Select(group => new
|
|
|
|
|
{
|
|
|
|
|
MinDate = group.Min(entity => entity.DateTime),
|
|
|
|
|
MaxDate = group.Max(entity => entity.DateTime),
|
|
|
|
|
});
|
2023-10-17 15:24:50 +05:00
|
|
|
|
|
2023-11-01 17:45:25 +05:00
|
|
|
|
var result = await gquery.FirstOrDefaultAsync(token);
|
|
|
|
|
if (result is null)
|
|
|
|
|
return null;
|
2023-08-15 17:08:33 +05:00
|
|
|
|
|
2023-10-31 19:01:51 +05:00
|
|
|
|
var range = new DatesRangeDto
|
2023-08-15 17:08:33 +05:00
|
|
|
|
{
|
2023-10-31 19:01:51 +05:00
|
|
|
|
From = result.MinDate.ToOffset(TimeSpan.FromHours(telemetry.TimeZone!.Hours)).DateTime,
|
|
|
|
|
To = result.MaxDate.ToOffset(TimeSpan.FromHours(telemetry.TimeZone!.Hours)).DateTime,
|
2023-08-15 17:08:33 +05:00
|
|
|
|
};
|
2023-10-31 19:01:51 +05:00
|
|
|
|
return range;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public DatesRangeDto? GetRange(int idWell)
|
|
|
|
|
{
|
|
|
|
|
var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell);
|
|
|
|
|
if (telemetry is null)
|
|
|
|
|
return default;
|
|
|
|
|
|
|
|
|
|
return telemetryDataCache.GetOrDefaultDataDateRange(telemetry.Id);
|
2023-08-15 17:08:33 +05:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-15 17:44:48 +05:00
|
|
|
|
public abstract TDto Convert(TEntity src, double timezoneOffset);
|
2021-09-17 16:24:01 +05:00
|
|
|
|
|
2022-11-15 17:44:48 +05:00
|
|
|
|
public abstract TEntity Convert(TDto src, double timezoneOffset);
|
2022-01-10 18:12:31 +05:00
|
|
|
|
|
2021-09-17 16:24:01 +05:00
|
|
|
|
}
|
|
|
|
|
}
|