#408 Репозиторий хранения данных WITS0 #7
@ -6,6 +6,9 @@ using Persistence.Services.Interfaces;
|
|||||||
|
|
||||||
namespace Persistence.API.Controllers;
|
namespace Persistence.API.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Работа с параметрами Wits
|
||||||
|
/// </summary>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
@ -51,15 +54,17 @@ public class WitsDataController : ControllerBase, IWitsDataApi
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить набор параметров (Wits) для построения графика
|
/// Получить набор параметров (Wits) для построения графика
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dateFrom"></param>
|
/// <param name="discriminatorId">Дискриминатор системы</param>
|
||||||
/// <param name="dateTo"></param>
|
/// <param name="dateFrom">Начало временного интервала</param>
|
||||||
/// <param name="limit"></param>
|
/// <param name="dateTo">Конец временного интервала</param>
|
||||||
|
/// <param name="approxPointsCount">Количество точек</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("graph")]
|
[HttpGet("graph")]
|
||||||
public async Task<ActionResult<IEnumerable<WitsDataDto>>> GetValuesForGraph([FromQuery] DateTimeOffset dateFrom, [FromQuery] DateTimeOffset dateTo, [FromQuery] int limit, CancellationToken token)
|
public async Task<ActionResult<IEnumerable<WitsDataDto>>> GetValuesForGraph([FromQuery] int discriminatorId,
|
||||||
|
|||||||
|
[FromQuery] DateTimeOffset dateFrom, [FromQuery] DateTimeOffset dateTo, [FromQuery] int approxPointsCount, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await witsDataService.GetValuesForGraph(dateFrom, dateTo);
|
var result = await witsDataService.GetValuesForGraph(discriminatorId, dateFrom, dateTo, approxPointsCount, token);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
@ -72,10 +77,10 @@ public class WitsDataController : ControllerBase, IWitsDataApi
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
|
||||||
public async Task<IActionResult> InsertRange([FromBody] IEnumerable<WitsDataDto> dtos, CancellationToken token)
|
public async Task<IActionResult> AddRange([FromBody] IEnumerable<WitsDataDto> dtos, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = await witsDataService.InsertRange(dtos, token);
|
var result = await witsDataService.AddRange(dtos, token);
|
||||||
|
|
||||||
return CreatedAtAction(nameof(InsertRange), result);
|
return CreatedAtAction(nameof(AddRange), result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ public interface IWitsDataClient
|
|||||||
private const string BaseRoute = "/api/witsData";
|
private const string BaseRoute = "/api/witsData";
|
||||||
|
|
||||||
[Get($"{BaseRoute}/graph")]
|
[Get($"{BaseRoute}/graph")]
|
||||||
Task<IApiResponse<IEnumerable<WitsDataDto>>> GetValuesForGraph([Query] DateTimeOffset dateFrom, [Query] DateTimeOffset dateTo, [Query] int limit, CancellationToken token);
|
Task<IApiResponse<IEnumerable<WitsDataDto>>> GetValuesForGraph([Query] int discriminatorId, [Query] DateTimeOffset dateFrom, [Query] DateTimeOffset dateTo, [Query] int approxPointsCount, CancellationToken token);
|
||||||
on.nemtina
commented
discriminatorId лучше добавить в роут discriminatorId лучше добавить в роут
|
|||||||
|
|
||||||
[Post($"{BaseRoute}/")]
|
[Post($"{BaseRoute}/")]
|
||||||
Task<IApiResponse<int>> InsertRange([Body] IEnumerable<WitsDataDto> dtos, CancellationToken token);
|
Task<IApiResponse<int>> AddRange([Body] IEnumerable<WitsDataDto> dtos, CancellationToken token);
|
||||||
|
|
||||||
[Get($"{BaseRoute}/part")]
|
[Get($"{BaseRoute}/part")]
|
||||||
Task<IApiResponse<IEnumerable<WitsDataDto>>> GetPart([Query] int discriminatorId, [Query] DateTimeOffset dateBegin, [Query] int take = 24 * 60 * 60, CancellationToken token = default);
|
Task<IApiResponse<IEnumerable<WitsDataDto>>> GetPart([Query] int discriminatorId, [Query] DateTimeOffset dateBegin, [Query] int take = 24 * 60 * 60, CancellationToken token = default);
|
||||||
|
@ -62,7 +62,7 @@ public class WitsDataControllerTest : BaseIntegrationTest
|
|||||||
dbContext.CleanupDbSet<ParameterData>();
|
dbContext.CleanupDbSet<ParameterData>();
|
||||||
|
|
||||||
//act
|
//act
|
||||||
await InsertRange();
|
await AddRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -71,19 +71,27 @@ public class WitsDataControllerTest : BaseIntegrationTest
|
|||||||
//arrange
|
//arrange
|
||||||
dbContext.CleanupDbSet<ParameterData>();
|
dbContext.CleanupDbSet<ParameterData>();
|
||||||
|
|
||||||
|
var discriminatorId = 1;
|
||||||
|
var dateFrom = DateTimeOffset.UtcNow;
|
||||||
|
var dateTo = DateTimeOffset.UtcNow;
|
||||||
|
var approxPointCount = 12;
|
||||||
|
|
||||||
//act
|
//act
|
||||||
|
var response = await witsDataClient.GetValuesForGraph(discriminatorId, dateFrom, dateTo, approxPointCount, new CancellationToken());
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.Empty(response.Content);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region AfterSave
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetDatesRangeAsync_AfterSave_returns_success()
|
public async Task GetDatesRangeAsync_AfterSave_returns_success()
|
||||||
{
|
{
|
||||||
//arrange
|
//arrange
|
||||||
dbContext.CleanupDbSet<ParameterData>();
|
dbContext.CleanupDbSet<ParameterData>();
|
||||||
|
|
||||||
var dtos = await InsertRange();
|
var dtos = await AddRange();
|
||||||
var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;
|
var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;
|
||||||
|
|
||||||
//act
|
//act
|
||||||
@ -116,7 +124,7 @@ public class WitsDataControllerTest : BaseIntegrationTest
|
|||||||
//arrange
|
//arrange
|
||||||
dbContext.CleanupDbSet<ParameterData>();
|
dbContext.CleanupDbSet<ParameterData>();
|
||||||
|
|
||||||
var dtos = await InsertRange();
|
var dtos = await AddRange();
|
||||||
var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;
|
var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;
|
||||||
var dateBegin = dtos.FirstOrDefault()!.Timestamped;
|
var dateBegin = dtos.FirstOrDefault()!.Timestamped;
|
||||||
var take = 1;
|
var take = 1;
|
||||||
@ -134,11 +142,24 @@ public class WitsDataControllerTest : BaseIntegrationTest
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetValuesForGraph_AfterSave_returns_success()
|
public async Task GetValuesForGraph_AfterSave_returns_success()
|
||||||
{
|
{
|
||||||
|
//arrange
|
||||||
|
dbContext.CleanupDbSet<ParameterData>();
|
||||||
|
|
||||||
|
var dtos = await AddRange(37);
|
||||||
|
var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;
|
||||||
|
var dateFrom = dtos.Select(e => e.Timestamped).Min();
|
||||||
|
var dateTo = dtos.Select(e => e.Timestamped).Max();
|
||||||
|
var approxPointCount = 12;
|
||||||
|
|
||||||
|
//act
|
||||||
|
var response = await witsDataClient.GetValuesForGraph(discriminatorId, dateFrom, dateTo, approxPointCount, new CancellationToken());
|
||||||
|
|
||||||
|
//assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.Equal(approxPointCount, response.Content.Count());
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region BadRequest
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task InsertRange_returns_BadRequest()
|
public async Task InsertRange_returns_BadRequest()
|
||||||
on.nemtina
commented
AddRange_returns_BadRequest AddRange_returns_BadRequest
|
|||||||
{
|
{
|
||||||
@ -155,63 +176,43 @@ public class WitsDataControllerTest : BaseIntegrationTest
|
|||||||
{
|
{
|
||||||
RecordId = -1, // < 0
|
RecordId = -1, // < 0
|
||||||
ItemId = 101, // > 100
|
ItemId = 101, // > 100
|
||||||
Value = "string value"
|
Value = string.Empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//act
|
//act
|
||||||
var response = await witsDataClient.InsertRange(dtos, new CancellationToken());
|
var response = await witsDataClient.AddRange(dtos, new CancellationToken());
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
|
|
||||||
private async Task<IEnumerable<WitsDataDto>> InsertRange()
|
private async Task<IEnumerable<WitsDataDto>> AddRange(int countToCreate = 10)
|
||||||
{
|
{
|
||||||
//arrange
|
var dtos = new List<WitsDataDto>();
|
||||||
var dtos = new List<WitsDataDto>()
|
var timestamped = DateTimeOffset.UtcNow;
|
||||||
|
for (var i = 0; i < countToCreate && i < 100; i++)
|
||||||
on.nemtina
commented
i < countToCreate && i < 100 i < countToCreate && i < 100
А это так и нужно?
rs.efremov
commented
По сути нет, это на всякий случай - вдруг кому то когда нибудь придёт идея написать в этом контроллере тест и передать в качестве count >100. Хотя, наверное, лучше тогда человеку сразу увидеть ошибку, чем гадать - почему он говорит создать 150 записей, а создаётся 100 )) По сути нет, это на всякий случай - вдруг кому то когда нибудь придёт идея написать в этом контроллере тест и передать в качестве count >100. Хотя, наверное, лучше тогда человеку сразу увидеть ошибку, чем гадать - почему он говорит создать 150 записей, а создаётся 100 ))
|
|||||||
{
|
{
|
||||||
new WitsDataDto()
|
dtos.Add(new WitsDataDto()
|
||||||
{
|
{
|
||||||
DiscriminatorId = 1,
|
DiscriminatorId = 1,
|
||||||
Timestamped = DateTimeOffset.UtcNow,
|
Timestamped = timestamped.AddSeconds(i),
|
||||||
Values = new List<WitsValueDto>()
|
Values = new List<WitsValueDto>()
|
||||||
{
|
{
|
||||||
new WitsValueDto()
|
new WitsValueDto()
|
||||||
{
|
{
|
||||||
RecordId = 11,
|
RecordId = i + 1,
|
||||||
ItemId = 22,
|
ItemId = i + 1,
|
||||||
Value = "string value"
|
Value = new Random().Next(1, 100)
|
||||||
on.nemtina
commented
Лучше new Random() вынести на уровень выше, чтобы в цикле не создавался каждый раз новый экземпляр Лучше new Random() вынести на уровень выше, чтобы в цикле не создавался каждый раз новый экземпляр
|
|||||||
},
|
|
||||||
new WitsValueDto()
|
|
||||||
{
|
|
||||||
RecordId = 11,
|
|
||||||
ItemId = 27,
|
|
||||||
Value = 2.22
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
new WitsDataDto()
|
|
||||||
{
|
|
||||||
DiscriminatorId = 2,
|
|
||||||
Timestamped = DateTimeOffset.UtcNow,
|
|
||||||
Values = new List<WitsValueDto>()
|
|
||||||
{
|
|
||||||
new WitsValueDto()
|
|
||||||
{
|
|
||||||
RecordId = 13,
|
|
||||||
ItemId = 14,
|
|
||||||
Value = "string value"
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//act
|
//act
|
||||||
var response = await witsDataClient.InsertRange(dtos, new CancellationToken());
|
var response = await witsDataClient.AddRange(dtos, new CancellationToken());
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
var count = dtos.SelectMany(e => e.Values).Count();
|
var count = dtos.SelectMany(e => e.Values).Count();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Mapster;
|
using Mapster;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using Persistence.Database.Entity;
|
using Persistence.Database.Entity;
|
||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
using Persistence.Repositories;
|
using Persistence.Repositories;
|
||||||
@ -49,12 +50,31 @@ public class ParameterRepository : IParameterRepository
|
|||||||
return dtos;
|
return dtos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ParameterDto> GetValuesForGraph(DateTimeOffset dateFrom, DateTimeOffset dateTo)
|
public async Task<IEnumerable<ParameterDto>> GetValuesForGraph(int discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo,
|
||||||
|
int approxPointsCount, int? ratio, CancellationToken token)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var query = db.Set<ParameterData>().AsNoTracking();
|
||||||
|
var universalDateFrom = dateFrom.ToUniversalTime();
|
||||||
|
var universalDateTo = dateTo.ToUniversalTime();
|
||||||
|
|
||||||
|
query = query
|
||||||
|
.Where(e => e.DiscriminatorId == discriminatorId && e.Timestamp >= universalDateFrom && e.Timestamp <= universalDateTo)
|
||||||
on.nemtina
commented
Тут для удобства чтения лучше разделиnь Where на 2 части: Тут для удобства чтения лучше разделиnь Where на 2 части:
.Where(e => e.DiscriminatorId == discriminatorId)
.Where(e => e.Timestamp >= universalDateFrom && e.Timestamp <= universalDateTo)
|
|||||||
|
.OrderBy(e => e.Timestamp);
|
||||||
|
if (ratio != null)
|
||||||
|
{
|
||||||
|
query = query.Where(e => ((int) (e.Timestamp - dateFrom).TotalSeconds) % ratio == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> InsertRange(IEnumerable<ParameterDto> dtos, CancellationToken token)
|
var entities = await query
|
||||||
|
.Take((int)(2.5 * approxPointsCount))
|
||||||
|
.ToListAsync(token);
|
||||||
|
|
||||||
|
var dtos = entities.Select(e => e.Adapt<ParameterDto>());
|
||||||
|
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddRange(IEnumerable<ParameterDto> dtos, CancellationToken token)
|
||||||
{
|
{
|
||||||
var entities = dtos.Select(e => e.Adapt<ParameterData>());
|
var entities = dtos.Select(e => e.Adapt<ParameterData>());
|
||||||
|
|
||||||
|
@ -11,10 +11,14 @@ public interface IWitsDataApi : ISyncWithDiscriminatorApi<WitsDataDto>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить набор параметров (Wits) для построения графика
|
/// Получить набор параметров (Wits) для построения графика
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="discriminatorId"></param>
|
||||||
/// <param name="dateFrom"></param>
|
/// <param name="dateFrom"></param>
|
||||||
/// <param name="dateTo"></param>
|
/// <param name="dateTo"></param>
|
||||||
|
/// <param name="approxPointsCount"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ActionResult<IEnumerable<WitsDataDto>>> GetValuesForGraph(DateTimeOffset dateFrom, DateTimeOffset dateTo, int limit, CancellationToken token);
|
Task<ActionResult<IEnumerable<WitsDataDto>>> GetValuesForGraph(int discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo,
|
||||||
|
int approxPointsCount, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Сохранить набор параметров (Wits)
|
/// Сохранить набор параметров (Wits)
|
||||||
@ -22,5 +26,5 @@ public interface IWitsDataApi : ISyncWithDiscriminatorApi<WitsDataDto>
|
|||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IActionResult> InsertRange(IEnumerable<WitsDataDto> dtos, CancellationToken token);
|
Task<IActionResult> AddRange(IEnumerable<WitsDataDto> dtos, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
12
Persistence/Models/Configurations/WitsInfo.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Persistence.Models.Enumerations;
|
||||||
|
|
||||||
|
namespace Persistence.Models.Configurations;
|
||||||
|
|
||||||
|
public class WitsInfo
|
||||||
on.nemtina
commented
Тут нужны комментарии, без комментариев не очень понятно Тут нужны комментарии, без комментариев не очень понятно
|
|||||||
|
{
|
||||||
|
public int RecordId { get; set; }
|
||||||
|
|
||||||
|
public int ItemId { get; set; }
|
||||||
|
|
||||||
|
public WitsType ValueType { get; set; }
|
||||||
|
}
|
24
Persistence/Models/Enumerations/WitsType.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
namespace Persistence.Models.Enumerations;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WITS Data Type Codes
|
||||||
|
/// </summary>
|
||||||
|
public enum WitsType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Alphanumeric, Rep Code = 65
|
||||||
|
/// </summary>
|
||||||
|
A,
|
||||||
|
/// <summary>
|
||||||
|
/// 32 bit 2's complement signed integer, Rep Code = 73
|
||||||
|
/// </summary>
|
||||||
|
L,
|
||||||
|
/// <summary>
|
||||||
|
/// 16 bit 2's complement signed integer, Rep Code = 79
|
||||||
|
/// </summary>
|
||||||
|
S,
|
||||||
|
/// <summary>
|
||||||
|
/// 32 bit IEEE single precision floating point, Rep Code = 128
|
||||||
|
/// </summary>
|
||||||
|
F,
|
||||||
|
}
|
@ -6,6 +6,14 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Services\Config\WitsConfig.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Services\Config\WitsConfig.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||||
|
@ -22,10 +22,15 @@ public interface IParameterRepository
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получить набор параметров (Wits) для построения графика
|
/// Получить набор параметров (Wits) для построения графика
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="discriminatorId"></param>
|
||||||
/// <param name="dateFrom"></param>
|
/// <param name="dateFrom"></param>
|
||||||
/// <param name="dateTo"></param>
|
/// <param name="dateTo"></param>
|
||||||
|
/// <param name="approxPointsCount"></param>
|
||||||
|
/// <param name="ratio"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<ParameterDto> GetValuesForGraph(DateTimeOffset dateFrom, DateTimeOffset dateTo);
|
Task<IEnumerable<ParameterDto>> GetValuesForGraph(int discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo,
|
||||||
|
int approxPointsCount, int? ratio, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Сохранить набор параметров (Wits)
|
/// Сохранить набор параметров (Wits)
|
||||||
@ -34,5 +39,5 @@ public interface IParameterRepository
|
|||||||
/// <param name="witsIds"></param>
|
/// <param name="witsIds"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> InsertRange(IEnumerable<ParameterDto> dtos, CancellationToken token);
|
Task<int> AddRange(IEnumerable<ParameterDto> dtos, CancellationToken token);
|
||||||
}
|
}
|
||||||
|
12866
Persistence/Services/Config/WitsConfig.json
Normal file
@ -1,10 +1,46 @@
|
|||||||
using Persistence.Models;
|
using Persistence.Models;
|
||||||
|
|
||||||
namespace Persistence.Services.Interfaces;
|
namespace Persistence.Services.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Сервис для работы с параметрами Wits
|
||||||
|
/// </summary>
|
||||||
public interface IWitsDataService
|
public interface IWitsDataService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Получить набор параметров для построения графика
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
Task<DatesRangeDto> GetDatesRangeAsync(int idDiscriminator, CancellationToken token);
|
Task<DatesRangeDto> GetDatesRangeAsync(int idDiscriminator, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получить порцию записей, начиная с заданной даты
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idDiscriminator"></param>
|
||||||
|
/// <param name="dateBegin"></param>
|
||||||
|
/// <param name="take"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
Task<IEnumerable<WitsDataDto>> GetPart(int idDiscriminator, DateTimeOffset dateBegin, int take, CancellationToken token);
|
Task<IEnumerable<WitsDataDto>> GetPart(int idDiscriminator, DateTimeOffset dateBegin, int take, CancellationToken token);
|
||||||
Task<WitsDataDto> GetValuesForGraph(DateTimeOffset dateFrom, DateTimeOffset dateTo);
|
|
||||||
Task<int> InsertRange(IEnumerable<WitsDataDto> dtos, CancellationToken token);
|
/// <summary>
|
||||||
|
/// Получить диапазон дат, для которых есть данные в репозитории
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="discriminatorId"></param>
|
||||||
|
/// <param name="dateFrom"></param>
|
||||||
|
/// <param name="dateTo"></param>
|
||||||
|
/// <param name="approxPointsCount"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<IEnumerable<WitsDataDto>> GetValuesForGraph(int discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo, int approxPointsCount, CancellationToken token);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Сохранить набор параметров
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dtos"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<int> AddRange(IEnumerable<WitsDataDto> dtos, CancellationToken token);
|
||||||
}
|
}
|
@ -1,15 +1,28 @@
|
|||||||
using Persistence.Models;
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Persistence.Models;
|
||||||
using Persistence.Repositories;
|
using Persistence.Repositories;
|
||||||
using Persistence.Services.Interfaces;
|
using Persistence.Services.Interfaces;
|
||||||
|
using Persistence.Models.Configurations;
|
||||||
|
using Persistence.Models.Enumerations;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace Persistence.Services;
|
namespace Persistence.Services;
|
||||||
public class WitsDataService : IWitsDataService
|
public class WitsDataService : IWitsDataService
|
||||||
{
|
{
|
||||||
private readonly IParameterRepository witsDataRepository;
|
private readonly IParameterRepository witsDataRepository;
|
||||||
|
|
||||||
|
private readonly WitsInfo[] witsInfo;
|
||||||
|
|
||||||
private const int multiplier = 1000;
|
private const int multiplier = 1000;
|
||||||
public WitsDataService(IParameterRepository witsDataRepository)
|
private const string witsConfigPath = "Persistence.Services.Config.WitsConfig.json";
|
||||||
|
|
||||||
|
public WitsDataService(IParameterRepository witsDataRepository, IConfiguration configuration)
|
||||||
on.nemtina
commented
IConfiguration configuration можно удалить, он нигде не используется IConfiguration configuration можно удалить, он нигде не используется
|
|||||||
{
|
{
|
||||||
this.witsDataRepository = witsDataRepository;
|
this.witsDataRepository = witsDataRepository;
|
||||||
|
|
||||||
|
this.witsInfo = GetWitsInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<DatesRangeDto> GetDatesRangeAsync(int idDiscriminator, CancellationToken token)
|
public Task<DatesRangeDto> GetDatesRangeAsync(int idDiscriminator, CancellationToken token)
|
||||||
@ -23,37 +36,30 @@ public class WitsDataService : IWitsDataService
|
|||||||
{
|
{
|
||||||
var dtos = await witsDataRepository.GetPart(idDiscriminator, dateBegin, take, token);
|
var dtos = await witsDataRepository.GetPart(idDiscriminator, dateBegin, take, token);
|
||||||
|
|
||||||
var result = new List<WitsDataDto>();
|
var result = AdaptToWitsData(dtos);
|
||||||
foreach (var dto in dtos)
|
|
||||||
{
|
|
||||||
var witsDataDto = result.FirstOrDefault(e => e.DiscriminatorId == dto.DiscriminatorId && e.Timestamped == dto.Timestamp);
|
|
||||||
if (witsDataDto == null)
|
|
||||||
{
|
|
||||||
witsDataDto = new WitsDataDto()
|
|
||||||
{
|
|
||||||
DiscriminatorId = dto.DiscriminatorId,
|
|
||||||
Timestamped = dto.Timestamp
|
|
||||||
};
|
|
||||||
result.Add(witsDataDto);
|
|
||||||
}
|
|
||||||
var witsValueDto = new WitsValueDto()
|
|
||||||
{
|
|
||||||
RecordId = DecodeRecordId(dto.ParameterId),
|
|
||||||
ItemId = DecodeItemId(dto.ParameterId),
|
|
||||||
Value = dto.Value
|
|
||||||
};
|
|
||||||
witsDataDto.Values.Append(witsValueDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<WitsDataDto> GetValuesForGraph(DateTimeOffset dateFrom, DateTimeOffset dateTo)
|
public async Task<IEnumerable<WitsDataDto>> GetValuesForGraph(int discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo,
|
||||||
|
int approxPointsCount, CancellationToken token)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var intervalSec = (dateTo - dateFrom).TotalSeconds;
|
||||||
|
|
||||||
|
int? ratio = null;
|
||||||
|
if (intervalSec > 2 * approxPointsCount)
|
||||||
|
{
|
||||||
|
ratio = (int) intervalSec / approxPointsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> InsertRange(IEnumerable<WitsDataDto> dtos, CancellationToken token)
|
var dtos = await witsDataRepository.GetValuesForGraph(discriminatorId, dateFrom, dateTo, approxPointsCount, ratio, token);
|
||||||
|
|
||||||
|
var result = AdaptToWitsData(dtos);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddRange(IEnumerable<WitsDataDto> dtos, CancellationToken token)
|
||||||
{
|
{
|
||||||
var parameterDtos = dtos.SelectMany(e => e.Values.Select(t => new ParameterDto()
|
var parameterDtos = dtos.SelectMany(e => e.Values.Select(t => new ParameterDto()
|
||||||
{
|
{
|
||||||
@ -62,7 +68,7 @@ public class WitsDataService : IWitsDataService
|
|||||||
Value = t.Value.ToString()!,
|
Value = t.Value.ToString()!,
|
||||||
Timestamp = e.Timestamped
|
Timestamp = e.Timestamped
|
||||||
}));
|
}));
|
||||||
var result = await witsDataRepository.InsertRange(parameterDtos, token);
|
var result = await witsDataRepository.AddRange(parameterDtos, token);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -86,4 +92,81 @@ public class WitsDataService : IWitsDataService
|
|||||||
|
|
||||||
return resultId;
|
return resultId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<WitsDataDto> AdaptToWitsData(IEnumerable<ParameterDto> dtos)
|
||||||
|
{
|
||||||
|
var result = new List<WitsDataDto>();
|
||||||
|
foreach (var dto in dtos)
|
||||||
|
{
|
||||||
|
var witsDataDto = result.FirstOrDefault(e => e.DiscriminatorId == dto.DiscriminatorId && e.Timestamped == dto.Timestamp);
|
||||||
on.nemtina
commented
Тут тоже лучше разделить на 2 подзапроса: Тут тоже лучше разделить на 2 подзапроса:
result.Where(e => e.DiscriminatorId == dto.DiscriminatorId)
.Where(e => e.Timestamped == dto.Timestamp)
.FirstOrDefault();
|
|||||||
|
if (witsDataDto == null)
|
||||||
|
{
|
||||||
|
witsDataDto = new WitsDataDto()
|
||||||
|
{
|
||||||
|
DiscriminatorId = dto.DiscriminatorId,
|
||||||
|
Timestamped = dto.Timestamp
|
||||||
|
};
|
||||||
|
result.Add(witsDataDto);
|
||||||
|
}
|
||||||
|
var recordId = DecodeRecordId(dto.ParameterId);
|
||||||
|
var itemId = DecodeItemId(dto.ParameterId);
|
||||||
|
var witsValueDto = new WitsValueDto()
|
||||||
|
{
|
||||||
|
RecordId = recordId,
|
||||||
|
ItemId = itemId,
|
||||||
|
Value = ConvertValue(recordId, itemId, dto.Value)
|
||||||
|
};
|
||||||
|
|
||||||
|
witsDataDto.Values.Append(witsValueDto);
|
||||||
on.nemtina
commented
1. Тут внутрь witsDataDto ничего не добавляется потому что Append возвращает новую последовательность, поэтому писать нужно так:
witsDataDto.Values = witsDataDto.Values.Append(witsValueDto)
2. Не совсем понятный код: цикл по dto, потом поиск еще не добавленных элементов внутрь result и Append.
Это лучше бы отрефакторить. Ниже приложила вариант того, как можно читаемость (по моему мнению) улучшить.
3. И еще параметр take. Желательно иметь значение по умолчанию, а то если пользователь нечаянно null отправит, то запрос ничего не выдаст
![image](/attachments/0dc037f6-7cac-40a5-a8cf-2ca8c60acd5c)
|
|||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object ConvertValue(int recordId, int itemId, string value)
|
||||||
|
{
|
||||||
|
var witsType = witsInfo.FirstOrDefault(e => e.ItemId == itemId
|
||||||
|
&& e.RecordId == recordId)?.ValueType;
|
||||||
|
|
||||||
|
switch(witsType)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
case WitsType.S:
|
||||||
|
{
|
||||||
|
var result = Int16.Parse(value);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case WitsType.L:
|
||||||
|
{
|
||||||
|
var result = Int32.Parse(value);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case WitsType.F:
|
||||||
|
{
|
||||||
|
var result = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WitsInfo[] GetWitsInfo()
|
||||||
|
{
|
||||||
|
var stream = System.Reflection.Assembly.GetExecutingAssembly()
|
||||||
|
.GetManifestResourceStream(witsConfigPath);
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
Converters =
|
||||||
|
{
|
||||||
|
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var records = JsonSerializer.Deserialize<WitsInfo[]>(stream!, options) ?? [];
|
||||||
|
return records;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
discriminatorId лучше добавить в роут