diff --git a/AsbCloudApp/Repositories/IGtrRepository.cs b/AsbCloudApp/Repositories/IGtrRepository.cs
index 7aad6629..0d597dde 100644
--- a/AsbCloudApp/Repositories/IGtrRepository.cs
+++ b/AsbCloudApp/Repositories/IGtrRepository.cs
@@ -38,8 +38,14 @@ namespace AsbCloudApp.Repositories
///
///
///
- ///
///
- Task> GetLastDataByRecordIdAsync(int idWell, int idRecord, CancellationToken token = default);
+ IEnumerable GetLastDataByRecordId(int idWell, int idRecord);
+
+ ///
+ /// Последние полученные параметры
+ ///
+ ///
+ ///
+ IEnumerable GetLastData(int idWell);
}
}
diff --git a/AsbCloudInfrastructure/LinqExtensions.cs b/AsbCloudInfrastructure/LinqExtensions.cs
new file mode 100644
index 00000000..cf182051
--- /dev/null
+++ b/AsbCloudInfrastructure/LinqExtensions.cs
@@ -0,0 +1,39 @@
+// Ignore Spelling: Linq
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AsbCloudInfrastructure
+{
+ public static class LinqExtensions
+ {
+ public static TProp? MaxOrDefault(this IEnumerable enumerable, Func getter)
+ where TProp : struct
+ {
+ var value = MaxByOrDefault(enumerable, getter);
+ if (value is null)
+ return null;
+ return getter(value);
+ }
+
+ public static TObj? MaxByOrDefault(this IEnumerable enumerable, Func getter)
+ {
+ return enumerable.OrderByDescending(getter).FirstOrDefault();
+ }
+
+ public static TProp? MinOrDefault(this IEnumerable enumerable, Func getter)
+ where TProp : struct
+ {
+ var value = MinByOrDefault(enumerable, getter);
+ if (value is null)
+ return null;
+ return getter(value);
+ }
+
+ public static TObj? MinByOrDefault(this IEnumerable enumerable, Func getter)
+ {
+ return enumerable.OrderBy(getter).FirstOrDefault();
+ }
+ }
+}
diff --git a/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs b/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs
index a0af9bf7..12b97ea7 100644
--- a/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs
+++ b/AsbCloudInfrastructure/Repository/GtrWitsRepository.cs
@@ -5,6 +5,7 @@ using AsbCloudDb.Model;
using AsbCloudDb.Model.GTR;
using Microsoft.EntityFrameworkCore;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -16,12 +17,12 @@ namespace AsbCloudInfrastructure.Repository
{
private readonly IAsbCloudDbContext db;
private readonly ITelemetryService telemetryService;
+ private static ConcurrentDictionary> cache = new();
public GtrWitsRepository(
IAsbCloudDbContext db,
ITelemetryService telemetryService)
{
-
this.db = db;
this.telemetryService = telemetryService;
}
@@ -69,62 +70,21 @@ namespace AsbCloudInfrastructure.Repository
return dtos;
}
- public async Task> GetLastDataByRecordIdAsync(int idWell, int idRecord, CancellationToken token = default)
+ public IEnumerable GetLastDataByRecordId(int idWell, int idRecord)
+ {
+ var result = GetLastData(idWell)
+ .Where(item => item.IdRecord == idRecord);
+ return result;
+ }
+
+ public IEnumerable GetLastData(int idWell)
{
var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell);
if (telemetry is null)
return Enumerable.Empty();
- var timezone = telemetryService.GetTimezone(telemetry.Id);
-
- var witsRequest = new WitsRequest()
- {
- IdTelemetry = telemetry.Id,
- TimezoneHours = timezone.Hours,
- IdRecord = idRecord,
- };
-
- var recordAllInt = await GetGroupedItemsOrDefaultAsync(witsRequest, token);
- var recordAllFloat = await GetGroupedItemsOrDefaultAsync(witsRequest, token);
- var recordAllString = await GetGroupedItemsOrDefaultAsync(witsRequest, token);
-
- var dtos = recordAllFloat.Union(recordAllInt).Union(recordAllString);
- return dtos;
- }
-
- private async Task> GetGroupedItemsOrDefaultAsync(WitsRequest request, CancellationToken token)
- where TEntity : WitsItemBase
- where TValue : notnull
- {
- var query = BuildQuery(request);
- var groupedQuery = query.GroupBy(g => new
- {
- g.IdRecord,
- g.IdTelemetry,
- g.IdItem
- })
- .Select(g => new
- {
- g.Key.IdRecord,
- g.Key.IdItem,
- Data = g.OrderByDescending(i => i.DateTime)
- .FirstOrDefault()
- });
-
- var groupedEntities = await groupedQuery
- .ToArrayAsync(token)
- .ConfigureAwait(false);
-
- var dtos = groupedEntities
- .Select(e => new WitsItemRecordDto()
- {
- IdRecord = e.IdRecord,
- IdItem = e.IdItem,
- Date = e.Data!.DateTime.ToRemoteDateTime(request.TimezoneHours),
- Value = new JsonValue(e.Data!.Value)
- });
-
- return dtos;
+ var lastData = cache.GetValueOrDefault(telemetry.Id);
+ return lastData?.Values ?? Enumerable.Empty();
}
private async Task> GetItemsOrDefaultAsync(
@@ -187,29 +147,92 @@ namespace AsbCloudInfrastructure.Repository
{
var timezoneHours = telemetryService.GetTimezone(idTelemetry).Hours;
- foreach (var dto in dtos)
+ var cacheTelemetryItems = cache.GetValueOrDefault(idTelemetry);
+
+ foreach (var record in dtos)
{
- foreach (var item in dto.Items)
+ var dateTime = record.Date.ToUtcDateTimeOffset(timezoneHours);
+ foreach (var item in record.Items)
{
- var dateTime = dto.Date.ToUtcDateTimeOffset(timezoneHours);
+ if (cacheTelemetryItems?.TryGetValue((record.Id, item.Key), out var cacheItem) == true)
+ if (Math.Abs((dateTime - cacheItem.Date).TotalSeconds) < 1)
+ continue;
+
if (item.Value.Value is string valueString)
{
- var entity = MakeEntity(dto.Id, item.Key, idTelemetry, dateTime, valueString);
+ var entity = MakeEntity(record.Id, item.Key, idTelemetry, dateTime, valueString);
db.WitsItemString.Add(entity);
}
if (item.Value.Value is float valueFloat)
{
- var entity = MakeEntity(dto.Id, item.Key, idTelemetry, dateTime, valueFloat);
+ var entity = MakeEntity(record.Id, item.Key, idTelemetry, dateTime, valueFloat);
db.WitsItemFloat.Add(entity);
}
if (item.Value.Value is int valueInt)
{
- var entity = MakeEntity(dto.Id, item.Key, idTelemetry, dateTime, valueInt);
+ var entity = MakeEntity(record.Id, item.Key, idTelemetry, dateTime, valueInt);
db.WitsItemInt.Add(entity);
}
}
}
- await db.SaveChangesAsync(token);
+
+ try
+ {
+ await db.SaveChangesAsync(token);
+ }
+ catch(DbUpdateException ex)
+ {
+ var isRelational = ex.Source == "Microsoft.EntityFrameworkCore.Relational" && ex.Entries.Any();
+ if (!isRelational)
+ throw;
+ }
+
+ cache.AddOrUpdate(idTelemetry,
+ (_) => MakeNewCache(dtos),
+ (_, oldItemsDictionary) => {
+ foreach (var record in dtos)
+ foreach (var item in record.Items)
+ {
+ oldItemsDictionary.AddOrUpdate(
+ (record.Id, item.Key),
+ (_) => new WitsItemRecordDto
+ {
+ IdRecord = record.Id,
+ IdItem = item.Key,
+ Date = record.Date,
+ Value = item.Value
+ },
+ (_, _) => new WitsItemRecordDto
+ {
+ IdRecord = record.Id,
+ IdItem = item.Key,
+ Date = record.Date,
+ Value = item.Value
+ });
+ }
+ return oldItemsDictionary;
+ });
+ }
+
+ private static ConcurrentDictionary<(int, int), WitsItemRecordDto> MakeNewCache(IEnumerable dtos)
+ {
+ var items = dtos.SelectMany(record =>
+ record.Items.Select(
+ item => new WitsItemRecordDto {
+ IdItem = item.Key,
+ IdRecord = record.Id,
+ Date = record.Date,
+ Value = item.Value,
+ }));
+
+ var groups = items
+ .GroupBy(item => (item.IdRecord, item.IdItem));
+
+ var pairs = groups.Select(group => new KeyValuePair<(int, int), WitsItemRecordDto>(
+ group.Key,
+ group.OrderByDescending(item => item.Date).First()));
+
+ return new ConcurrentDictionary<(int, int), WitsItemRecordDto>(pairs);
}
private static TEntity MakeEntity(int idRecord, int idItem, int idTelemetry, DateTimeOffset dateTime, TValue value)
@@ -224,6 +247,18 @@ namespace AsbCloudInfrastructure.Repository
Value = value,
};
+ private static TEntity MakeEntity(WitsItemRecordDto dto, int idTelemetry, DateTimeOffset dateTime)
+ where TEntity : WitsItemBase, new()
+ where TValue : notnull
+ => new TEntity()
+ {
+ IdRecord = dto.IdRecord,
+ IdItem = dto.IdItem,
+ IdTelemetry = idTelemetry,
+ DateTime = dateTime,
+ Value = (TValue)dto.Value.Value,
+ };
+
private class WitsRequest
{
public int IdTelemetry { get; set; }
diff --git a/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs b/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs
index 1209ea65..a6cf23a2 100644
--- a/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs
+++ b/AsbCloudWebApi/Controllers/SAUB/GtrWitsController.cs
@@ -77,7 +77,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
///
[HttpGet("{idWell}/{idRecord}")]
[Permission]
- public async Task>> GetLastDataByRecordIdAsync(int idWell, int idRecord, CancellationToken token = default)
+ public async Task>> GetLastDataByRecordIdAsync(int idWell, int idRecord, CancellationToken token)
{
int? idCompany = User.GetCompanyId();
@@ -90,7 +90,7 @@ namespace AsbCloudWebApi.Controllers.SAUB
if (!isCompanyOwnsWell)
return Forbid();
- var content = await gtrRepository.GetLastDataByRecordIdAsync(idWell, idRecord, token).ConfigureAwait(false);
+ var content = gtrRepository.GetLastDataByRecordId(idWell, idRecord);
return Ok(content);
}