diff --git a/AsbCloudApp/Data/TrajectoryDto.cs b/AsbCloudApp/Data/TrajectoryDto.cs new file mode 100644 index 00000000..80c965f4 --- /dev/null +++ b/AsbCloudApp/Data/TrajectoryDto.cs @@ -0,0 +1,13 @@ +namespace AsbCloudApp.Data +{ + /// + /// Формирование визуализации траектории 3D + /// + /// Глубина ствола + /// Зенитный угол + /// Азимут географиеский + public record TrajectoryDto( + double WellboreDepth, + double ZenithAngle, + double AzimuthGeo); +} diff --git a/AsbCloudApp/Repositories/IActualTrajectoryRepository.cs b/AsbCloudApp/Repositories/IActualTrajectoryRepository.cs new file mode 100644 index 00000000..76c1648d --- /dev/null +++ b/AsbCloudApp/Repositories/IActualTrajectoryRepository.cs @@ -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 +{ + /// + /// CRUD для работы с фактической траекторией из клиента + /// + /// + public interface IActualTrajectoryRepository : ITrajectoryRepository + { + + } +} diff --git a/AsbCloudApp/Repositories/IPlannedTrajectoryRepository.cs b/AsbCloudApp/Repositories/IPlannedTrajectoryRepository.cs index 94f19162..85362f9f 100644 --- a/AsbCloudApp/Repositories/IPlannedTrajectoryRepository.cs +++ b/AsbCloudApp/Repositories/IPlannedTrajectoryRepository.cs @@ -9,7 +9,7 @@ namespace AsbCloudApp.Repositories /// CRUD для работы с плановой траекторией из клиента /// /// - public interface IPlannedTrajectoryRepository + public interface IPlannedTrajectoryRepository : ITrajectoryRepository { /// /// Получить все добавленные по скважине координаты плановой траектории diff --git a/AsbCloudApp/Repositories/ITrajectoryRepository.cs b/AsbCloudApp/Repositories/ITrajectoryRepository.cs new file mode 100644 index 00000000..123f1669 --- /dev/null +++ b/AsbCloudApp/Repositories/ITrajectoryRepository.cs @@ -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 +{ + /// + /// Репозиторий по работе с траекторией + /// + public interface ITrajectoryRepository + { + /// + /// Получение траектории для 3D-визуализации + /// + /// ключ скважины + /// + /// + Task> GetTrajectoryAsync(int idWell, CancellationToken token); + } +} diff --git a/AsbCloudApp/Services/ITrajectoryVisualizationService.cs b/AsbCloudApp/Services/ITrajectoryVisualizationService.cs index 31d77271..286a81d5 100644 --- a/AsbCloudApp/Services/ITrajectoryVisualizationService.cs +++ b/AsbCloudApp/Services/ITrajectoryVisualizationService.cs @@ -11,11 +11,11 @@ namespace AsbCloudApp.Services public interface ITrajectoryVisualizationService { /// - /// Получение траектории по скважине + /// Получение плановой и фактической траектории по скважине /// - /// + /// ключ скважины /// /// - Task> GetTrajectoryAsync(int idWell, CancellationToken token); + Task>> GetTrajectoryAsync(int idWell, CancellationToken token); } } diff --git a/AsbCloudDb/Model/WITS/Record7.cs b/AsbCloudDb/Model/WITS/Record7.cs index d856f8a7..0b768243 100644 --- a/AsbCloudDb/Model/WITS/Record7.cs +++ b/AsbCloudDb/Model/WITS/Record7.cs @@ -12,6 +12,7 @@ namespace AsbCloudDb.Model.WITS { /// + /// /// RecordId = 7, /// ItemId = 8, /// LongMnemonic = "DEPTSVYM", @@ -87,6 +88,7 @@ namespace AsbCloudDb.Model.WITS public string? Svytype { get; set; } /// + /// /// RecordId = 7, /// ItemId = 13, /// LongMnemonic = "SVYINC", @@ -117,6 +119,7 @@ namespace AsbCloudDb.Model.WITS public float? Svyazu { get; set; } /// + /// /// RecordId = 7, /// ItemId = 15, /// LongMnemonic = "SVYAZC", diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 7952b1f4..1911d5a8 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -180,6 +180,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); // Subsystem service diff --git a/AsbCloudInfrastructure/Repository/ActualTrajectoryRepository.cs b/AsbCloudInfrastructure/Repository/ActualTrajectoryRepository.cs new file mode 100644 index 00000000..b89c9cf8 --- /dev/null +++ b/AsbCloudInfrastructure/Repository/ActualTrajectoryRepository.cs @@ -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> 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; + } + } +} diff --git a/AsbCloudInfrastructure/Repository/PlannedTrajectoryRepository.cs b/AsbCloudInfrastructure/Repository/PlannedTrajectoryRepository.cs index 0db7022d..d09b9690 100644 --- a/AsbCloudInfrastructure/Repository/PlannedTrajectoryRepository.cs +++ b/AsbCloudInfrastructure/Repository/PlannedTrajectoryRepository.cs @@ -86,7 +86,7 @@ namespace AsbCloudInfrastructure.Repository .Where(x => x.IdWell == idWell); var entities = await query .OrderBy(e => e.WellboreDepth) - .ToListAsync(token); + .ToArrayAsync(token); var result = entities .Select(r => Convert(r, offsetHours)); return result; @@ -115,6 +115,22 @@ namespace AsbCloudInfrastructure.Repository entity.UpdateDate = DateTime.Now.ToUtcDateTimeOffset(offsetHours); return entity; } + + public async Task> 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); + } } } diff --git a/AsbCloudInfrastructure/Services/Trajectory/TrajectoryVisualizationService.cs b/AsbCloudInfrastructure/Services/Trajectory/TrajectoryVisualizationService.cs index 8eb94b5a..6e298a37 100644 --- a/AsbCloudInfrastructure/Services/Trajectory/TrajectoryVisualizationService.cs +++ b/AsbCloudInfrastructure/Services/Trajectory/TrajectoryVisualizationService.cs @@ -12,28 +12,53 @@ namespace AsbCloudInfrastructure.Services.Trajectory 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> GetTrajectoryAsync(int idWell, CancellationToken token) + /// + /// Получение плановой и фактической траектории по скважине + /// + /// ключ скважины + /// + /// + public async Task>> GetTrajectoryAsync(int idWell, CancellationToken token) { - var geoCoordinates = (await repository.GetAsync(idWell, token)).ToArray(); + var result = new PlanFactBase>(); - if (geoCoordinates.Length < 2) - return Enumerable.Empty(); + var geoPlanCoordinates = await plannedRepository.GetTrajectoryAsync(idWell, token); + var geoFactCoordinates = await factualRepository.GetTrajectoryAsync(idWell, token); - var cartesianCoordinates = new List(geoCoordinates.Length) { - new (), - }; + result.Plan = GetTrajectoryVisualisation(geoPlanCoordinates); + result.Fact = GetTrajectoryVisualisation(geoFactCoordinates); - for (var i = 1; i < geoCoordinates.Length; i++) + return result; + } + + /// + /// Формирует список координат для визуализации трактории 3D + /// + /// + /// + private IEnumerable GetTrajectoryVisualisation(IEnumerable 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 deltaWellLength = geoCoordinates[i].WellboreDepth - intervalGeoParams.WellboreDepth; + var intervalGeoParams = geoCoordinatesArray[i - 1]; + var deltaWellLength = geoCoordinatesArray[i].WellboreDepth - intervalGeoParams.WellboreDepth; var projectionLengthToXYSurface = deltaWellLength * Math.Sin(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, }; - cartesianCoordinates.Add(coordinates); + cartesianCoordinates[i] = coordinates; } return cartesianCoordinates; diff --git a/AsbCloudWebApi.Tests/ServicesTests/TrajectoryVisualizationServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/TrajectoryVisualizationServiceTest.cs index f4532d7c..1a0dab1b 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/TrajectoryVisualizationServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/TrajectoryVisualizationServiceTest.cs @@ -1,9 +1,8 @@ using AsbCloudApp.Data; +using AsbCloudApp.Data.WITS; using AsbCloudApp.Repositories; -using AsbCloudInfrastructure.Services; using AsbCloudInfrastructure.Services.Trajectory; using Moq; -using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -14,11 +13,12 @@ namespace AsbCloudWebApi.Tests.ServicesTests { public class TrajectoryVisualizationServiceTest { - private Mock MakePlannedTrajectoryRepositoryMock(IEnumerable dateForGetMethod) + private Mock MakeTrajectoryRepositoryMock(IEnumerable dateForGetMethod) + where T : class, ITrajectoryRepository { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(r => r.GetAsync(It.IsAny(), It.IsAny())) + mock.Setup(r => r.GetTrajectoryAsync(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(dateForGetMethod)); return mock; @@ -27,65 +27,86 @@ namespace AsbCloudWebApi.Tests.ServicesTests [Fact] public async Task GetTrajectoryAsync_SameCounts() { - var plannedTrajectory = new PlannedTrajectoryDto[] + var plannedTrajectory = new TrajectoryDto[] { - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 0d}, - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 10d}, - new() { AzimuthGeo = 0d, ZenithAngle = 30d, WellboreDepth = 20d}, - new() { AzimuthGeo = 30d, ZenithAngle = 0d, WellboreDepth = 30d}, - new() { AzimuthGeo = 30d, ZenithAngle = 90d, WellboreDepth = 40d}, - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 50d}, + new(0d, 0d, 0d), + new(0d, 0d, 10d), + new(0d, 30d, 20d), + new(30d, 0d, 30d), + new(30d, 90d, 40d), + new(0d, 0d, 50d), }; - var mock = MakePlannedTrajectoryRepositoryMock(plannedTrajectory); - var service = new TrajectoryVisualizationService(mock.Object); - var result = await service.GetTrajectoryAsync(1, CancellationToken.None); + var actualTrajectory = new TrajectoryDto[] + { + new(0, 0, 0), + new(30,30,10), + new(0, 0, 20), + }; - Assert.Equal(plannedTrajectory.Length, result.Count()); + var mockPlan = MakeTrajectoryRepositoryMock(plannedTrajectory); + var mockFact = MakeTrajectoryRepositoryMock(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] public async Task GetTrajectoryAsync_StraigthBore() { - var plannedTrajectory = new PlannedTrajectoryDto[] + var trajectory = new TrajectoryDto[] { - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 0d}, - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 0d}, - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 20d}, - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 20d}, - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 30d}, - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 50d}, + new(0, 0, 0), + new(0, 0, 0), + new(20, 0, 0), + new(20, 0, 0), + new(30, 0, 0), + new(50, 0, 0), }; - var mock = MakePlannedTrajectoryRepositoryMock(plannedTrajectory); - var service = new TrajectoryVisualizationService(mock.Object); + var mockPlan = MakeTrajectoryRepositoryMock(trajectory); + var mockFact = MakeTrajectoryRepositoryMock(trajectory); + var service = new TrajectoryVisualizationService(mockPlan.Object, mockFact.Object); 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, lastPoint.Y, 0.1d); - Assert.Equal(50d, lastPoint.Z, 0.1d); + Assert.Equal(0d, lastPointPlan.X, 0.1d); + Assert.Equal(0d, lastPointPlan.Y, 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] public async Task GetTrajectoryAsync_Match() { - var plannedTrajectory = new PlannedTrajectoryDto[] + var trajectory = new TrajectoryDto[] { - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 0d}, - new() { AzimuthGeo = 30d, ZenithAngle = 30d, WellboreDepth = 10d}, - new() { AzimuthGeo = 0d, ZenithAngle = 0d, WellboreDepth = 20d}, + new(0, 0, 0), + new(10, 30, 30), + new(20, 0, 0), }; - var mock = MakePlannedTrajectoryRepositoryMock(plannedTrajectory); - var service = new TrajectoryVisualizationService(mock.Object); + var mockPlanned = MakeTrajectoryRepositoryMock(trajectory); + var mockFactual = MakeTrajectoryRepositoryMock(trajectory); + var service = new TrajectoryVisualizationService(mockPlanned.Object, mockFactual.Object); var result = await service.GetTrajectoryAsync(1, CancellationToken.None); - var lastPoint = result.Last(); - var tolerance = 0.001d; + var lastPointPlan = result.Plan!.Last(); + var lastPointFact = result.Fact!.Last(); + var tolerancePlan = 0.001d; + var toleranceFact = 0.001d; - Assert.InRange(lastPoint.Z, 10 + tolerance, 20 - tolerance); - Assert.InRange(lastPoint.Y, 0 + tolerance, 10 - tolerance); - Assert.InRange(lastPoint.X, 0 + tolerance, 10 - tolerance); + Assert.InRange(lastPointPlan.Z, 10 + tolerancePlan, 20 - tolerancePlan); + Assert.InRange(lastPointPlan.Y, 0 + tolerancePlan, 10 - tolerancePlan); + 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); } } } diff --git a/AsbCloudWebApi/Controllers/PlannedTrajectoryController.cs b/AsbCloudWebApi/Controllers/PlannedTrajectoryController.cs index df564558..5d019e18 100644 --- a/AsbCloudWebApi/Controllers/PlannedTrajectoryController.cs +++ b/AsbCloudWebApi/Controllers/PlannedTrajectoryController.cs @@ -225,14 +225,14 @@ namespace AsbCloudWebApi.Controllers } /// - /// Получение координат для визуализации траектории + /// Получение координат для визуализации траектории (плановой и фактической) /// /// /// /// [HttpGet] - [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] - public async Task GetTrajectoryAsync(int idWell, CancellationToken token) + [ProducesResponseType(typeof(PlanFactBase>), (int)System.Net.HttpStatusCode.OK)] + public async Task GetPlanFactTrajectoryAsync(int idWell, CancellationToken token) { if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false))