Merge branch 'WitsData' into ClientFactory

This commit is contained in:
Roman Efremov 2024-12-11 11:24:31 +05:00
commit b471a44d5f
37 changed files with 14175 additions and 104 deletions

View File

@ -22,7 +22,7 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[HttpPost("{idDiscriminator}")]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
public async Task<IActionResult> Add(
[FromRoute] Guid idDiscriminator,
[FromRoute] int idDiscriminator,
[FromBody] DataWithWellDepthAndSectionDto dto,
CancellationToken token)
{
@ -35,7 +35,7 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[HttpPost("range/{idDiscriminator}")]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
public async Task<IActionResult> AddRange(
[FromRoute] Guid idDiscriminator,
[FromRoute] int idDiscriminator,
[FromBody] IEnumerable<DataWithWellDepthAndSectionDto> dtos,
CancellationToken token)
{
@ -68,7 +68,7 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[HttpPost("replace/{idDiscriminator}")]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
public async Task<IActionResult> ClearAndAddRange(
[FromRoute] Guid idDiscriminator,
[FromRoute] int idDiscriminator,
[FromBody] IEnumerable<DataWithWellDepthAndSectionDto> dtos,
CancellationToken token)
{
@ -104,7 +104,7 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[HttpGet("{idDiscriminator}")]
[ProducesResponseType(typeof(PaginationContainer<DataWithWellDepthAndSectionDto>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetCurrent(
[FromRoute] Guid idDiscriminator,
[FromRoute] int idDiscriminator,
[FromQuery] SectionPartRequest filterRequest,
[FromQuery] PaginationRequest paginationRequest,
CancellationToken token)
@ -118,7 +118,7 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[HttpGet("moment/{idDiscriminator}")]
[ProducesResponseType(typeof(PaginationContainer<DataWithWellDepthAndSectionDto>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetByDate(
[FromRoute] Guid idDiscriminator,
[FromRoute] int idDiscriminator,
DateTimeOffset moment,
[FromQuery] SectionPartRequest filterRequest,
[FromQuery] PaginationRequest paginationRequest,
@ -133,7 +133,7 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[ProducesResponseType(typeof(IEnumerable<ChangeLogDto>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
public async Task<IActionResult> GetChangeLogForDate(
[FromRoute] Guid idDiscriminator,
[FromRoute] int idDiscriminator,
DateTimeOffset dateBegin,
DateTimeOffset dateEnd,
CancellationToken token)
@ -146,7 +146,7 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[HttpGet("datesChange/{idDiscriminator}")]
[ProducesResponseType(typeof(IEnumerable<DateOnly>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
public async Task<IActionResult> GetDatesChange([FromRoute] Guid idDiscriminator, CancellationToken token)
public async Task<IActionResult> GetDatesChange([FromRoute] int idDiscriminator, CancellationToken token)
{
var result = await repository.GetDatesChange(idDiscriminator, token);
@ -156,7 +156,7 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[HttpGet("part/{idDiscriminator}")]
[ProducesResponseType(typeof(IEnumerable<DataWithWellDepthAndSectionDto>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
public async Task<IActionResult> GetPart([FromRoute] Guid idDiscriminator, DateTimeOffset dateBegin, int take = 86400, CancellationToken token = default)
public async Task<ActionResult<IEnumerable<DataWithWellDepthAndSectionDto>>> GetPart([FromRoute] int idDiscriminator, DateTimeOffset dateBegin, int take = 86400, CancellationToken token = default)
{
var result = await repository.GetGtDate(idDiscriminator, dateBegin, token);
@ -166,7 +166,7 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
[HttpGet("datesRange/{idDiscriminator}")]
[ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
public async Task<IActionResult> GetDatesRangeAsync([FromRoute] Guid idDiscriminator, CancellationToken token)
public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync([FromRoute] int idDiscriminator, CancellationToken token)
{
var result = await repository.GetDatesRange(idDiscriminator, token);

View File

@ -0,0 +1,86 @@
using System.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Persistence.Models;
using Persistence.Services.Interfaces;
namespace Persistence.API.Controllers;
/// <summary>
/// Работа с параметрами Wits
/// </summary>
[ApiController]
[Authorize]
[Route("api/[controller]")]
public class WitsDataController : ControllerBase, IWitsDataApi
{
private readonly IWitsDataService witsDataService;
public WitsDataController(IWitsDataService witsDataService)
{
this.witsDataService = witsDataService;
}
/// <summary>
/// Получить диапазон дат, для которых есть данные в репозитории
/// </summary>
/// <param name="discriminatorId"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("{discriminatorId}/datesRange")]
public async Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync([FromRoute] int discriminatorId, CancellationToken token)
{
var result = await witsDataService.GetDatesRangeAsync(discriminatorId, token);
return result == null ? NoContent() : Ok(result);
}
/// <summary>
/// Получить порцию записей, начиная с заданной даты
/// </summary>
/// <param name="discriminatorId"></param>
/// <param name="dateBegin"></param>
/// <param name="take"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("{discriminatorId}/part")]
public async Task<ActionResult<IEnumerable<WitsDataDto>>> GetPart([FromRoute] int discriminatorId, [FromQuery] DateTimeOffset dateBegin, [FromQuery] int take, CancellationToken token)
{
var result = await witsDataService.GetPart(discriminatorId, dateBegin, take, token);
return Ok(result);
}
/// <summary>
/// Получить набор параметров (Wits) для построения графика
/// </summary>
/// <param name="discriminatorId">Дискриминатор системы</param>
/// <param name="dateFrom">Начало временного интервала</param>
/// <param name="dateTo">Конец временного интервала</param>
/// <param name="approxPointsCount">Количество точек</param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet("{discriminatorId}/graph")]
public async Task<ActionResult<IEnumerable<WitsDataDto>>> GetValuesForGraph([FromRoute] int discriminatorId,
[FromQuery] DateTimeOffset dateFrom, [FromQuery] DateTimeOffset dateTo, [FromQuery] int approxPointsCount, CancellationToken token)
{
var result = await witsDataService.GetValuesForGraph(discriminatorId, dateFrom, dateTo, approxPointsCount, token);
return Ok(result);
}
/// <summary>
/// Сохранить набор параметров (Wits)
/// </summary>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpPost]
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
public async Task<IActionResult> AddRange([FromBody] IEnumerable<WitsDataDto> dtos, CancellationToken token)
{
var result = await witsDataService.AddRange(dtos, token);
return CreatedAtAction(nameof(AddRange), result);
}
}

View File

@ -6,6 +6,8 @@ using Microsoft.OpenApi.Models;
using Persistence.Database.Entity;
using Persistence.Models;
using Persistence.Models.Configurations;
using Persistence.Services;
using Persistence.Services.Interfaces;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using System.Text.Json.Nodes;
@ -55,8 +57,13 @@ public static class DependencyInjection
});
}
#region Authentication
public static void AddJWTAuthentication(this IServiceCollection services, IConfiguration configuration)
public static void AddServices(this IServiceCollection services)
{
services.AddTransient<IWitsDataService, WitsDataService>();
}
#region Authentication
public static void AddJWTAuthentication(this IServiceCollection services, IConfiguration configuration)
{
var needUseKeyCloak = configuration
.GetSection("NeedUseKeyCloak")

View File

@ -25,13 +25,13 @@ public class Startup
services.AddAuthorization();
services.AddJWTAuthentication(Configuration);
services.AddMemoryCache();
services.AddServices();
DependencyInjection.MapsterSetup();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI();

View File

@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Mvc;
using Persistence.Models;
using Refit;
namespace Persistence.Client.Clients;
public interface IWitsDataClient
{
private const string BaseRoute = "/api/witsData";
[Get($"{BaseRoute}/{{discriminatorId}}/graph")]
Task<IApiResponse<IEnumerable<WitsDataDto>>> GetValuesForGraph(int discriminatorId, [Query] DateTimeOffset dateFrom, [Query] DateTimeOffset dateTo, [Query] int approxPointsCount, CancellationToken token);
[Post($"{BaseRoute}/")]
Task<IApiResponse<int>> AddRange(IEnumerable<WitsDataDto> dtos, CancellationToken token);
[Get($"{BaseRoute}/{{discriminatorId}}/part")]
Task<IApiResponse<IEnumerable<WitsDataDto>>> GetPart(int discriminatorId, [Query] DateTimeOffset dateBegin, [Query] int take = 24 * 60 * 60, CancellationToken token = default);
[Get($"{BaseRoute}/{{discriminatorId}}/datesRange")]
Task<IApiResponse<DatesRangeDto>> GetDatesRangeAsync(int discriminatorId, CancellationToken token);
}

View File

@ -0,0 +1,257 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Persistence.Database.Model;
#nullable disable
namespace Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistenceDbContext))]
[Migration("20241203120141_ParameterDataMigration")]
partial class ParameterDataMigration
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.UseCollation("Russian_Russia.1251")
.HasAnnotation("ProductVersion", "8.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "adminpack");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Persistence.Database.Entity.DrillingSystem", b =>
{
b.Property<Guid>("SystemId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id системы автобурения");
b.Property<string>("Description")
.HasColumnType("text")
.HasComment("Описание системы автобурения");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("varchar(256)")
.HasComment("Наименование системы автобурения");
b.HasKey("SystemId");
b.ToTable("DrillingSystem");
});
modelBuilder.Entity("Persistence.Database.Entity.ParameterData", b =>
{
b.Property<int>("DiscriminatorId")
.HasColumnType("integer")
.HasComment("Дискриминатор системы");
b.Property<int>("ParameterId")
.HasColumnType("integer")
.HasComment("Id параметра");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Временная отметка");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("varchar(256)")
.HasComment("Значение параметра в виде строки");
b.HasKey("DiscriminatorId", "ParameterId", "Timestamp");
b.ToTable("ParameterData");
});
modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id события");
b.Property<int>("CategoryId")
.HasColumnType("integer")
.HasComment("Id Категории важности");
b.Property<double?>("Depth")
.HasColumnType("double precision")
.HasComment("Глубина забоя");
b.Property<string>("MessageText")
.IsRequired()
.HasColumnType("varchar(512)")
.HasComment("Текст сообщения");
b.Property<Guid>("SystemId")
.HasColumnType("uuid")
.HasComment("Id системы автобурения, к которой относится сообщение");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Дата возникновения");
b.Property<Guid>("UserId")
.HasColumnType("uuid")
.HasComment("Id пользователя за пультом бурильщика");
b.HasKey("EventId");
b.HasIndex("SystemId");
b.ToTable("TechMessage");
});
modelBuilder.Entity("Persistence.Database.Entity.TimestampedSet", b =>
{
b.Property<Guid>("IdDiscriminator")
.HasColumnType("uuid")
.HasComment("Дискриминатор ссылка на тип сохраняемых данных");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Отметка времени, строго в UTC");
b.Property<string>("Set")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Набор сохраняемых данных");
b.HasKey("IdDiscriminator", "Timestamp");
b.ToTable("TimestampedSets", t =>
{
t.HasComment("Общая таблица данных временных рядов");
});
});
modelBuilder.Entity("Persistence.Database.Model.DataSaub", b =>
{
b.Property<DateTimeOffset>("Date")
.HasColumnType("timestamp with time zone")
.HasColumnName("date");
b.Property<double?>("AxialLoad")
.HasColumnType("double precision")
.HasColumnName("axialLoad");
b.Property<double?>("BitDepth")
.HasColumnType("double precision")
.HasColumnName("bitDepth");
b.Property<double?>("BlockPosition")
.HasColumnType("double precision")
.HasColumnName("blockPosition");
b.Property<double?>("BlockSpeed")
.HasColumnType("double precision")
.HasColumnName("blockSpeed");
b.Property<double?>("Flow")
.HasColumnType("double precision")
.HasColumnName("flow");
b.Property<double?>("HookWeight")
.HasColumnType("double precision")
.HasColumnName("hookWeight");
b.Property<int>("IdFeedRegulator")
.HasColumnType("integer")
.HasColumnName("idFeedRegulator");
b.Property<int?>("Mode")
.HasColumnType("integer")
.HasColumnName("mode");
b.Property<double?>("Mse")
.HasColumnType("double precision")
.HasColumnName("mse");
b.Property<short>("MseState")
.HasColumnType("smallint")
.HasColumnName("mseState");
b.Property<double?>("Pressure")
.HasColumnType("double precision")
.HasColumnName("pressure");
b.Property<double?>("Pump0Flow")
.HasColumnType("double precision")
.HasColumnName("pump0Flow");
b.Property<double?>("Pump1Flow")
.HasColumnType("double precision")
.HasColumnName("pump1Flow");
b.Property<double?>("Pump2Flow")
.HasColumnType("double precision")
.HasColumnName("pump2Flow");
b.Property<double?>("RotorSpeed")
.HasColumnType("double precision")
.HasColumnName("rotorSpeed");
b.Property<double?>("RotorTorque")
.HasColumnType("double precision")
.HasColumnName("rotorTorque");
b.Property<string>("User")
.HasColumnType("text")
.HasColumnName("user");
b.Property<double?>("WellDepth")
.HasColumnType("double precision")
.HasColumnName("wellDepth");
b.HasKey("Date");
b.ToTable("DataSaub");
});
modelBuilder.Entity("Persistence.Database.Model.Setpoint", b =>
{
b.Property<Guid>("Key")
.HasColumnType("uuid")
.HasComment("Ключ");
b.Property<DateTimeOffset>("Created")
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания уставки");
b.Property<Guid>("IdUser")
.HasColumnType("uuid")
.HasComment("Id автора последнего изменения");
b.Property<object>("Value")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Значение уставки");
b.HasKey("Key", "Created");
b.ToTable("Setpoint");
});
modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b =>
{
b.HasOne("Persistence.Database.Entity.DrillingSystem", "System")
.WithMany()
.HasForeignKey("SystemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("System");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Persistence.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class ParameterDataMigration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ParameterData",
columns: table => new
{
DiscriminatorId = table.Column<int>(type: "integer", nullable: false, comment: "Дискриминатор системы"),
ParameterId = table.Column<int>(type: "integer", nullable: false, comment: "Id параметра"),
Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"),
Value = table.Column<string>(type: "varchar(256)", nullable: false, comment: "Значение параметра в виде строки")
},
constraints: table =>
{
table.PrimaryKey("PK_ParameterData", x => new { x.DiscriminatorId, x.ParameterId, x.Timestamp });
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ParameterData");
}
}
}

View File

@ -46,6 +46,30 @@ namespace Persistence.Database.Postgres.Migrations
b.ToTable("DrillingSystem");
});
modelBuilder.Entity("Persistence.Database.Entity.ParameterData", b =>
{
b.Property<int>("DiscriminatorId")
.HasColumnType("integer")
.HasComment("Дискриминатор системы");
b.Property<int>("ParameterId")
.HasColumnType("integer")
.HasComment("Id параметра");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Временная отметка");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("varchar(256)")
.HasComment("Значение параметра в виде строки");
b.HasKey("DiscriminatorId", "ParameterId", "Timestamp");
b.ToTable("ParameterData");
});
modelBuilder.Entity("Persistence.Database.Entity.TechMessage", b =>
{
b.Property<Guid>("EventId")

View File

@ -15,7 +15,7 @@ public class ChangeLog : IChangeLog, IWithSectionPart
public Guid Id { get; set; }
[Comment("Дискриминатор таблицы")]
public Guid IdDiscriminator { get; set; }
public int IdDiscriminator { get; set; }
[Comment("Автор изменения")]
public Guid IdAuthor { get; set; }

View File

@ -39,7 +39,7 @@ public interface IChangeLog
/// <summary>
/// Дискриминатор таблицы
/// </summary>
public Guid IdDiscriminator { get; set; }
public int IdDiscriminator { get; set; }
/// <summary>
/// Значение

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Persistence.Database.Entity;
[PrimaryKey(nameof(DiscriminatorId), nameof(ParameterId), nameof(Timestamp))]
public class ParameterData
{
[Required, Comment("Дискриминатор системы")]
public int DiscriminatorId { get; set; }
[Comment("Id параметра")]
public int ParameterId { get; set; }
[Column(TypeName = "varchar(256)"), Comment("Значение параметра в виде строки")]
public required string Value { get; set; }
[Comment("Временная отметка")]
public DateTimeOffset Timestamp { get; set; }
}

View File

@ -17,7 +17,11 @@ public class PersistenceDbContext : DbContext
public DbSet<ChangeLog> ChangeLog => Set<ChangeLog>();
public PersistenceDbContext(DbContextOptions options)
public DbSet<TechMessage> TechMessage => Set<TechMessage>();
public DbSet<ParameterData> ParameterData => Set<ParameterData>();
public PersistenceDbContext(DbContextOptions options)
: base(options)
{

View File

@ -1,8 +1,9 @@
using Mapster;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Persistence.Client;
using Persistence.Client.Clients.Interfaces;
using Persistence.Database.Entity;
using Persistence.Database.Model;
using Persistence.Models;
using Persistence.Models.Requests;
@ -25,8 +26,10 @@ public class ChangeLogControllerTest : BaseIntegrationTest
[Fact]
public async Task ClearAndInsertRange_InEmptyDb()
{
// arrange
var idDiscriminator = Guid.NewGuid();
// arrange
dbContext.CleanupDbSet<ChangeLog>();
var idDiscriminator = 1;
var dtos = Generate(2, DateTimeOffset.UtcNow);
// act
@ -57,7 +60,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
{
// arrange
var count = 1;
var idDiscriminator = Guid.NewGuid();
var idDiscriminator = 1;
var dtos = Generate(count, DateTimeOffset.UtcNow);
var dto = dtos.FirstOrDefault()!;
@ -73,7 +76,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest
{
// arrange
var count = 3;
var idDiscriminator = Guid.NewGuid();
var idDiscriminator = 1;
var dtos = Generate(count, DateTimeOffset.UtcNow);
// act
@ -86,8 +89,10 @@ public class ChangeLogControllerTest : BaseIntegrationTest
[Fact]
public async Task Update_returns_success()
{
// arrange
var idDiscriminator = Guid.NewGuid();
// arrange
dbContext.CleanupDbSet<ChangeLog>();
var idDiscriminator = 1;
var dtos = Generate(1, DateTimeOffset.UtcNow);
var dto = dtos.FirstOrDefault()!;
var result = await client.Add(idDiscriminator, dto, new CancellationToken());
@ -218,9 +223,11 @@ public class ChangeLogControllerTest : BaseIntegrationTest
[Fact]
public async Task GetByDate_returns_success()
{
// arrange
//создаем записи
var count = 5;
// arrange
dbContext.CleanupDbSet<ChangeLog>();
//создаем записи
var count = 5;
var changeLogItems = CreateChangeLogItems(count, (-15, 15));
var idDiscriminator = changeLogItems.Item1;
var entities = changeLogItems.Item2;
@ -269,12 +276,14 @@ public class ChangeLogControllerTest : BaseIntegrationTest
int daysAfterNowFilter,
int changeLogCount)
{
// arrange
//создаем записи
var count = insertedCount;
// arrange
dbContext.CleanupDbSet<ChangeLog>();
//создаем записи
var count = insertedCount;
var daysRange = (daysBeforeNowChangeLog, daysAfterNowChangeLog);
var changeLogItems = CreateChangeLogItems(count, daysRange);
var idDiscriminator = changeLogItems.Item1;
var idDiscriminator = 1;
var entities = changeLogItems.Item2;
foreach (var entity in entities)
@ -312,12 +321,12 @@ public class ChangeLogControllerTest : BaseIntegrationTest
}
private (Guid, ChangeLog[]) CreateChangeLogItems(int count, (int, int) daysRange)
private (int, ChangeLog[]) CreateChangeLogItems(int count, (int, int) daysRange)
{
var minDayCount = daysRange.Item1;
var maxDayCount = daysRange.Item2;
Guid idDiscriminator = Guid.NewGuid();
var idDiscriminator = 1;
var dtos = Generate(count, DateTimeOffset.UtcNow);
var entities = dtos.Select(d =>
{

View File

@ -132,7 +132,7 @@ namespace Persistence.IntegrationTests.Controllers
dbContext.CleanupDbSet<Setpoint>();
//act
var response = await setpointClient.GetDatesRangeAsync(new CancellationToken());
var response = await setpointClient.GetDatesRangeAsync(CancellationToken.None);
//assert
Assert.NotNull(response);
@ -150,10 +150,10 @@ namespace Persistence.IntegrationTests.Controllers
var dateBegin = DateTimeOffset.MinValue;
var take = 1;
var part = await setpointClient.GetPart(dateBegin, take, new CancellationToken());
var part = await setpointClient.GetPart(dateBegin, take, CancellationToken.None);
//act
var response = await setpointClient.GetDatesRangeAsync(new CancellationToken());
var response = await setpointClient.GetDatesRangeAsync(CancellationToken.None);
//assert
Assert.NotNull(response);
@ -178,7 +178,7 @@ namespace Persistence.IntegrationTests.Controllers
var take = 2;
//act
var response = await setpointClient.GetPart(dateBegin, take, new CancellationToken());
var response = await setpointClient.GetPart(dateBegin, take, CancellationToken.None);
//assert
Assert.NotNull(response);
@ -194,7 +194,7 @@ namespace Persistence.IntegrationTests.Controllers
await Add();
//act
var response = await setpointClient.GetPart(dateBegin, take, new CancellationToken());
var response = await setpointClient.GetPart(dateBegin, take, CancellationToken.None);
//assert
Assert.NotNull(response);

View File

@ -40,7 +40,7 @@ namespace Persistence.IntegrationTests.Controllers
};
//act
var response = await techMessagesClient.GetPage(requestDto, new CancellationToken());
var response = await techMessagesClient.GetPage(requestDto, CancellationToken.None);
//assert
Assert.NotNull(response);
@ -55,7 +55,7 @@ namespace Persistence.IntegrationTests.Controllers
//arrange
var dtos = await InsertRange();
var dtosCount = dtos.Count();
var PaginationRequest = new PaginationRequest()
var requestDto = new PaginationRequest()
{
Skip = 0,
Take = 2,
@ -63,7 +63,7 @@ namespace Persistence.IntegrationTests.Controllers
};
//act
var response = await techMessagesClient.GetPage(PaginationRequest, new CancellationToken());
var response = await techMessagesClient.GetPage(requestDto, CancellationToken.None);
//assert
Assert.NotNull(response);
@ -95,16 +95,11 @@ namespace Persistence.IntegrationTests.Controllers
}
};
try
{
//act
var response = await techMessagesClient.AddRange(dtos, new CancellationToken());
}
catch (Exception ex)
{
//assert
Assert.Equal(exceptionMessage, ex.Message);
}
//act
var response = await techMessagesClient.AddRange(dtos, new CancellationToken());
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
@ -116,7 +111,7 @@ namespace Persistence.IntegrationTests.Controllers
dbContext.CleanupDbSet<Database.Entity.DrillingSystem>();
//act
var response = await techMessagesClient.GetSystems(new CancellationToken());
var response = await techMessagesClient.GetSystems(CancellationToken.None);
//assert
Assert.NotNull(response);
@ -134,7 +129,7 @@ namespace Persistence.IntegrationTests.Controllers
.ToArray();
//act
var response = await techMessagesClient.GetSystems(new CancellationToken());
var response = await techMessagesClient.GetSystems(CancellationToken.None);
//assert
Assert.NotNull(response);
@ -154,7 +149,7 @@ namespace Persistence.IntegrationTests.Controllers
var autoDrillingSystem = nameof(TechMessageDto.System);
//act
var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken());
var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, CancellationToken.None);
//assert
Assert.NotNull(response);
@ -171,7 +166,7 @@ namespace Persistence.IntegrationTests.Controllers
var filteredDtos = dtos.Where(e => e.CategoryId == imortantId && e.System == autoDrillingSystem);
//act
var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, new CancellationToken());
var response = await techMessagesClient.GetStatistics(autoDrillingSystem, imortantId, CancellationToken.None);
//assert
Assert.NotNull(response);
@ -190,7 +185,7 @@ namespace Persistence.IntegrationTests.Controllers
dbContext.CleanupDbSet<DrillingSystem>();
//act
var response = await techMessagesClient.GetDatesRangeAsync(new CancellationToken());
var response = await techMessagesClient.GetDatesRangeAsync(CancellationToken.None);
//assert
Assert.NotNull(response);
@ -205,7 +200,7 @@ namespace Persistence.IntegrationTests.Controllers
await InsertRange();
//act
var response = await techMessagesClient.GetDatesRangeAsync(new CancellationToken());
var response = await techMessagesClient.GetDatesRangeAsync(CancellationToken.None);
//assert
Assert.NotNull(response);
@ -221,7 +216,7 @@ namespace Persistence.IntegrationTests.Controllers
var take = 2;
//act
var response = await techMessagesClient.GetPart(dateBegin, take, new CancellationToken());
var response = await techMessagesClient.GetPart(dateBegin, take, CancellationToken.None);
//assert
Assert.NotNull(response);
@ -237,7 +232,7 @@ namespace Persistence.IntegrationTests.Controllers
await InsertRange();
//act
var response = await techMessagesClient.GetPart(dateBegin, take, new CancellationToken());
var response = await techMessagesClient.GetPart(dateBegin, take, CancellationToken.None);
//assert
Assert.NotNull(response);
@ -277,7 +272,7 @@ namespace Persistence.IntegrationTests.Controllers
//act
var response = await techMessagesClient.AddRange(dtos, new CancellationToken());
var response = await techMessagesClient.AddRange(dtos, CancellationToken.None);
//assert
Assert.Equal(dtos.Count, response);

View File

@ -0,0 +1,234 @@
using System.Net;
using Microsoft.Extensions.DependencyInjection;
using Persistence.Client;
using Persistence.Client.Clients;
using Persistence.Database.Entity;
using Persistence.Models;
using Xunit;
namespace Persistence.IntegrationTests.Controllers;
public class WitsDataControllerTest : BaseIntegrationTest
{
private IWitsDataClient witsDataClient;
public WitsDataControllerTest(WebAppFactoryFixture factory) : base(factory)
{
var scope = factory.Services.CreateScope();
var persistenceClientFactory = scope.ServiceProvider
.GetRequiredService<PersistenceClientFactory>();
witsDataClient = persistenceClientFactory.GetClient<IWitsDataClient>();
}
[Fact]
public async Task GetDatesRangeAsync_returns_success()
{
//arrange
dbContext.CleanupDbSet<ParameterData>();
var discriminatorId = 1;
//act
var response = await witsDataClient.GetDatesRangeAsync(discriminatorId, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
}
[Fact]
public async Task GetPart_returns_success()
{
//arrange
dbContext.CleanupDbSet<ParameterData>();
var discriminatorId = 1;
var dateBegin = DateTimeOffset.UtcNow;
var take = 1;
//act
var response = await witsDataClient.GetPart(discriminatorId, dateBegin, take, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.Empty(response.Content);
}
[Fact]
public async Task InsertRange_returns_success()
{
//arrange
dbContext.CleanupDbSet<ParameterData>();
//act
await AddRange();
}
[Fact]
public async Task GetValuesForGraph_returns_success()
{
//arrange
dbContext.CleanupDbSet<ParameterData>();
var discriminatorId = 1;
var dateFrom = DateTimeOffset.UtcNow;
var dateTo = DateTimeOffset.UtcNow;
var approxPointCount = 12;
//act
var response = await witsDataClient.GetValuesForGraph(discriminatorId, dateFrom, dateTo, approxPointCount, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.Empty(response.Content);
}
[Fact]
public async Task GetDatesRangeAsync_AfterSave_returns_success()
{
//arrange
dbContext.CleanupDbSet<ParameterData>();
var dtos = await AddRange();
var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;
//act
var response = await witsDataClient.GetDatesRangeAsync(discriminatorId, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
var expectedDateFrom = dtos
.Select(e => e.Timestamped)
.Min()
.ToString("dd.MM.yyyy-HH:mm:ss");
var actualDateFrom = response.Content.From.DateTime
.ToString("dd.MM.yyyy-HH:mm:ss");
Assert.Equal(expectedDateFrom, actualDateFrom);
var expectedDateTo = dtos
.Select(e => e.Timestamped)
.Max()
.ToString("dd.MM.yyyy-HH:mm:ss");
var actualDateTo = response.Content.To.DateTime
.ToString("dd.MM.yyyy-HH:mm:ss");
Assert.Equal(expectedDateTo, actualDateTo);
}
[Fact]
public async Task GetPart_AfterSave_returns_success()
{
//arrange
dbContext.CleanupDbSet<ParameterData>();
var dtos = await AddRange();
var discriminatorId = dtos.FirstOrDefault()!.DiscriminatorId;
var dateBegin = dtos.FirstOrDefault()!.Timestamped;
var take = 1;
//act
var response = await witsDataClient.GetPart(discriminatorId, dateBegin, take, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.NotEmpty(response.Content);
Assert.Equal(take, response.Content.Count());
var expectedDto = dtos.FirstOrDefault();
var actualDto = response.Content.FirstOrDefault();
Assert.Equal(expectedDto?.DiscriminatorId, actualDto?.DiscriminatorId);
var expectedValueDto = expectedDto?.Values.FirstOrDefault();
var actualValueDto = actualDto?.Values.FirstOrDefault();
Assert.Equal(expectedValueDto?.ItemId, actualValueDto?.ItemId);
Assert.Equal(expectedValueDto?.RecordId, actualValueDto?.RecordId);
}
[Fact]
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, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(response.Content);
Assert.Equal(approxPointCount, response.Content.Count());
}
[Fact]
public async Task AddRange_returns_BadRequest()
{
//arrange
var dtos = new List<WitsDataDto>()
{
new WitsDataDto()
{
DiscriminatorId = -1, // < 0
Timestamped = DateTimeOffset.UtcNow,
Values = new List<WitsValueDto>()
{
new WitsValueDto()
{
RecordId = -1, // < 0
ItemId = 101, // > 100
Value = string.Empty
}
}
}
};
//act
var response = await witsDataClient.AddRange(dtos, CancellationToken.None);
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
private async Task<IEnumerable<WitsDataDto>> AddRange(int countToCreate = 10)
{
var dtos = new List<WitsDataDto>();
var timestamped = DateTimeOffset.UtcNow;
for (var i = 0; i < countToCreate; i++)
{
var random = new Random();
dtos.Add(new WitsDataDto()
{
DiscriminatorId = 1,
Timestamped = timestamped.AddSeconds(i),
Values = new List<WitsValueDto>()
{
new WitsValueDto()
{
RecordId = i + 1,
ItemId = i + 1,
Value = random.Next(1, 100)
}
}
});
}
//act
var response = await witsDataClient.AddRange(dtos, CancellationToken.None);
//assert
var count = dtos.SelectMany(e => e.Values).Count();
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
Assert.Equal(count, response.Content);
return dtos;
}
}

View File

@ -34,6 +34,7 @@ public static class DependencyInjection
services.AddTransient<IChangeLogRepository, ChangeLogRepository>();
services.AddTransient<ITimestampedSetRepository, TimestampedSetRepository>();
services.AddTransient<ITechMessagesRepository, TechMessagesRepository>();
services.AddTransient<IParameterRepository, ParameterRepository>();
return services;
}

View File

@ -16,7 +16,7 @@ public class ChangeLogRepository : IChangeLogRepository
this.db = db;
}
public async Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token)
public async Task<int> AddRange(Guid idAuthor, int idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token)
{
var entities = new List<ChangeLog>();
foreach (var dto in dtos)
@ -49,7 +49,7 @@ public class ChangeLogRepository : IChangeLogRepository
return result;
}
public async Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token)
public async Task<int> MarkAsDeleted(Guid idEditor, int idDiscriminator, CancellationToken token)
{
var query = db.Set<ChangeLog>()
.Where(s => s.IdDiscriminator == idDiscriminator)
@ -75,7 +75,7 @@ public class ChangeLogRepository : IChangeLogRepository
return await db.SaveChangesAsync(token);
}
public async Task<int> ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token)
public async Task<int> ClearAndAddRange(Guid idAuthor, int idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token)
{
var result = 0;
@ -127,7 +127,7 @@ public class ChangeLogRepository : IChangeLogRepository
}
public async Task<PaginationContainer<DataWithWellDepthAndSectionDto>> GetByDate(
Guid idDiscriminator,
int idDiscriminator,
DateTimeOffset momentUtc,
SectionPartRequest filterRequest,
PaginationRequest paginationRequest,
@ -142,14 +142,14 @@ public class ChangeLogRepository : IChangeLogRepository
return result;
}
private IQueryable<ChangeLog> CreateQuery(Guid idDiscriminator)
private IQueryable<ChangeLog> CreateQuery(int idDiscriminator)
{
var query = db.Set<ChangeLog>().Where(e => e.IdDiscriminator == idDiscriminator);
return query;
}
public async Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
public async Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(int idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
{
var query = db.Set<ChangeLog>().Where(s => s.IdDiscriminator == idDiscriminator);
@ -169,7 +169,7 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<IEnumerable<DateOnly>> GetDatesChange(Guid idDiscriminator, CancellationToken token)
public async Task<IEnumerable<DateOnly>> GetDatesChange(int idDiscriminator, CancellationToken token)
{
var query = db.Set<ChangeLog>().Where(e => e.IdDiscriminator == idDiscriminator);
@ -195,7 +195,7 @@ public class ChangeLogRepository : IChangeLogRepository
return datesOnly;
}
private ChangeLog CreateEntityFromDto(Guid idAuthor, Guid idDiscriminator, DataWithWellDepthAndSectionDto dto)
private ChangeLog CreateEntityFromDto(Guid idAuthor, int idDiscriminator, DataWithWellDepthAndSectionDto dto)
{
var entity = new ChangeLog()
{
@ -214,7 +214,7 @@ public class ChangeLogRepository : IChangeLogRepository
return entity;
}
public async Task<IEnumerable<DataWithWellDepthAndSectionDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
public async Task<IEnumerable<DataWithWellDepthAndSectionDto>> GetGtDate(int idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
{
var date = dateBegin.ToUniversalTime();
var query = this.db.Set<ChangeLog>()
@ -228,7 +228,7 @@ public class ChangeLogRepository : IChangeLogRepository
return dtos;
}
public async Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token)
public async Task<DatesRangeDto?> GetDatesRange(int idDiscriminator, CancellationToken token)
{
var query = db.Set<ChangeLog>()
.Where(e => e.IdDiscriminator == idDiscriminator)

View File

@ -0,0 +1,87 @@
using Mapster;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using Persistence.Database.Entity;
using Persistence.Models;
using Persistence.Repositories;
namespace Persistence.Repository.Repositories;
public class ParameterRepository : IParameterRepository
{
private DbContext db;
public ParameterRepository(DbContext db)
{
this.db = db;
}
protected virtual IQueryable<ParameterData> GetQueryReadOnly() => db.Set<ParameterData>();
public async Task<DatesRangeDto> GetDatesRangeAsync(int idDiscriminator, CancellationToken token)
{
var query = GetQueryReadOnly()
.Where(e => e.DiscriminatorId == idDiscriminator)
.GroupBy(e => 1)
.Select(group => new
{
Min = group.Min(e => e.Timestamp),
Max = group.Max(e => e.Timestamp),
});
var values = await query.FirstOrDefaultAsync(token);
var result = new DatesRangeDto()
{
From = values?.Min ?? DateTimeOffset.MinValue,
To = values?.Max ?? DateTimeOffset.MaxValue
};
return result;
}
public async Task<IEnumerable<ParameterDto>> GetPart(int idDiscriminator, DateTimeOffset dateBegin, int take, CancellationToken token)
{
var query = GetQueryReadOnly();
var universalDate = dateBegin.ToUniversalTime();
var entities = await query
.Where(e => e.DiscriminatorId == idDiscriminator && e.Timestamp >= universalDate)
.Take(take)
.ToArrayAsync(token);
var dtos = entities.Select(e => e.Adapt<ParameterDto>());
return dtos;
}
public async Task<IEnumerable<ParameterDto>> GetValuesForGraph(int discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo,
int approxPointsCount, int? ratio, CancellationToken token)
{
var query = db.Set<ParameterData>().AsNoTracking();
var universalDateFrom = dateFrom.ToUniversalTime();
var universalDateTo = dateTo.ToUniversalTime();
query = query
.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);
}
var entities = await query
.Take((int)(2.5 * approxPointsCount))
.ToArrayAsync(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>());
await db.Set<ParameterData>().AddRangeAsync(entities, token);
var result = await db.SaveChangesAsync(token);
return result;
}
}

View File

@ -16,7 +16,7 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<DataWithWellDepthAndS
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> ClearAndAddRange(Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
Task<IActionResult> ClearAndAddRange(int idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
/// <summary>
/// Получение данных на текущую дату (с пагинацией)
@ -26,7 +26,7 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<DataWithWellDepthAndS
/// <param name="paginationRequest">параметры запроса пагинации</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> GetCurrent(Guid idDiscriminator, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token);
Task<IActionResult> GetCurrent(int idDiscriminator, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token);
/// <summary>
/// Получение данных на определенную дату (с пагинацией)
@ -37,7 +37,7 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<DataWithWellDepthAndS
/// <param name="paginationRequest">параметры запроса пагинации</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> GetByDate(Guid idDiscriminator, DateTimeOffset moment, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token);
Task<IActionResult> GetByDate(int idDiscriminator, DateTimeOffset moment, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token);
/// <summary>
/// Получение исторических данных за определенный период времени
@ -47,7 +47,7 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<DataWithWellDepthAndS
/// <param name="dateEnd"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> GetChangeLogForDate(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
Task<IActionResult> GetChangeLogForDate(int idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
/// <summary>
/// Добавить одну запись
@ -56,7 +56,7 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<DataWithWellDepthAndS
/// <param name="dto"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> Add(Guid idDiscriminator, DataWithWellDepthAndSectionDto dto, CancellationToken token);
Task<IActionResult> Add(int idDiscriminator, DataWithWellDepthAndSectionDto dto, CancellationToken token);
/// <summary>
/// Добавить несколько записей
@ -65,7 +65,7 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<DataWithWellDepthAndS
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> AddRange(Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
Task<IActionResult> AddRange(int idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
/// <summary>
/// Обновить одну запись
@ -105,5 +105,5 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<DataWithWellDepthAndS
/// <param name="idDiscriminator"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> GetDatesChange(Guid idDiscriminator, CancellationToken token);
Task<IActionResult> GetDatesChange(int idDiscriminator, CancellationToken token);
}

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Persistence.Models;
namespace Persistence.API;
@ -8,21 +8,21 @@ namespace Persistence.API;
/// </summary>
public interface ISyncWithDiscriminatorApi<TDto>
{
/// <summary>
/// Получить порцию записей, начиная с заданной даты
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dateBegin"></param>
/// <param name="take">количество записей</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> GetPart(Guid idDiscriminator, DateTimeOffset dateBegin, int take = 24 * 60 * 60, CancellationToken token = default);
/// <summary>
/// Получить порцию записей, начиная с заданной даты
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="dateBegin"></param>
/// <param name="take">количество записей</param>
/// <param name="token"></param>
/// <returns></returns>
Task<ActionResult<IEnumerable<TDto>>> GetPart(int idDiscriminator, DateTimeOffset dateBegin, int take = 24 * 60 * 60, CancellationToken token = default);
/// <summary>
/// Получить диапазон дат, для которых есть данные в репозитории
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> GetDatesRangeAsync(Guid idDiscriminator, CancellationToken token);
}
/// <summary>
/// Получить диапазон дат, для которых есть данные в репозитории
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<ActionResult<DatesRangeDto>> GetDatesRangeAsync(int idDiscriminator, CancellationToken token);
}

View File

@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Mvc;
using Persistence.Models;
namespace Persistence.API;
/// <summary>
/// Интерфейс для работы с параметрами Wits
/// </summary>
public interface IWitsDataApi : ISyncWithDiscriminatorApi<WitsDataDto>
{
/// <summary>
/// Получить набор параметров (Wits) для построения графика
/// </summary>
/// <param name="discriminatorId"></param>
/// <param name="dateFrom"></param>
/// <param name="dateTo"></param>
/// <param name="approxPointsCount"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<ActionResult<IEnumerable<WitsDataDto>>> GetValuesForGraph(int discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo,
int approxPointsCount, CancellationToken token);
/// <summary>
/// Сохранить набор параметров (Wits)
/// </summary>
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IActionResult> AddRange(IEnumerable<WitsDataDto> dtos, CancellationToken token);
}

View File

@ -0,0 +1,15 @@
using Persistence.Models.Enumerations;
namespace Persistence.Models.Configurations;
/// <summary>
/// Протокол Wits
/// </summary>
public class WitsInfo
{
public int RecordId { get; set; }
public int ItemId { get; set; }
public WitsType ValueType { get; set; }
}

View 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,
}

View File

@ -0,0 +1,32 @@
using System.ComponentModel.DataAnnotations;
namespace Persistence.Models;
/// <summary>
/// Модель параметра
/// </summary>
public class ParameterDto
{
/// <summary>
/// Дискриминатор системы
/// </summary>
[Range(0, int.MaxValue, ErrorMessage = "Дискриминатор системы не может быть меньше 0")]
public int DiscriminatorId { get; set; }
/// <summary>
/// Id параметра
/// </summary>
[Range(0, int.MaxValue, ErrorMessage = "Id параметра не может быть меньше 0")]
public int ParameterId { get; set; }
/// <summary>
/// Значение параметра в виде строки
/// </summary>
[StringLength(256, MinimumLength = 1, ErrorMessage = "Допустимая длина значения параметра от 1 до 256 символов")]
public required string Value { get; set; }
/// <summary>
/// Временная отметка
/// </summary>
public DateTimeOffset Timestamp { get; set; }
}

View File

@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
namespace Persistence.Models;
/// <summary>
/// Группа параметров Wits
/// </summary>
public class WitsDataDto
{
/// <summary>
/// Временная отметка
/// </summary>
public required DateTimeOffset Timestamped { get; set; }
/// <summary>
/// Дискриминатор системы
/// </summary>
[Range(0, int.MaxValue, ErrorMessage = "Дискриминатор системы не может быть меньше 0")]
public required int DiscriminatorId { get; set; }
/// <summary>
/// Параметры
/// </summary>
public IEnumerable<WitsValueDto> Values { get; set; } = [];
}

View File

@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations;
namespace Persistence.Models;
/// <summary>
/// Параметр Wits
/// </summary>
public class WitsValueDto
{
/// <summary>
/// Wits - Record Number
/// </summary>
[Range(1, 100, ErrorMessage = "Значение \"Record Number\" обязано находиться в диапозоне от 1 до 100")]
public int RecordId { get; set; }
/// <summary>
/// Wits - Record Item
/// </summary>
[Range(1, 100, ErrorMessage = "Значение \"Wits Record Item\" обязано находиться в диапозоне от 1 до 100")]
public int ItemId { get; set; }
/// <summary>
/// Значение параметра
/// </summary>
public required object Value { get; set; }
}

View File

@ -6,6 +6,14 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="Services\Config\WitsConfig.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Services\Config\WitsConfig.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />

View File

@ -17,7 +17,7 @@ public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<DataWit
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
Task<int> AddRange(Guid idAuthor, int idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
/// <summary>
/// Пометить записи как удаленные
@ -35,7 +35,7 @@ public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<DataWit
/// <param name="idDiscriminator">дискриминатор таблицы</param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token);
Task<int> MarkAsDeleted(Guid idEditor, int idDiscriminator, CancellationToken token);
/// <summary>
/// Очистить и добавить новые
@ -45,7 +45,7 @@ public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<DataWit
/// <param name="dtos"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
Task<int> ClearAndAddRange(Guid idAuthor, int idDiscriminator, IEnumerable<DataWithWellDepthAndSectionDto> dtos, CancellationToken token);
/// <summary>
/// Редактирование записей
@ -65,7 +65,7 @@ public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<DataWit
/// <param name="paginationRequest">параметры запроса пагинации</param>
/// <param name="token"></param>
/// <returns></returns>
Task<PaginationContainer<DataWithWellDepthAndSectionDto>> GetByDate(Guid idDiscriminator, DateTimeOffset moment, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token);
Task<PaginationContainer<DataWithWellDepthAndSectionDto>> GetByDate(int idDiscriminator, DateTimeOffset moment, SectionPartRequest filterRequest, PaginationRequest paginationRequest, CancellationToken token);
/// <summary>
/// Получение измененных записей за период времени
@ -75,7 +75,7 @@ public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<DataWit
/// <param name="dateEnd"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(int idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token);
/// <summary>
/// Получение списка дат, в которые происходили изменения (день, месяц, год, без времени)
@ -83,5 +83,5 @@ public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<DataWit
/// <param name="idDiscriminator"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<DateOnly>> GetDatesChange(Guid idDiscriminator, CancellationToken token);
Task<IEnumerable<DateOnly>> GetDatesChange(int idDiscriminator, CancellationToken token);
}

View File

@ -0,0 +1,43 @@
using Persistence.Models;
namespace Persistence.Repositories;
public interface IParameterRepository
{
/// <summary>
/// Получить порцию записей, начиная с заданной даты
/// </summary>
/// <param name="dateBegin"></param>
/// <param name="take"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<ParameterDto>> GetPart(int idDiscriminator, DateTimeOffset dateBegin, int take, CancellationToken token);
/// <summary>
/// Получить диапазон дат, для которых есть данные в репозитории
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
Task<DatesRangeDto> GetDatesRangeAsync(int idDiscriminator, CancellationToken token);
/// <summary>
/// Получить набор параметров (Wits) для построения графика
/// </summary>
/// <param name="discriminatorId"></param>
/// <param name="dateFrom"></param>
/// <param name="dateTo"></param>
/// <param name="approxPointsCount"></param>
/// <param name="ratio"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<ParameterDto>> GetValuesForGraph(int discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo,
int approxPointsCount, int? ratio, CancellationToken token);
/// <summary>
/// Сохранить набор параметров (Wits)
/// </summary>
/// <param name="dtos"></param>
/// <param name="witsIds"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<int> AddRange(IEnumerable<ParameterDto> dtos, CancellationToken token);
}

View File

@ -15,7 +15,7 @@ public interface ISyncWithDiscriminatorRepository<TDto>
/// <param name="dateBegin">дата начала</param>
/// <param name="token"></param>
/// /// <returns></returns>
Task<IEnumerable<TDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token);
Task<IEnumerable<TDto>> GetGtDate(int idDiscriminator, DateTimeOffset dateBegin, CancellationToken token);
/// <summary>
@ -24,5 +24,5 @@ public interface ISyncWithDiscriminatorRepository<TDto>
/// <param name="idDiscriminator">дискриминатор таблицы</param>
/// <param name="token"></param>
/// <returns></returns>
Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token);
Task<DatesRangeDto?> GetDatesRange(int idDiscriminator, CancellationToken token);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
namespace Persistence.Services;
namespace Persistence.Services.Interfaces;
/// <summary>
/// Сервис по работе с БД

View File

@ -1,4 +1,4 @@
namespace Persistence.Services;
namespace Persistence.Services.Interfaces;
public interface ITimeSeriesDataObserverService
{

View File

@ -0,0 +1,46 @@
using Persistence.Models;
namespace Persistence.Services.Interfaces;
/// <summary>
/// Сервис для работы с параметрами Wits
/// </summary>
public interface IWitsDataService
{
/// <summary>
/// Получить набор параметров для построения графика
/// </summary>
/// <param name="idDiscriminator"></param>
/// <param name="token"></param>
/// <returns></returns>
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);
/// <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);
}

View File

@ -1,4 +1,4 @@
namespace Persistence.Services;
namespace Persistence.Services.Interfaces;
public abstract class TimeSeriesDataObserverService : ITimeSeriesDataObserverService
{
}

View File

@ -0,0 +1,174 @@
using System.Text.Json.Serialization;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Persistence.Models;
using Persistence.Repositories;
using Persistence.Services.Interfaces;
using Persistence.Models.Configurations;
using Persistence.Models.Enumerations;
using System.Globalization;
namespace Persistence.Services;
public class WitsDataService : IWitsDataService
{
private readonly IParameterRepository witsDataRepository;
private readonly WitsInfo[] witsInfo;
private const int multiplier = 1000;
private const string witsConfigPath = "Persistence.Services.Config.WitsConfig.json";
public WitsDataService(IParameterRepository witsDataRepository)
{
this.witsDataRepository = witsDataRepository;
this.witsInfo = GetWitsInfo();
}
public Task<DatesRangeDto> GetDatesRangeAsync(int idDiscriminator, CancellationToken token)
{
var result = witsDataRepository.GetDatesRangeAsync(idDiscriminator, token);
return result;
}
public async Task<IEnumerable<WitsDataDto>> GetPart(int idDiscriminator, DateTimeOffset dateBegin, int take, CancellationToken token)
{
var dtos = await witsDataRepository.GetPart(idDiscriminator, dateBegin, take, token);
var result = AdaptToWitsData(dtos);
return result;
}
public async Task<IEnumerable<WitsDataDto>> GetValuesForGraph(int discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo,
int approxPointsCount, CancellationToken token)
{
var intervalSec = (dateTo - dateFrom).TotalSeconds;
int? ratio = null;
if (intervalSec > 2 * approxPointsCount)
{
ratio = (int) intervalSec / approxPointsCount;
}
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()
{
DiscriminatorId = e.DiscriminatorId,
ParameterId = EncodeId(t.RecordId, t.ItemId),
Value = t.Value.ToString()!,
Timestamp = e.Timestamped
}));
var result = await witsDataRepository.AddRange(parameterDtos, token);
return result;
}
private int EncodeId(int recordId, int itemId)
{
var resultId = multiplier * recordId + itemId;
return resultId;
}
private int DecodeRecordId(int id)
{
var resultId = id / multiplier;
return resultId;
}
private int DecodeItemId(int id)
{
var resultId = id % multiplier;
return resultId;
}
private IEnumerable<WitsDataDto> AdaptToWitsData(IEnumerable<ParameterDto> dtos)
{
var result = new List<WitsDataDto>();
var witsGroup = dtos
.GroupBy(e => new { e.DiscriminatorId, e.Timestamp });
foreach (var witsInGroup in witsGroup)
{
var witsDataDto = new WitsDataDto()
{
DiscriminatorId = witsInGroup.Key.DiscriminatorId,
Timestamped = witsInGroup.Key.Timestamp
};
witsDataDto.Values = witsInGroup.Select(e =>
{
var recordId = DecodeRecordId(e.ParameterId);
var itemId = DecodeItemId(e.ParameterId);
return new WitsValueDto()
{
RecordId = recordId,
ItemId = itemId,
Value = ConvertValue(recordId, itemId, e.Value)
};
});
result.Add(witsDataDto);
}
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;
}
}