Merge branch 'dev' into feature/nullable-enabled-2

This commit is contained in:
Никита Фролов 2023-05-18 11:04:24 +05:00
commit f7d0e43d2f
12 changed files with 228 additions and 62 deletions

View File

@ -0,0 +1,13 @@
namespace AsbCloudApp.Data
{
/// <summary>
/// Формирование визуализации траектории 3D
/// </summary>
/// <param name="WellboreDepth">Глубина ствола</param>
/// <param name="ZenithAngle">Зенитный угол</param>
/// <param name="AzimuthGeo">Азимут географиеский</param>
public record TrajectoryDto(
double WellboreDepth,
double ZenithAngle,
double AzimuthGeo);
}

View File

@ -0,0 +1,17 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.WITS;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Repositories
{
/// <summary>
/// CRUD для работы с фактической траекторией из клиента
/// </summary>
/// <returns></returns>
public interface IActualTrajectoryRepository : ITrajectoryRepository
{
}
}

View File

@ -9,7 +9,7 @@ namespace AsbCloudApp.Repositories
/// CRUD для работы с плановой траекторией из клиента /// CRUD для работы с плановой траекторией из клиента
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public interface IPlannedTrajectoryRepository public interface IPlannedTrajectoryRepository : ITrajectoryRepository
{ {
/// <summary> /// <summary>
/// Получить все добавленные по скважине координаты плановой траектории /// Получить все добавленные по скважине координаты плановой траектории

View File

@ -0,0 +1,24 @@
using AsbCloudApp.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Repositories
{
/// <summary>
/// Репозиторий по работе с траекторией
/// </summary>
public interface ITrajectoryRepository
{
/// <summary>
/// Получение траектории для 3D-визуализации
/// </summary>
/// <param name="idWell">ключ скважины</param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<TrajectoryDto>> GetTrajectoryAsync(int idWell, CancellationToken token);
}
}

View File

@ -11,11 +11,11 @@ namespace AsbCloudApp.Services
public interface ITrajectoryVisualizationService public interface ITrajectoryVisualizationService
{ {
/// <summary> /// <summary>
/// Получение траектории по скважине /// Получение плановой и фактической траектории по скважине
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell">ключ скважины</param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
Task<IEnumerable<TrajectoryVisualizationDto>> GetTrajectoryAsync(int idWell, CancellationToken token); Task<PlanFactBase<IEnumerable<TrajectoryVisualizationDto>>> GetTrajectoryAsync(int idWell, CancellationToken token);
} }
} }

View File

@ -12,6 +12,7 @@ namespace AsbCloudDb.Model.WITS
{ {
/// <summary> /// <summary>
/// длина ствола
/// RecordId = 7, /// RecordId = 7,
/// ItemId = 8, /// ItemId = 8,
/// LongMnemonic = "DEPTSVYM", /// LongMnemonic = "DEPTSVYM",
@ -87,6 +88,7 @@ namespace AsbCloudDb.Model.WITS
public string? Svytype { get; set; } public string? Svytype { get; set; }
/// <summary> /// <summary>
/// зенитный угол
/// RecordId = 7, /// RecordId = 7,
/// ItemId = 13, /// ItemId = 13,
/// LongMnemonic = "SVYINC", /// LongMnemonic = "SVYINC",
@ -117,6 +119,7 @@ namespace AsbCloudDb.Model.WITS
public float? Svyazu { get; set; } public float? Svyazu { get; set; }
/// <summary> /// <summary>
/// азимут географический
/// RecordId = 7, /// RecordId = 7,
/// ItemId = 15, /// ItemId = 15,
/// LongMnemonic = "SVYAZC", /// LongMnemonic = "SVYAZC",

View File

@ -180,6 +180,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<ITelemetryWirelineRunOutRepository, TelemetryWirelineRunOutRepository>(); services.AddTransient<ITelemetryWirelineRunOutRepository, TelemetryWirelineRunOutRepository>();
services.AddTransient<IWellFinalDocumentsRepository, WellFinalDocumentsRepository>(); services.AddTransient<IWellFinalDocumentsRepository, WellFinalDocumentsRepository>();
services.AddTransient<IPlannedTrajectoryRepository, PlannedTrajectoryRepository>(); services.AddTransient<IPlannedTrajectoryRepository, PlannedTrajectoryRepository>();
services.AddTransient<IActualTrajectoryRepository, ActualTrajectoryRepository>();
services.AddTransient<IFaqRepository, FaqRepository>(); services.AddTransient<IFaqRepository, FaqRepository>();
// Subsystem service // Subsystem service

View File

@ -0,0 +1,46 @@
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository
{
internal class ActualTrajectoryRepository : IActualTrajectoryRepository
{
private readonly IAsbCloudDbContext db;
private readonly IWellService wellService;
public ActualTrajectoryRepository(IAsbCloudDbContext db, IWellService wellService)
{
this.db = db;
this.wellService = wellService;
}
public async Task<IEnumerable<TrajectoryDto>> GetTrajectoryAsync(int idWell, CancellationToken token)
{
var well = wellService.GetOrDefault(idWell);
if (well is null || well.Timezone is null)
throw new ArgumentInvalidException("idWell doesn`t exist", nameof(idWell));
var entities = await db.Record7
.AsNoTracking()
.Where(x => x.IdTelemetry == well.IdTelemetry)
.Where(coord => coord.Deptsvym != null && coord.Svyinc != null && coord.Svyazc != null)
.OrderBy(e => e.Deptsvym)
.Select(x => new { x.Deptsvym, x.Svyinc, x.Svyazc })
.ToArrayAsync(token);
var result = entities
.Select(coord => new TrajectoryDto(coord.Deptsvym!.Value, coord.Svyinc!.Value, coord.Svyazc!.Value))
.ToArray();
return result;
}
}
}

View File

@ -86,7 +86,7 @@ namespace AsbCloudInfrastructure.Repository
.Where(x => x.IdWell == idWell); .Where(x => x.IdWell == idWell);
var entities = await query var entities = await query
.OrderBy(e => e.WellboreDepth) .OrderBy(e => e.WellboreDepth)
.ToListAsync(token); .ToArrayAsync(token);
var result = entities var result = entities
.Select(r => Convert(r, offsetHours)); .Select(r => Convert(r, offsetHours));
return result; return result;
@ -115,6 +115,22 @@ namespace AsbCloudInfrastructure.Repository
entity.UpdateDate = DateTime.Now.ToUtcDateTimeOffset(offsetHours); entity.UpdateDate = DateTime.Now.ToUtcDateTimeOffset(offsetHours);
return entity; return entity;
} }
public async Task<IEnumerable<TrajectoryDto>> GetTrajectoryAsync(int idWell, CancellationToken token)
{
var well = wellService.GetOrDefault(idWell);
if (well is null || well.Timezone is null)
throw new ArgumentInvalidException("idWell doesn`t exist", nameof(idWell));
var query = db.PlannedTrajectories
.AsNoTracking()
.Where(x => x.IdWell == idWell);
return await query
.Select(coord => new TrajectoryDto(coord.WellboreDepth, coord.ZenithAngle, coord.AzimuthGeo))
.ToArrayAsync()
.ConfigureAwait(false);
}
} }
} }

View File

@ -12,28 +12,53 @@ namespace AsbCloudInfrastructure.Services.Trajectory
public class TrajectoryVisualizationService : ITrajectoryVisualizationService public class TrajectoryVisualizationService : ITrajectoryVisualizationService
{ {
private readonly IPlannedTrajectoryRepository repository; private readonly IPlannedTrajectoryRepository plannedRepository;
private readonly IActualTrajectoryRepository factualRepository;
public TrajectoryVisualizationService(IPlannedTrajectoryRepository repository) public TrajectoryVisualizationService(IPlannedTrajectoryRepository plannedRepository, IActualTrajectoryRepository factualRepository)
{ {
this.repository = repository; this.plannedRepository = plannedRepository;
this.factualRepository = factualRepository;
} }
public async Task<IEnumerable<TrajectoryVisualizationDto>> GetTrajectoryAsync(int idWell, CancellationToken token) /// <summary>
/// Получение плановой и фактической траектории по скважине
/// </summary>
/// <param name="idWell">ключ скважины</param>
/// <param name="token"></param>
/// <returns></returns>
public async Task<PlanFactBase<IEnumerable<TrajectoryVisualizationDto>>> GetTrajectoryAsync(int idWell, CancellationToken token)
{ {
var geoCoordinates = (await repository.GetAsync(idWell, token)).ToArray(); var result = new PlanFactBase<IEnumerable<TrajectoryVisualizationDto>>();
if (geoCoordinates.Length < 2) var geoPlanCoordinates = await plannedRepository.GetTrajectoryAsync(idWell, token);
return Enumerable.Empty<TrajectoryVisualizationDto>(); var geoFactCoordinates = await factualRepository.GetTrajectoryAsync(idWell, token);
var cartesianCoordinates = new List<TrajectoryVisualizationDto>(geoCoordinates.Length) { result.Plan = GetTrajectoryVisualisation(geoPlanCoordinates);
new (), result.Fact = GetTrajectoryVisualisation(geoFactCoordinates);
};
for (var i = 1; i < geoCoordinates.Length; i++) return result;
}
/// <summary>
/// Формирует список координат для визуализации трактории 3D
/// </summary>
/// <param name="geoCoordinates"></param>
/// <returns></returns>
private IEnumerable<TrajectoryVisualizationDto> GetTrajectoryVisualisation(IEnumerable<TrajectoryDto> geoCoordinates)
{
var geoCoordinatesLength = geoCoordinates.Count();
if (geoCoordinatesLength < 2)
return new TrajectoryVisualizationDto[0];
var cartesianCoordinates = new TrajectoryVisualizationDto[geoCoordinatesLength];
cartesianCoordinates[0] = new();
var geoCoordinatesArray = geoCoordinates.ToArray();
for (var i = 1; i < geoCoordinatesLength; i++)
{ {
var intervalGeoParams = geoCoordinates[i - 1]; var intervalGeoParams = geoCoordinatesArray[i - 1];
var deltaWellLength = geoCoordinates[i].WellboreDepth - intervalGeoParams.WellboreDepth; var deltaWellLength = geoCoordinatesArray[i].WellboreDepth - intervalGeoParams.WellboreDepth;
var projectionLengthToXYSurface = deltaWellLength * Math.Sin(intervalGeoParams.ZenithAngle * Math.PI / 180); var projectionLengthToXYSurface = deltaWellLength * Math.Sin(intervalGeoParams.ZenithAngle * Math.PI / 180);
var dz = deltaWellLength * Math.Cos(intervalGeoParams.ZenithAngle * Math.PI / 180); var dz = deltaWellLength * Math.Cos(intervalGeoParams.ZenithAngle * Math.PI / 180);
@ -48,7 +73,7 @@ namespace AsbCloudInfrastructure.Services.Trajectory
Y = preCoordinates.Y + dy, Y = preCoordinates.Y + dy,
}; };
cartesianCoordinates.Add(coordinates); cartesianCoordinates[i] = coordinates;
} }
return cartesianCoordinates; return cartesianCoordinates;

View File

@ -1,9 +1,8 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using AsbCloudApp.Data.WITS;
using AsbCloudApp.Repositories; using AsbCloudApp.Repositories;
using AsbCloudInfrastructure.Services;
using AsbCloudInfrastructure.Services.Trajectory; using AsbCloudInfrastructure.Services.Trajectory;
using Moq; using Moq;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -14,11 +13,12 @@ namespace AsbCloudWebApi.Tests.ServicesTests
{ {
public class TrajectoryVisualizationServiceTest public class TrajectoryVisualizationServiceTest
{ {
private Mock<IPlannedTrajectoryRepository> MakePlannedTrajectoryRepositoryMock(IEnumerable<PlannedTrajectoryDto> dateForGetMethod) private Mock<T> MakeTrajectoryRepositoryMock<T>(IEnumerable<TrajectoryDto> dateForGetMethod)
where T : class, ITrajectoryRepository
{ {
var mock = new Mock<IPlannedTrajectoryRepository>(); var mock = new Mock<T>();
mock.Setup(r => r.GetAsync(It.IsAny<int>(), It.IsAny<CancellationToken>())) mock.Setup(r => r.GetTrajectoryAsync(It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(dateForGetMethod)); .Returns(Task.FromResult(dateForGetMethod));
return mock; return mock;
@ -27,65 +27,86 @@ namespace AsbCloudWebApi.Tests.ServicesTests
[Fact] [Fact]
public async Task GetTrajectoryAsync_SameCounts() public async Task GetTrajectoryAsync_SameCounts()
{ {
var plannedTrajectory = new PlannedTrajectoryDto[] var plannedTrajectory = new TrajectoryDto[]
{ {
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 0d}, new(0d, 0d, 0d),
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 10d}, new(0d, 0d, 10d),
new() { AzimuthGeo = 0d, ZenithAngle = 30d, WellboreDepth = 20d}, new(0d, 30d, 20d),
new() { AzimuthGeo = 30d, ZenithAngle = 0d, WellboreDepth = 30d}, new(30d, 0d, 30d),
new() { AzimuthGeo = 30d, ZenithAngle = 90d, WellboreDepth = 40d}, new(30d, 90d, 40d),
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 50d}, new(0d, 0d, 50d),
}; };
var mock = MakePlannedTrajectoryRepositoryMock(plannedTrajectory); var actualTrajectory = new TrajectoryDto[]
var service = new TrajectoryVisualizationService(mock.Object); {
var result = await service.GetTrajectoryAsync(1, CancellationToken.None); new(0, 0, 0),
new(30,30,10),
new(0, 0, 20),
};
Assert.Equal(plannedTrajectory.Length, result.Count()); var mockPlan = MakeTrajectoryRepositoryMock<IPlannedTrajectoryRepository>(plannedTrajectory);
var mockFact = MakeTrajectoryRepositoryMock<IActualTrajectoryRepository>(actualTrajectory);
var service = new TrajectoryVisualizationService(mockPlan.Object, mockFact.Object);
var result = await service.GetTrajectoryAsync(1, CancellationToken.None);
Assert.Equal(plannedTrajectory.Length, result.Plan?.Count());
Assert.Equal(actualTrajectory.Length, result.Fact?.Count());
} }
[Fact] [Fact]
public async Task GetTrajectoryAsync_StraigthBore() public async Task GetTrajectoryAsync_StraigthBore()
{ {
var plannedTrajectory = new PlannedTrajectoryDto[] var trajectory = new TrajectoryDto[]
{ {
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 0d}, new(0, 0, 0),
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 0d}, new(0, 0, 0),
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 20d}, new(20, 0, 0),
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 20d}, new(20, 0, 0),
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 30d}, new(30, 0, 0),
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 50d}, new(50, 0, 0),
}; };
var mock = MakePlannedTrajectoryRepositoryMock(plannedTrajectory); var mockPlan = MakeTrajectoryRepositoryMock<IPlannedTrajectoryRepository>(trajectory);
var service = new TrajectoryVisualizationService(mock.Object); var mockFact = MakeTrajectoryRepositoryMock<IActualTrajectoryRepository>(trajectory);
var service = new TrajectoryVisualizationService(mockPlan.Object, mockFact.Object);
var result = await service.GetTrajectoryAsync(1, CancellationToken.None); var result = await service.GetTrajectoryAsync(1, CancellationToken.None);
var lastPoint = result.Last(); var lastPointPlan = result.Plan!.Last();
var lastPointFact = result.Fact!.Last();
Assert.Equal(0d, lastPoint.X, 0.1d); Assert.Equal(0d, lastPointPlan.X, 0.1d);
Assert.Equal(0d, lastPoint.Y, 0.1d); Assert.Equal(0d, lastPointPlan.Y, 0.1d);
Assert.Equal(50d, lastPoint.Z, 0.1d); Assert.Equal(50d, lastPointPlan.Z, 0.1d);
Assert.Equal(0d, lastPointFact.X, 0.1d);
Assert.Equal(0d, lastPointFact.Y, 0.1d);
Assert.Equal(50d, lastPointFact.Z, 0.1d);
} }
[Fact] [Fact]
public async Task GetTrajectoryAsync_Match() public async Task GetTrajectoryAsync_Match()
{ {
var plannedTrajectory = new PlannedTrajectoryDto[] var trajectory = new TrajectoryDto[]
{ {
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 0d}, new(0, 0, 0),
new() { AzimuthGeo = 30d, ZenithAngle = 30d, WellboreDepth = 10d}, new(10, 30, 30),
new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 20d}, new(20, 0, 0),
}; };
var mock = MakePlannedTrajectoryRepositoryMock(plannedTrajectory); var mockPlanned = MakeTrajectoryRepositoryMock<IPlannedTrajectoryRepository>(trajectory);
var service = new TrajectoryVisualizationService(mock.Object); var mockFactual = MakeTrajectoryRepositoryMock<IActualTrajectoryRepository>(trajectory);
var service = new TrajectoryVisualizationService(mockPlanned.Object, mockFactual.Object);
var result = await service.GetTrajectoryAsync(1, CancellationToken.None); var result = await service.GetTrajectoryAsync(1, CancellationToken.None);
var lastPoint = result.Last(); var lastPointPlan = result.Plan!.Last();
var tolerance = 0.001d; var lastPointFact = result.Fact!.Last();
var tolerancePlan = 0.001d;
var toleranceFact = 0.001d;
Assert.InRange(lastPoint.Z, 10 + tolerance, 20 - tolerance); Assert.InRange(lastPointPlan.Z, 10 + tolerancePlan, 20 - tolerancePlan);
Assert.InRange(lastPoint.Y, 0 + tolerance, 10 - tolerance); Assert.InRange(lastPointPlan.Y, 0 + tolerancePlan, 10 - tolerancePlan);
Assert.InRange(lastPoint.X, 0 + tolerance, 10 - tolerance); Assert.InRange(lastPointPlan.X, 0 + tolerancePlan, 10 - tolerancePlan);
Assert.InRange(lastPointFact.Z, 10 + toleranceFact, 20 - toleranceFact);
Assert.InRange(lastPointFact.Y, 0 + toleranceFact, 10 - toleranceFact);
Assert.InRange(lastPointFact.X, 0 + toleranceFact, 10 - toleranceFact);
} }
} }
} }

View File

@ -225,14 +225,14 @@ namespace AsbCloudWebApi.Controllers
} }
/// <summary> /// <summary>
/// Получение координат для визуализации траектории /// Получение координат для визуализации траектории (плановой и фактической)
/// </summary> /// </summary>
/// <param name="idWell"></param> /// <param name="idWell"></param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(IEnumerable<TrajectoryVisualizationDto>), (int)System.Net.HttpStatusCode.OK)] [ProducesResponseType(typeof(PlanFactBase<IEnumerable<TrajectoryVisualizationDto>>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetTrajectoryAsync(int idWell, CancellationToken token) public async Task<IActionResult> GetPlanFactTrajectoryAsync(int idWell, CancellationToken token)
{ {
if (!await CanUserAccessToWellAsync(idWell, if (!await CanUserAccessToWellAsync(idWell,
token).ConfigureAwait(false)) token).ConfigureAwait(false))