Merge branch 'fix/save-fact-operations' of http://test.digitaldrilling.ru:8080/DDrilling/AsbCloudServer into fix/save-fact-operations

This commit is contained in:
Olga Nemt 2023-05-23 10:53:46 +05:00
commit 1e54fb3b16
14 changed files with 174 additions and 375 deletions

View File

@ -16,11 +16,6 @@ namespace AsbCloudApp.Services
/// </summary> /// </summary>
ITimezoneService TimeZoneService { get; } ITimezoneService TimeZoneService { get; }
/// <summary>
/// трекер запросов
/// </summary>
ITelemetryTracker TelemetryTracker { get; }
/// <summary> /// <summary>
/// получить idWell по uid телеметрии /// получить idWell по uid телеметрии
/// </summary> /// </summary>
@ -42,13 +37,6 @@ namespace AsbCloudApp.Services
/// <returns></returns> /// <returns></returns>
SimpleTimezoneDto GetTimezone(int idTelemetry); SimpleTimezoneDto GetTimezone(int idTelemetry);
/// <summary>
/// Получить дату получения последних данных
/// </summary>
/// <param name="idTelemetry"></param>
/// <returns></returns>
DateTime GetLastTelemetryDate(int idTelemetry);
/// <summary> /// <summary>
/// получить idTelemetry по IdWell /// получить idTelemetry по IdWell
/// </summary> /// </summary>

View File

@ -1,39 +0,0 @@
using AsbCloudApp.Data;
using System;
using System.Collections.Generic;
namespace AsbCloudApp.Services
{
/// <summary>
/// Сервис статистики телеметрии
/// </summary>
public interface ITelemetryTracker
{
/// <summary>
/// получить дату последней отправки данных панелью
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
DateTimeOffset GetLastTelemetryDateByUid(string uid);
/// <summary>
/// получить диапазон дат за которые есть данные по телеметрии
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
DatesRangeDto GetTelemetryDateRangeByUid(string uid);
/// <summary>
/// список передающих телеметрий
/// </summary>
/// <returns></returns>
IEnumerable<string> GetTransmittingTelemetriesUids();
/// <summary>
/// обновить статистику по телеметрии
/// </summary>
/// <param name="uid"></param>
/// <param name="remoteDate"></param>
void SaveRequestDate(string uid, DateTimeOffset remoteDate);
}
}

View File

@ -63,7 +63,7 @@ namespace AsbCloudApp.Services
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <returns></returns> /// <returns></returns>
DateTimeOffset GetLastTelemetryDate(int idWell); DateTime GetLastTelemetryDate(int idWell);
//TODO: выяснить и удалить отсюда //TODO: выяснить и удалить отсюда
/// <summary> /// <summary>

View File

@ -1,12 +0,0 @@
using System;
using System.Collections.Concurrent;
namespace AsbCloudApp.Services
{
/// <summary>
/// Репозиторий для хранения в оперативке данных (от панели)
/// </summary>
public class InstantDataRepository : ConcurrentDictionary<int, ConcurrentDictionary<Type, object>>
{
}
}

View File

@ -1,12 +1,10 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.GTR;
using AsbCloudApp.Data.SAUB; using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Data.Subsystems; using AsbCloudApp.Data.Subsystems;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudApp.Services.Subsystems; using AsbCloudApp.Services.Subsystems;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudDb.Model.GTR;
using AsbCloudDb.Model.Subsystems; using AsbCloudDb.Model.Subsystems;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Repository; using AsbCloudInfrastructure.Repository;
@ -102,10 +100,8 @@ namespace AsbCloudInfrastructure
services.AddScoped<IEmailService, EmailService>(); services.AddScoped<IEmailService, EmailService>();
services.AddSingleton(new WitsInfoService()); services.AddSingleton(new WitsInfoService());
services.AddSingleton(new InstantDataRepository()); services.AddSingleton(provider => TelemetryDataCache<TelemetryDataSaubDto>.GetInstance<TelemetryDataSaub>(provider));
services.AddSingleton(provider=> TelemetryDataCache<TelemetryDataSaubDto>.GetInstance<TelemetryDataSaub>(configuration)); services.AddSingleton(provider => TelemetryDataCache<TelemetryDataSpinDto>.GetInstance<TelemetryDataSpin>(provider));
services.AddSingleton(provider=> TelemetryDataCache<TelemetryDataSpinDto>.GetInstance<TelemetryDataSpin>(configuration));
services.AddSingleton<ITelemetryTracker, TelemetryTracker>();
services.AddSingleton<IRequerstTrackerService, RequestTrackerService>(); services.AddSingleton<IRequerstTrackerService, RequestTrackerService>();
services.AddSingleton<BackgroundWorker>(); services.AddSingleton<BackgroundWorker>();
services.AddSingleton<IReduceSamplingService>(provider => ReduceSamplingService.GetInstance(configuration)); services.AddSingleton<IReduceSamplingService>(provider => ReduceSamplingService.GetInstance(configuration));

View File

@ -111,7 +111,7 @@ namespace AsbCloudInfrastructure.Repository
{ {
var dto = well.Adapt<WellDto>(); var dto = well.Adapt<WellDto>();
dto.WellType = well.WellType.Caption; dto.WellType = well.WellType.Caption;
dto.LastTelemetryDate = wellService.GetLastTelemetryDate(well.Id).DateTime; dto.LastTelemetryDate = wellService.GetLastTelemetryDate(well.Id);
dto.Cluster = gCluster.Key.Caption; dto.Cluster = gCluster.Key.Caption;
dto.Deposit = gDeposit.Key.Caption; dto.Deposit = gDeposit.Key.Caption;
return dto; return dto;

View File

@ -64,7 +64,6 @@ namespace AsbCloudInfrastructure.Services.SAUB
}); });
var entityMaxDate = entities.Max(e => e.DateTime); var entityMaxDate = entities.Max(e => e.DateTime);
telemetryService.TelemetryTracker.SaveRequestDate(uid, entityMaxDate);
var dbset = db.Set<TEntity>(); var dbset = db.Set<TEntity>();
var stopwatch = Stopwatch.StartNew(); var stopwatch = Stopwatch.StartNew();
@ -101,19 +100,15 @@ namespace AsbCloudInfrastructure.Services.SAUB
DateTimeOffset dateBeginUtc; DateTimeOffset dateBeginUtc;
if (dateBegin == default) if (dateBegin == default)
{ {
dateBeginUtc = telemetryService.GetLastTelemetryDate(telemetry.Id) var dateRange = telemetryDataCache.GetOrDefaultDataDateRange(telemetry.Id);
.ToUtcDateTimeOffset(timezone.Hours); dateBeginUtc = (dateRange?.To.ToUtcDateTimeOffset(timezone.Hours) ?? DateTime.UtcNow)
if (dateBeginUtc != default) .AddSeconds(-intervalSec);
dateBeginUtc = dateBeginUtc.AddSeconds(-intervalSec);
} }
else else
{ {
dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timezone.Hours); dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timezone.Hours);
} }
if (dateBeginUtc == default)
dateBeginUtc = DateTime.UtcNow.AddSeconds(-intervalSec);
var cacheData = telemetryDataCache.GetOrDefault(telemetry.Id, dateBeginUtc.ToRemoteDateTime(timezone.Hours), intervalSec, approxPointsCount); var cacheData = telemetryDataCache.GetOrDefault(telemetry.Id, dateBeginUtc.ToRemoteDateTime(timezone.Hours), intervalSec, approxPointsCount);
if (cacheData is not null) if (cacheData is not null)
return cacheData; return cacheData;

View File

@ -7,17 +7,28 @@ using Microsoft.EntityFrameworkCore;
using Mapster; using Mapster;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using AsbCloudInfrastructure.Background;
using System.Threading;
using AsbCloudApp.Data;
namespace AsbCloudInfrastructure.Services.SAUB namespace AsbCloudInfrastructure.Services.SAUB
{ {
public class TelemetryDataCache<TDto> public class TelemetryDataCache<TDto>
where TDto : AsbCloudApp.Data.ITelemetryData where TDto : AsbCloudApp.Data.ITelemetryData
{ {
class TelemetryDataCacheItem
{
public TDto? FirstByDate { get; init; }
public CyclycArray<TDto> LastData { get; init; } = null!;
}
private IServiceProvider provider = null!;
private const int activeWellCapacity = 12 * 60 * 60; private const int activeWellCapacity = 12 * 60 * 60;
private const int doneWellCapacity = 65 * 60; private const int doneWellCapacity = 65 * 60;
private readonly ConcurrentDictionary<int, CyclycArray<TDto>> caches; // key == idTelemetry
private readonly ConcurrentDictionary<int, TelemetryDataCacheItem> caches;
private bool isLoading = false; private bool isLoading = false;
private TelemetryDataCache() private TelemetryDataCache()
@ -27,35 +38,22 @@ namespace AsbCloudInfrastructure.Services.SAUB
private static TelemetryDataCache<TDto>? instance; private static TelemetryDataCache<TDto>? instance;
//TODO: Move initialize fromDB to bacground service task public static TelemetryDataCache<TDto> GetInstance<TEntity>(IServiceProvider provider)
public static TelemetryDataCache<TDto> GetInstance<TEntity>(IConfiguration configuration) where TEntity : class, AsbCloudDb.Model.ITelemetryData
where TEntity : class, ITelemetryData
{ {
if (instance is null) if (instance is null)
{ {
instance = new TelemetryDataCache<TDto>(); instance = new TelemetryDataCache<TDto>();
_ = Task.Run(() => var worker = provider.GetRequiredService<BackgroundWorker>();
{ var workId = $"Telemetry cache loading from DB {typeof(TEntity).Name}";
using var db = MakeContext(configuration); var work = new WorkBase(workId, async (workId, provider, token) => {
instance.InitializeCacheFromDB<TEntity>(db); var db = provider.GetRequiredService<IAsbCloudDbContext>();
db.Dispose(); await instance.InitializeCacheFromDBAsync<TEntity>(db, token);
}); });
worker.Push(work);
} }
return instance; instance.provider = provider;
}
public static TelemetryDataCache<TDto> GetInstance<TEntity>(IAsbCloudDbContext db, out Task initializationTask)
where TEntity : class, ITelemetryData
{
if (instance is null)
{
instance = new TelemetryDataCache<TDto>();
initializationTask = Task.Run(() =>
{
instance.InitializeCacheFromDB<TEntity>(db);
});
}
else
initializationTask = Task.CompletedTask;
return instance; return instance;
} }
@ -66,24 +64,33 @@ namespace AsbCloudInfrastructure.Services.SAUB
/// <param name="range"></param> /// <param name="range"></param>
public void AddRange(int idTelemetry, IEnumerable<TDto> range) public void AddRange(int idTelemetry, IEnumerable<TDto> range)
{ {
CyclycArray<TDto> cacheItem; if (!range.Any())
return;
var newItems = range
.OrderBy(i => i.DateTime);
foreach (var item in newItems)
item.IdTelemetry = idTelemetry;
TelemetryDataCacheItem cacheItem;
if (isLoading) if (isLoading)
{ {
if (caches.TryGetValue(idTelemetry, out CyclycArray<TDto>? localCacheItem)) if (caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? localCacheItem))
cacheItem = localCacheItem; cacheItem = localCacheItem;
else else
return; return;
} }
else else
{ {
cacheItem = caches.GetOrAdd(idTelemetry, _ => new CyclycArray<TDto>(activeWellCapacity)); cacheItem = caches.GetOrAdd(idTelemetry, _ => new TelemetryDataCacheItem()
{
FirstByDate = newItems.ElementAt(0),
LastData = new CyclycArray<TDto>(activeWellCapacity)
});
} }
var newItems = range cacheItem.LastData.AddRange(newItems);
.OrderBy(i => i.DateTime);
foreach (var item in newItems)
item.IdTelemetry = idTelemetry;
cacheItem.AddRange(newItems);
} }
/// <summary> /// <summary>
@ -98,14 +105,16 @@ namespace AsbCloudInfrastructure.Services.SAUB
/// <returns></returns> /// <returns></returns>
public IEnumerable<TDto>? GetOrDefault(int idTelemetry, DateTime dateBegin, double intervalSec = 600d, int approxPointsCount = 1024) public IEnumerable<TDto>? GetOrDefault(int idTelemetry, DateTime dateBegin, double intervalSec = 600d, int approxPointsCount = 1024)
{ {
if(!caches.TryGetValue(idTelemetry, out CyclycArray<TDto>? cacheItem)) if(!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null; return null;
if (cacheItem is null || !cacheItem.Any() || cacheItem[0].DateTime > dateBegin) var cacheLastData = cacheItem.LastData;
if (!cacheLastData.Any() || cacheLastData[0].DateTime > dateBegin)
return null; return null;
var dateEnd = dateBegin.AddSeconds(intervalSec); var dateEnd = dateBegin.AddSeconds(intervalSec);
var items = cacheItem var items = cacheLastData
.Where(i => i.DateTime >= dateBegin && i.DateTime <= dateEnd); .Where(i => i.DateTime >= dateBegin && i.DateTime <= dateEnd);
var ratio = items.Count() / approxPointsCount; var ratio = items.Count() / approxPointsCount;
@ -116,19 +125,43 @@ namespace AsbCloudInfrastructure.Services.SAUB
return items; return items;
} }
private void InitializeCacheFromDB<TEntity>(IAsbCloudDbContext db) public TDto? GetLastOrDefault(int idTelemetry)
where TEntity : class, ITelemetryData {
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return default;
return cacheItem.LastData.LastOrDefault();
}
public DatesRangeDto? GetOrDefaultDataDateRange(int idTelemetry)
{
if (!caches.TryGetValue(idTelemetry, out TelemetryDataCacheItem? cacheItem))
return null;
var from = cacheItem.FirstByDate?.DateTime;
if(!cacheItem.LastData.Any())
return null;
var to = cacheItem.LastData[^1].DateTime;
from = from ?? cacheItem.LastData[0].DateTime;
return new DatesRangeDto { From = from.Value, To = to };
}
private async Task InitializeCacheFromDBAsync<TEntity>(IAsbCloudDbContext db, CancellationToken token)
where TEntity : class, AsbCloudDb.Model.ITelemetryData
{ {
if (isLoading) if (isLoading)
throw new Exception("Multiple cache loading detected."); throw new Exception("Multiple cache loading detected.");
isLoading = true; isLoading = true;
Well[] wells = Array.Empty<Well>(); Well[] wells = Array.Empty<Well>();
wells = db.Set<Well>()
.Include(well => well.Telemetry) wells = await db.Set<Well>()
.Include(well => well.Cluster) .Include(well => well.Telemetry)
.Where(well => well.IdTelemetry != null) .Include(well => well.Cluster)
.ToArray(); .Where(well => well.IdTelemetry != null)
.ToArrayAsync(token);
foreach (Well well in wells) foreach (Well well in wells)
{ {
@ -139,46 +172,61 @@ namespace AsbCloudInfrastructure.Services.SAUB
var idTelemetry = well.IdTelemetry!.Value; var idTelemetry = well.IdTelemetry!.Value;
var hoursOffset = well.Timezone.Hours; var hoursOffset = well.Timezone.Hours;
IEnumerable<TDto> cacheItemData = GetCacheDataFromDb<TEntity>(db, idTelemetry, capacity, hoursOffset); var cacheItem = await GetOrDefaultCacheDataFromDbAsync<TEntity>(db, idTelemetry, capacity, hoursOffset, token);
var cacheItem = new CyclycArray<TDto>(capacity); if(cacheItem is not null)
cacheItem.AddRange(cacheItemData); {
caches.TryAdd(idTelemetry, cacheItem); caches.TryAdd(idTelemetry, cacheItem);
System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}> for well: {well.Cluster?.Caption}/{well.Caption} loaded");
System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}> for well: {well.Cluster?.Caption}/{well.Caption} loaded"); }
else
{
System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}> for well: {well.Cluster?.Caption}/{well.Caption} has no data");
}
} }
System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}> load complete"); System.Diagnostics.Trace.TraceInformation($"cache<{typeof(TDto).Name}> load complete");
isLoading = false; isLoading = false;
} }
private static IAsbCloudDbContext MakeContext(IConfiguration configuration) private static async Task<TelemetryDataCacheItem?> GetOrDefaultCacheDataFromDbAsync<TEntity>(IAsbCloudDbContext db, int idTelemetry, int capacity, double hoursOffset, CancellationToken token)
where TEntity : class, AsbCloudDb.Model.ITelemetryData
{ {
var connectionString = configuration.GetConnectionString("DefaultConnection"); var query = db.Set<TEntity>()
var options = new DbContextOptionsBuilder<AsbCloudDbContext>() .Where(i => i.IdTelemetry == idTelemetry);
.UseNpgsql(connectionString)
.Options;
var db = new AsbCloudDbContext(options);
return db;
}
private static IEnumerable<TDto> GetCacheDataFromDb<TEntity>(IAsbCloudDbContext db, int idTelemetry, int capacity, double hoursOffset) var firstDbEntity = await query
where TEntity : class, ITelemetryData .OrderBy(i => i.DateTime)
{ .FirstOrDefaultAsync(token);
var entities = db.Set<TEntity>()
.Where(i => i.IdTelemetry == idTelemetry) if (firstDbEntity is null)
return default;
var first = firstDbEntity.Adapt<TDto>();
first.DateTime = firstDbEntity.DateTime.ToRemoteDateTime(hoursOffset);
var entities = await query
.OrderByDescending(i => i.DateTime) .OrderByDescending(i => i.DateTime)
.Take(capacity) .Take(capacity)
.ToArray() .ToArrayAsync(token);
var dtos = entities
.AsEnumerable() .AsEnumerable()
.Reverse(); .Reverse()
.Select(entity => {
var dto = entity.Adapt<TDto>();
dto.DateTime = entity.DateTime.ToRemoteDateTime(hoursOffset);
return dto;
});
var dtos = entities.Select(entity => { var cacheItem = new CyclycArray<TDto>(capacity);
var dto = entity.Adapt<TDto>(); cacheItem.AddRange(dtos);
dto.DateTime = entity.DateTime.ToRemoteDateTime(hoursOffset);
return dto;
});
return dtos; var item = new TelemetryDataCacheItem
{
FirstByDate = first,
LastData = cacheItem,
};
return item;
} }
} }
} }

View File

@ -19,21 +19,20 @@ namespace AsbCloudInfrastructure.Services.SAUB
{ {
private readonly IAsbCloudDbContext db; private readonly IAsbCloudDbContext db;
private readonly IMemoryCache memoryCache; private readonly IMemoryCache memoryCache;
private readonly ITelemetryTracker telemetryTracker; private readonly TelemetryDataCache<TelemetryDataSaubDto> dataSaubCache;
private readonly ITimezoneService timezoneService; private readonly ITimezoneService timezoneService;
public ITimezoneService TimeZoneService => timezoneService; public ITimezoneService TimeZoneService => timezoneService;
public ITelemetryTracker TelemetryTracker => telemetryTracker;
public TelemetryService( public TelemetryService(
IAsbCloudDbContext db, IAsbCloudDbContext db,
IMemoryCache memoryCache, IMemoryCache memoryCache,
ITelemetryTracker telemetryTracker, TelemetryDataCache<TelemetryDataSaubDto> dataSaubCache,
ITimezoneService timezoneService) ITimezoneService timezoneService)
{ {
this.db = db; this.db = db;
this.memoryCache = memoryCache; this.memoryCache = memoryCache;
this.telemetryTracker = telemetryTracker; this.dataSaubCache = dataSaubCache;
this.timezoneService = timezoneService; this.timezoneService = timezoneService;
} }
@ -47,34 +46,11 @@ namespace AsbCloudInfrastructure.Services.SAUB
memoryCache.DropBasic<Telemetry>(); memoryCache.DropBasic<Telemetry>();
} }
public DateTime GetLastTelemetryDate(int idTelemetry)
{
var telemetry = GetTelemetryCache().FirstOrDefault(t => t.Id == idTelemetry);
if (telemetry is null)
throw new Exception($"Telemetry id:{idTelemetry} does not exist");
var uid = telemetry.RemoteUid;
var timzone = GetTimezone(idTelemetry);
var lastTelemetryDate = telemetryTracker.GetLastTelemetryDateByUid(uid);
return lastTelemetryDate.ToRemoteDateTime(timzone.Hours);
}
public DatesRangeDto GetDatesRange(int idTelemetry) public DatesRangeDto GetDatesRange(int idTelemetry)
{ {
var telemetry = GetTelemetryCache().FirstOrDefault(t => t.Id == idTelemetry); var cacheDataRange = dataSaubCache.GetOrDefaultDataDateRange(idTelemetry)
if (telemetry is null) ?? new ();
throw new Exception($"Telemetry id:{idTelemetry} does not exist"); return cacheDataRange;
var dto = TelemetryTracker.GetTelemetryDateRangeByUid(telemetry.RemoteUid);
if (dto is null)
throw new Exception($"Telemetry id:{idTelemetry} has no data");
var timezone = GetTimezone(idTelemetry);
dto.From = dto.From.ToTimeZoneOffsetHours(timezone.Hours);
dto.To = dto.To.ToTimeZoneOffsetHours(timezone.Hours);
return dto;
} }
public int GetOrCreateTelemetryIdByUid(string uid) public int GetOrCreateTelemetryIdByUid(string uid)

View File

@ -1,156 +0,0 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.SAUB
{
public class TelemetryTracker : ITelemetryTracker
{
class TrackerStat
{
//public int Id { get; set; }
public string RemoteUid { get; set; } = null!;
/// <summary>
/// Время последнего запроса (по времени сервера)
/// </summary>
public DateTimeOffset LastTimeServer { get; set; }
/// <summary>
/// Дата первых данных в БД
/// </summary>
public DateTimeOffset TelemetryDateUtcMin { get; set; }
/// <summary>
/// Дата последних данных в БД
/// </summary>
public DateTimeOffset TelemetryDateUtcMax { get; set; }
}
private readonly ConcurrentDictionary<string, TrackerStat> telemetriesStats;
public TelemetryTracker(IConfiguration configuration, IMemoryCache memoryCache)
{
// TODO: make this background work
var contextOptions = new DbContextOptionsBuilder<AsbCloudDbContext>()
.UseNpgsql(configuration.GetConnectionString("DefaultConnection"))
.Options;
var db = new AsbCloudDbContext(contextOptions);
var cacheTelemetry = memoryCache.GetOrCreateBasic(db.Set<Telemetry>().Include(t=>t.Well));
var keyValuePairs = new Dictionary<string, TrackerStat>(cacheTelemetry.Count());
foreach (var telemetry in cacheTelemetry)
{
var date = telemetry.Info?.DrillingStartDate
?? ParseDateFromUidOrDefault(telemetry.RemoteUid, DateTimeOffset.MinValue);
keyValuePairs[telemetry.RemoteUid] = new TrackerStat
{
RemoteUid = telemetry.RemoteUid,
TelemetryDateUtcMin = date,
TelemetryDateUtcMax = date,
LastTimeServer = date,
};
}
telemetriesStats = new ConcurrentDictionary<string, TrackerStat>(keyValuePairs);
Task.Run(async () =>
{
db.Database.SetCommandTimeout(2 * 60);
var dates = await db.TelemetryDataSaub
.GroupBy(d => d.IdTelemetry)
.Select(g => new
{
IdTelemetry = g.Key,
DateMax = g.Max(d => d.DateTime),
DateMin = g.Min(d => d.DateTime),
})
.AsNoTracking()
.ToListAsync()
.ConfigureAwait(false);
var oldRequests = dates.Select(t => new
{
Uid = cacheTelemetry.FirstOrDefault(c => c.Id == t.IdTelemetry)?.RemoteUid,
t.DateMax,
t.DateMin,
}).Where(s => !string.IsNullOrEmpty(s.Uid));
foreach (var oldReq in oldRequests)
{
if (oldReq.Uid is not null)
{
var telemetryStat = telemetriesStats.GetOrAdd(oldReq.Uid, (uid) => new TrackerStat { RemoteUid = uid });
telemetryStat.TelemetryDateUtcMin = oldReq.DateMin;
telemetryStat.TelemetryDateUtcMax = oldReq.DateMax;
telemetryStat.LastTimeServer = oldReq.DateMax;
}
}
}).ContinueWith((t) =>
{
db.Dispose();
return t;
});
}
private static DateTimeOffset ParseDateFromUidOrDefault(string remoteUid, DateTimeOffset defaultValue = default)
{
//example: uid = 20211102_173407926
if (string.IsNullOrEmpty(remoteUid) || remoteUid.Length != 18)
return defaultValue;
if (DateTime.TryParseExact(remoteUid, "yyyyMMdd_HHmmssfff",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.AssumeUniversal,
out DateTime parsedDate))
return parsedDate;
return defaultValue;
}
public void SaveRequestDate(string uid, DateTimeOffset remoteDate)
{
var stat = telemetriesStats.GetOrAdd(uid, _ => new TrackerStat
{
RemoteUid = uid,
TelemetryDateUtcMin = remoteDate
}
);
stat.LastTimeServer = DateTime.Now;
if (stat.TelemetryDateUtcMax.ToUniversalTime() < remoteDate.ToUniversalTime())
stat.TelemetryDateUtcMax = remoteDate;
}
public DateTimeOffset GetLastTelemetryDateByUid(string uid) =>
telemetriesStats.GetValueOrDefault(uid)?.TelemetryDateUtcMax ?? default;
public DatesRangeDto GetTelemetryDateRangeByUid(string uid)
{
var stat = telemetriesStats.GetValueOrDefault(uid);
var range = new DatesRangeDto
{
From = stat?.TelemetryDateUtcMin.UtcDateTime ?? default,
To = stat?.TelemetryDateUtcMax.UtcDateTime ?? default,
};
return range;
}
public IEnumerable<string> GetTransmittingTelemetriesUids() =>
telemetriesStats.Keys;
}
}

View File

@ -1,13 +1,14 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMap; using AsbCloudApp.Data.ProcessMap;
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudApp.Requests; using AsbCloudApp.Requests;
using AsbCloudApp.Services; using AsbCloudApp.Services;
using AsbCloudApp.Services.Subsystems; using AsbCloudApp.Services.Subsystems;
using AsbCloudDb.Model; using AsbCloudDb.Model;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using AsbCloudInfrastructure.Services.SAUB;
using Mapster; using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -46,6 +47,7 @@ namespace AsbCloudInfrastructure.Services
var operationsStatService = serviceProvider.GetRequiredService<IOperationsStatService>(); var operationsStatService = serviceProvider.GetRequiredService<IOperationsStatService>();
var processMapRepository = serviceProvider.GetRequiredService<IProcessMapPlanRepository>(); var processMapRepository = serviceProvider.GetRequiredService<IProcessMapPlanRepository>();
var subsystemOperationTimeService = serviceProvider.GetRequiredService<ISubsystemOperationTimeService>(); var subsystemOperationTimeService = serviceProvider.GetRequiredService<ISubsystemOperationTimeService>();
var telemetryDataSaubCache = serviceProvider.GetRequiredService<TelemetryDataCache<TelemetryDataSaubDto>>();
var activeWells = await wellService.GetAsync(new() {IdState = 1}, token); var activeWells = await wellService.GetAsync(new() {IdState = 1}, token);
@ -56,21 +58,6 @@ namespace AsbCloudInfrastructure.Services
.Where(w => w.IdTelemetry != null) .Where(w => w.IdTelemetry != null)
.Select(t => t.IdTelemetry); .Select(t => t.IdTelemetry);
var lastTelemetryInfo = await db.TelemetryDataSaub
.Where(t => idTelemetries.Contains(t.IdTelemetry))
.Select(t => new
{
t.IdTelemetry,
t.WellDepth,
t.DateTime,
})
.GroupBy(t => t.IdTelemetry)
.Select(g => g.OrderByDescending(t => t.DateTime)
.First()
)
.AsNoTracking()
.ToArrayAsync(token);
var processMapRequests = activeWellsIds.Select(id => new ProcessMapRequest { IdWell = id }); var processMapRequests = activeWellsIds.Select(id => new ProcessMapRequest { IdWell = id });
var processMaps = await processMapRepository.GetProcessMapAsync(processMapRequests, token); var processMaps = await processMapRepository.GetProcessMapAsync(processMapRequests, token);
@ -83,43 +70,53 @@ namespace AsbCloudInfrastructure.Services
}); });
var operationsStat = await operationsStatService.GetWellsStatAsync(activeWellsIds, token); var operationsStat = await operationsStatService.GetWellsStatAsync(activeWellsIds, token);
var subsystemStat = await subsystemOperationTimeService.GetStatByActiveWells(activeWellsIds, token); var subsystemStat = await subsystemOperationTimeService.GetStatByActiveWells(activeWellsIds, token);
WellMapInfo = activeWells.Select(well => { WellMapInfo = activeWells.Select(well => {
var wellMapInfo = well.Adapt<WellMapInfoWithComanies>(); var wellMapInfo = well.Adapt<WellMapInfoWithComanies>();
var wellLastTelemetryInfo = lastTelemetryInfo.FirstOrDefault(t => t.IdTelemetry == well.IdTelemetry);
var wellOperationsStat = operationsStat.FirstOrDefault(s => s.Id == well.Id); var wellOperationsStat = operationsStat.FirstOrDefault(s => s.Id == well.Id);
var wellLastFactSection = wellOperationsStat?.Sections.LastOrDefault(s => s.Fact is not null); var wellLastFactSection = wellOperationsStat?.Sections.LastOrDefault(s => s.Fact is not null);
var wellSubsystemStat = subsystemStat.FirstOrDefault(s => s.Well.Id == well.Id); double? currentDepth = null;
double? planTotalDepth = null;
DateTime lastTelemetryDate = default;
double currentDepth = wellLastTelemetryInfo?.WellDepth if (well.IdTelemetry.HasValue)
?? wellLastFactSection?.Fact?.WellDepthEnd {
?? 0d; var lastSaubTelemetry = telemetryDataSaubCache.GetLastOrDefault(well.IdTelemetry.Value);
if(lastSaubTelemetry is not null)
{
currentDepth = lastSaubTelemetry.WellDepth;
lastTelemetryDate = lastSaubTelemetry.DateTime;
}
}
currentDepth ??= wellLastFactSection?.Fact?.WellDepthEnd;
var wellProcessMaps = processMaps var wellProcessMaps = processMaps
.Where(p => p.IdWell == well.Id) .Where(p => p.IdWell == well.Id)
.OrderBy(p => p.DepthEnd); .OrderBy(p => p.DepthEnd);
int? idSection = wellLastFactSection?.Id; int? idSection = wellLastFactSection?.Id;
ProcessMapPlanDto? welllProcessMap = null;
ProcessMapPlanDto? welllProcessMap; if (idSection.HasValue)
if (idSection is not null)
{ {
welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.IdWellSectionType == idSection); welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.IdWellSectionType == idSection);
} }
else else if(currentDepth.HasValue)
{ {
welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.DepthStart <= currentDepth && p.DepthEnd >= currentDepth); welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.DepthStart <= currentDepth.Value && p.DepthEnd >= currentDepth.Value);
idSection ??= welllProcessMap?.IdWellSectionType; idSection ??= welllProcessMap?.IdWellSectionType;
} }
wellMapInfo.LastTelemetryDate = wellLastTelemetryInfo?.DateTime.ToRemoteDateTime(5) ?? new DateTime(); planTotalDepth = wellDepthByProcessMap.FirstOrDefault(p => p.Id == well.Id)?.DepthEnd;
planTotalDepth ??= wellOperationsStat?.Total.Plan?.WellDepthEnd;
wellMapInfo.WellDepth = new() wellMapInfo.WellDepth = new()
{ {
Plan = wellDepthByProcessMap.FirstOrDefault(p => p.Id == well.Id)?.DepthEnd, Plan = planTotalDepth,
Fact = currentDepth, Fact = currentDepth,
}; };
@ -135,6 +132,7 @@ namespace AsbCloudInfrastructure.Services
Fact = wellOperationsStat?.Total.Fact?.RouteSpeed, Fact = wellOperationsStat?.Total.Fact?.RouteSpeed,
}; };
var wellSubsystemStat = subsystemStat.FirstOrDefault(s => s.Well.Id == well.Id);
wellMapInfo.SaubUsage = wellSubsystemStat?.SubsystemAKB?.KUsage ?? 0d; wellMapInfo.SaubUsage = wellSubsystemStat?.SubsystemAKB?.KUsage ?? 0d;
wellMapInfo.SpinUsage = wellSubsystemStat?.SubsystemSpinMaster?.KUsage ?? 0d; wellMapInfo.SpinUsage = wellSubsystemStat?.SubsystemSpinMaster?.KUsage ?? 0d;
wellMapInfo.TvdLagPercent = wellOperationsStat?.TvdLagDays ?? 0d; wellMapInfo.TvdLagPercent = wellOperationsStat?.TvdLagDays ?? 0d;

View File

@ -151,7 +151,7 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
WellType = wellType?.Caption ?? "", WellType = wellType?.Caption ?? "",
IdState = well.IdState, IdState = well.IdState,
State = wellService.GetStateText(well.IdState), State = wellService.GetStateText(well.IdState),
LastTelemetryDate = wellService.GetLastTelemetryDate(well.Id).DateTime, LastTelemetryDate = wellService.GetLastTelemetryDate(well.Id),
Companies = await wellService.GetCompaniesAsync(well.Id, token) Companies = await wellService.GetCompaniesAsync(well.Id, token)
}; };

View File

@ -57,15 +57,15 @@ namespace AsbCloudInfrastructure.Services
private void DropCacheRelationCompanyWell() private void DropCacheRelationCompanyWell()
=> memoryCache.DropBasic<RelationCompanyWell>(); => memoryCache.DropBasic<RelationCompanyWell>();
public DateTimeOffset GetLastTelemetryDate(int idWell) public DateTime GetLastTelemetryDate(int idWell)
{ {
var well = GetOrDefault(idWell); var well = GetOrDefault(idWell);
if (well?.IdTelemetry is null) if (well?.IdTelemetry is null)
return DateTimeOffset.MinValue; return DateTime.MinValue;
var lastTelemetryDate = telemetryService.GetLastTelemetryDate((int)well.IdTelemetry); var datesRange = telemetryService.GetDatesRange(well.IdTelemetry.Value);
return lastTelemetryDate; return datesRange.To;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -97,7 +97,7 @@ namespace AsbCloudInfrastructure.Services
dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude; dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude;
dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude; dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude;
if (well.IdTelemetry is not null) if (well.IdTelemetry is not null)
dto.LastTelemetryDate = telemetryService.GetLastTelemetryDate(well.IdTelemetry.Value); dto.LastTelemetryDate = telemetryService.GetDatesRange(well.IdTelemetry.Value).To;
return dto; return dto;
}), }),
@ -256,7 +256,7 @@ namespace AsbCloudInfrastructure.Services
dto.Cluster = entity.Cluster.Caption; dto.Cluster = entity.Cluster.Caption;
dto.Deposit = entity.Cluster.Deposit.Caption; dto.Deposit = entity.Cluster.Deposit.Caption;
if (entity.IdTelemetry is not null) if (entity.IdTelemetry is not null)
dto.LastTelemetryDate = telemetryService.GetLastTelemetryDate((int)entity.IdTelemetry); dto.LastTelemetryDate = telemetryService.GetDatesRange(entity.IdTelemetry.Value).To;
dto.Companies = entity.RelationCompaniesWells dto.Companies = entity.RelationCompaniesWells
.Select(r => Convert(r.Company)) .Select(r => Convert(r.Company))
.ToList(); .ToList();

View File

@ -10,6 +10,8 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading; using System.Threading;
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using AsbCloudApp.Data.SAUB;
using AsbCloudInfrastructure.Services.SAUB;
namespace AsbCloudInfrastructure namespace AsbCloudInfrastructure
{ {
@ -28,6 +30,9 @@ namespace AsbCloudInfrastructure
var wellService = provider.GetRequiredService<IWellService>(); var wellService = provider.GetRequiredService<IWellService>();
wellService.EnshureTimezonesIsSetAsync(CancellationToken.None).Wait();// TODO: make this background work wellService.EnshureTimezonesIsSetAsync(CancellationToken.None).Wait();// TODO: make this background work
_ = provider.GetRequiredService<TelemetryDataCache<TelemetryDataSaubDto>>();
_ = provider.GetRequiredService<TelemetryDataCache<TelemetryDataSpinDto>>();
var backgroundWorker = provider.GetRequiredService<BackgroundWorker>(); var backgroundWorker = provider.GetRequiredService<BackgroundWorker>();
backgroundWorker.Push(WellInfoService.MakeWork()); backgroundWorker.Push(WellInfoService.MakeWork());
backgroundWorker.Push(OperationDetectionWorkFactory.MakeWork()); backgroundWorker.Push(OperationDetectionWorkFactory.MakeWork());
@ -45,7 +50,7 @@ namespace AsbCloudInfrastructure
var workAction = (string _, IServiceProvider _, CancellationToken _) => { var workAction = (string _, IServiceProvider _, CancellationToken _) => {
var bytes = GC.GetTotalMemory(false); var bytes = GC.GetTotalMemory(false);
var bytesString = FromatBytes(bytes); var bytesString = FromatBytes(bytes);
System.Diagnostics.Trace.TraceInformation($"Total memory allocated is {bytesString} bytes. DbContext count is:{AsbCloudDb.Model.AsbCloudDbContext.ReferenceCount}"); System.Diagnostics.Trace.TraceInformation($"Total memory allocated is {bytesString} bytes. DbContext count is:{AsbCloudDbContext.ReferenceCount}");
return Task.CompletedTask; return Task.CompletedTask;
}; };
var workPeriod = TimeSpan.FromMinutes(1); var workPeriod = TimeSpan.FromMinutes(1);