From b3eb4e10f4f4546c8584386a202e42c49d8af789 Mon Sep 17 00:00:00 2001 From: aestene Date: Fri, 13 Oct 2023 13:20:40 +0200 Subject: [PATCH 1/9] Use inspection target directly from Echo request --- backend/api.test/Database/Models.cs | 18 ++++++------ .../api/Controllers/Models/EchoInspection.cs | 29 ++++++++++++------- .../Controllers/Models/EchoMissionResponse.cs | 25 +++++++++++++++- backend/api/Database/Models/Inspection.cs | 18 +++++++++--- backend/api/Database/Models/Pose.cs | 2 +- backend/api/Services/EchoService.cs | 3 +- .../api/Services/Models/EchoPoseResponse.cs | 19 +++++++----- .../Services/Models/IsarMissionDefinition.cs | 10 +++---- 8 files changed, 85 insertions(+), 39 deletions(-) diff --git a/backend/api.test/Database/Models.cs b/backend/api.test/Database/Models.cs index 86a7fa382..fb8cf2a9f 100644 --- a/backend/api.test/Database/Models.cs +++ b/backend/api.test/Database/Models.cs @@ -10,7 +10,7 @@ public class TestPose [Fact] public void TestRotationNorth() { - var mockAngleAxisParameters = new EchoVector(0, 0, 1); + var mockAngleAxisParameters = new EnuPosition(0, 0, 1); float mockAngle = 0; var expected = new Orientation() @@ -31,7 +31,7 @@ public void TestRotationNorth() [Fact] public void TestRotationSouth() { - var mockAngleAxisParameters = new EchoVector(0, 0, 1); + var mockAngleAxisParameters = new EnuPosition(0, 0, 1); float mockAngle = MathF.PI; var expected = new Orientation() @@ -54,7 +54,7 @@ public void TestRotationSouth() [Fact] public void TestNegativaRotation() { - var mockAngleAxisParameters = new EchoVector(0, 0, 1); + var mockAngleAxisParameters = new EnuPosition(0, 0, 1); float mockAngle = -180F * MathF.PI / 180F; var expected = new Orientation() @@ -95,7 +95,7 @@ private static EchoPose ConvertPredefinedPoseToEchoPose( Orientation orientation ) { - var enuPosition = new EchoVector(position.X, position.Y, position.Z); + var enuPosition = new EnuPosition(position.X, position.Y, position.Z); var axisAngle = ConvertOrientation(orientation); return new EchoPose(enuPosition, axisAngle); } @@ -114,15 +114,15 @@ private static AxisAngle ConvertOrientation(Orientation orientation) if (angle < 0) angle += 2F * MathF.PI; - return new AxisAngle(new EchoVector(0, 0, 1), angle); + return new AxisAngle(new EnuPosition(0, 0, 1), angle); } public class AxisAngle { - public EchoVector Axis; + public EnuPosition Axis; public float Angle; - public AxisAngle(EchoVector axis, float angle) + public AxisAngle(EnuPosition axis, float angle) { Axis = axis; Angle = angle; @@ -131,10 +131,10 @@ public AxisAngle(EchoVector axis, float angle) public class EchoPose { - public EchoVector Position; + public EnuPosition Position; public AxisAngle Orientation; - public EchoPose(EchoVector position, AxisAngle orientation) + public EchoPose(EnuPosition position, AxisAngle orientation) { Position = position; Orientation = orientation; diff --git a/backend/api/Controllers/Models/EchoInspection.cs b/backend/api/Controllers/Models/EchoInspection.cs index b6a1d22ca..2cf7ca04d 100644 --- a/backend/api/Controllers/Models/EchoInspection.cs +++ b/backend/api/Controllers/Models/EchoInspection.cs @@ -1,24 +1,28 @@ using Api.Database.Models; - namespace Api.Controllers.Models { public class EchoInspection { - public InspectionType InspectionType { get; set; } - - public float? TimeInSeconds { get; set; } public EchoInspection() { InspectionType = InspectionType.Image; + InspectionPoint = new Position(); } - public EchoInspection(SensorType echoSensorType) + public EchoInspection(SensorType echoSensorType, Position inspectionPoint) { InspectionType = InspectionTypeFromEchoSensorType(echoSensorType.Key); TimeInSeconds = (float?)echoSensorType.TimeInSeconds; + InspectionPoint = inspectionPoint; } + public InspectionType InspectionType { get; set; } + + public Position InspectionPoint { get; set; } + + public float? TimeInSeconds { get; set; } + private static InspectionType InspectionTypeFromEchoSensorType(string sensorType) { return sensorType switch @@ -31,9 +35,9 @@ private static InspectionType InspectionTypeFromEchoSensorType(string sensorType "ThermicVideo" => InspectionType.ThermalVideo, "ThermalVideo" => InspectionType.ThermalVideo, _ - => throw new InvalidDataException( - $"Echo sensor type '{sensorType}' not supported" - ) + => throw new InvalidDataException( + $"Echo sensor type '{sensorType}' not supported" + ) }; } } @@ -43,18 +47,23 @@ public class EchoInspectionComparer : IEqualityComparer public bool Equals(EchoInspection? e1, EchoInspection? e2) { if (ReferenceEquals(e1, e2)) + { return true; + } if (e2 is null || e1 is null) + { return false; + } return e1.InspectionType == e2.InspectionType - && e1.TimeInSeconds == e2.TimeInSeconds; + && e1.TimeInSeconds == e2.TimeInSeconds + && e1.InspectionPoint == e2.InspectionPoint; } public int GetHashCode(EchoInspection e) { - // We cannot incorporate TimeInSeconds here are SQL queries do not handle + // We cannot incorporate TimeInSeconds here are SQL queries do not handle // nullables even with short circuiting logic return (int)e.InspectionType; } diff --git a/backend/api/Controllers/Models/EchoMissionResponse.cs b/backend/api/Controllers/Models/EchoMissionResponse.cs index 8603dfe14..bfd3203e2 100644 --- a/backend/api/Controllers/Models/EchoMissionResponse.cs +++ b/backend/api/Controllers/Models/EchoMissionResponse.cs @@ -1,6 +1,6 @@ # nullable disable using System.Text.Json.Serialization; - +using Api.Services.Models; namespace Api.Controllers.Models { public class EchoMissionResponse @@ -55,6 +55,8 @@ public class PlanItem [JsonPropertyName("poseId")] public int? PoseId { get; set; } + + public InspectionPoint InspectionPoint { get; set; } } public class SensorType @@ -71,4 +73,25 @@ public class SensorType [JsonPropertyName("planItemId")] public int PlanItemId { get; set; } } + + public class InspectionPoint + { + [JsonPropertyName("inspectionPointId")] + public int Id { get; set; } + + [JsonPropertyName("installationCode")] + public string InstallationCode { get; set; } + + [JsonPropertyName("tag")] + public string Tag { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("position")] + public EnuPosition EnuPosition { get; set; } + + [JsonPropertyName("isDefault")] + public bool IsDefault { get; set; } + } } diff --git a/backend/api/Database/Models/Inspection.cs b/backend/api/Database/Models/Inspection.cs index a438ae92b..065556008 100644 --- a/backend/api/Database/Models/Inspection.cs +++ b/backend/api/Database/Models/Inspection.cs @@ -7,13 +7,13 @@ namespace Api.Database.Models { public class Inspection { - private InspectionStatus _status; public Inspection() { InspectionType = InspectionType.Image; Status = InspectionStatus.NotStarted; + InspectionTarget = new Position(); } public Inspection(EchoInspection echoInspection) @@ -21,6 +21,7 @@ public Inspection(EchoInspection echoInspection) InspectionType = echoInspection.InspectionType; VideoDuration = echoInspection.TimeInSeconds; Status = InspectionStatus.NotStarted; + InspectionTarget = echoInspection.InspectionPoint; } public Inspection(CustomInspectionQuery inspectionQuery) @@ -41,6 +42,7 @@ public Inspection(Inspection copy) VideoDuration = copy.VideoDuration; AnalysisType = copy.AnalysisType; InspectionUrl = copy.InspectionUrl; + InspectionTarget = copy.InspectionTarget; } [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -50,6 +52,8 @@ public Inspection(Inspection copy) // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local public string? IsarStepId { get; private set; } = Guid.NewGuid().ToString(); + public Position InspectionTarget { get; set; } + [Required] public InspectionStatus Status { @@ -57,9 +61,15 @@ public InspectionStatus Status set { _status = value; - if (IsCompleted && EndTime is null) { EndTime = DateTime.UtcNow; } - - if (_status is InspectionStatus.InProgress && StartTime is null) { StartTime = DateTime.UtcNow; } + if (IsCompleted && EndTime is null) + { + EndTime = DateTime.UtcNow; + } + + if (_status is InspectionStatus.InProgress && StartTime is null) + { + StartTime = DateTime.UtcNow; + } } } diff --git a/backend/api/Database/Models/Pose.cs b/backend/api/Database/Models/Pose.cs index cd33086fe..1135d703a 100644 --- a/backend/api/Database/Models/Pose.cs +++ b/backend/api/Database/Models/Pose.cs @@ -111,7 +111,7 @@ float w Orientation = new Orientation(x_ori, y_ori, z_ori, w); } - public Pose(EchoVector enuPosition, float echoAngle) + public Pose(EnuPosition enuPosition, float echoAngle) { Position = new Position(enuPosition.East, enuPosition.North, enuPosition.Up); Orientation = AxisAngleToQuaternion(echoAngle); diff --git a/backend/api/Services/EchoService.cs b/backend/api/Services/EchoService.cs index 424402305..45e9c169a 100644 --- a/backend/api/Services/EchoService.cs +++ b/backend/api/Services/EchoService.cs @@ -137,8 +137,7 @@ private List ProcessPlanItems(List planItems, string installa $"https://stid.equinor.com/{installationCode}/tag?tagNo={planItem.Tag}" ), Inspections = planItem.SensorTypes - .Select(sensor => new EchoInspection(sensor)) - .ToList() + .Select(sensor => new EchoInspection(sensor, planItem.InspectionPoint.EnuPosition.ToPosition())).Distinct(new EchoInspectionComparer()).ToList() }; if (tag.Inspections.IsNullOrEmpty()) diff --git a/backend/api/Services/Models/EchoPoseResponse.cs b/backend/api/Services/Models/EchoPoseResponse.cs index 720dc034e..210ae11ea 100644 --- a/backend/api/Services/Models/EchoPoseResponse.cs +++ b/backend/api/Services/Models/EchoPoseResponse.cs @@ -1,5 +1,6 @@ #nullable disable using System.Text.Json.Serialization; +using Api.Database.Models; namespace Api.Services.Models { public class EchoPoseResponse @@ -13,32 +14,36 @@ public class EchoPoseResponse [JsonPropertyName("name")] public string Name { get; set; } [JsonPropertyName("position")] - public EchoVector Position { get; set; } + public EnuPosition Position { get; set; } [JsonPropertyName("robotBodyDirectionDegrees")] public float RobotBodyDirectionDegrees { get; set; } [JsonPropertyName("isDefault")] public bool IsDefault { get; set; } } - public class EchoVector + public class EnuPosition { + public EnuPosition(float east, float north, float up) + { + East = east; + North = north; + Up = up; + } [JsonPropertyName("e")] public float East { get; set; } [JsonPropertyName("n")] public float North { get; set; } [JsonPropertyName("u")] public float Up { get; set; } - public EchoVector(float east, float north, float up) + + public Position ToPosition() { - East = east; - North = north; - Up = up; + return new Position(East, North, Up); } } public class EchoPoseRequestBody { public string InstallationCode { get; set; } public List Tags { get; set; } - } } diff --git a/backend/api/Services/Models/IsarMissionDefinition.cs b/backend/api/Services/Models/IsarMissionDefinition.cs index 824a30e2f..ab466fa78 100644 --- a/backend/api/Services/Models/IsarMissionDefinition.cs +++ b/backend/api/Services/Models/IsarMissionDefinition.cs @@ -53,7 +53,7 @@ public IsarTaskDefinition(MissionTask missionTask, MissionRun missionRun) var isarInspections = new List(); foreach (var inspection in missionTask.Inspections) { - isarInspections.Add(new IsarInspectionDefinition(inspection, missionTask, missionRun)); + isarInspections.Add(new IsarInspectionDefinition(inspection, missionRun)); } Inspections = isarInspections; } @@ -76,14 +76,14 @@ public struct IsarInspectionDefinition [JsonPropertyName("metadata")] public Dictionary? Metadata { get; set; } - public IsarInspectionDefinition(Inspection inspection, MissionTask task, MissionRun missionRun) + public IsarInspectionDefinition(Inspection inspection, MissionRun missionRun) { Id = inspection.IsarStepId; Type = inspection.InspectionType.ToString(); InspectionTarget = new IsarPosition( - task.InspectionTarget.X, - task.InspectionTarget.Y, - task.InspectionTarget.Z, + inspection.InspectionTarget.X, + inspection.InspectionTarget.Y, + inspection.InspectionTarget.Z, "asset" ); Duration = inspection.VideoDuration; From 1c3d5412a2381aa4b4afb5606ab802d1e3e451da Mon Sep 17 00:00:00 2001 From: aestene Date: Fri, 13 Oct 2023 14:12:18 +0200 Subject: [PATCH 2/9] Use robot pose directly from Echo request --- backend/api.test/Mocks/EchoServiceMock.cs | 11 +- backend/api/Controllers/EchoController.cs | 287 ++++++++---------- .../Controllers/Models/EchoMissionResponse.cs | 28 ++ backend/api/Services/EchoService.cs | 41 +-- .../api/Services/Models/EchoPoseResponse.cs | 23 -- 5 files changed, 166 insertions(+), 224 deletions(-) diff --git a/backend/api.test/Mocks/EchoServiceMock.cs b/backend/api.test/Mocks/EchoServiceMock.cs index 9aa2045d3..11491da46 100644 --- a/backend/api.test/Mocks/EchoServiceMock.cs +++ b/backend/api.test/Mocks/EchoServiceMock.cs @@ -4,8 +4,6 @@ using System.Threading.Tasks; using Api.Controllers.Models; using Api.Services; -using Api.Services.Models; - namespace Api.Test.Mocks { public class MockEchoService : IEchoService @@ -38,13 +36,13 @@ public class MockEchoService : IEchoService new() { EchoMissionId = 1, - Name = "test", + Name = "test" }; public async Task> GetAvailableMissions(string? installationCode) { await Task.Run(() => Thread.Sleep(1)); - return new List(new CondensedEchoMissionDefinition[] { MockMissionDefinition }); + return new List(new[] { MockMissionDefinition }); } public async Task GetMissionById(int missionId) @@ -58,11 +56,6 @@ public async Task> GetEchoPlantInfos() await Task.Run(() => Thread.Sleep(1)); return _mockEchoPlantInfo; } - public async Task GetRobotPoseFromPoseId(int poseId) - { - await Task.Run(() => Thread.Sleep(1)); - return new EchoPoseResponse(); - } public Task GetMissionByPath(string relativePath) { diff --git a/backend/api/Controllers/EchoController.cs b/backend/api/Controllers/EchoController.cs index 989410b50..76f59f054 100644 --- a/backend/api/Controllers/EchoController.cs +++ b/backend/api/Controllers/EchoController.cs @@ -2,36 +2,34 @@ using System.Text.Json; using Api.Controllers.Models; using Api.Services; -using Api.Services.Models; using Api.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; - -namespace Api.Controllers; - -[ApiController] -[Route("echo")] -[Authorize(Roles = Role.Any)] -public class EchoController : ControllerBase +namespace Api.Controllers { - private readonly ILogger _logger; + [ApiController] + [Route("echo")] + [Authorize(Roles = Role.Any)] + public class EchoController : ControllerBase + { - private readonly IEchoService _echoService; + private readonly IEchoService _echoService; + private readonly ILogger _logger; - private readonly IRobotService _robotService; + private readonly IRobotService _robotService; - public EchoController(ILogger logger, IEchoService echoService, IRobotService robotService) - { - _logger = logger; - _echoService = echoService; - _robotService = robotService; - } + public EchoController(ILogger logger, IEchoService echoService, IRobotService robotService) + { + _logger = logger; + _echoService = echoService; + _robotService = robotService; + } /// - /// List all available Echo missions for the installation + /// List all available Echo missions for the installation /// /// - /// These missions are created in the Echo mission planner + /// These missions are created in the Echo mission planner /// [HttpGet] [Route("available-missions/{plantCode}")] @@ -60,159 +58,126 @@ public async Task>> GetAvaila } } - /// - /// Lookup Echo mission by Id - /// - /// - /// This mission is created in the Echo mission planner - /// - [HttpGet] - [Route("missions/{missionId}")] - [ProducesResponseType(typeof(EchoMission), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - [ProducesResponseType(StatusCodes.Status502BadGateway)] - public async Task> GetEchoMission([FromRoute] int missionId) - { - try - { - var mission = await _echoService.GetMissionById(missionId); - return Ok(mission); - } - catch (HttpRequestException e) - { - if (e.StatusCode.HasValue && (int)e.StatusCode.Value == 404) + /// + /// Lookup Echo mission by Id + /// + /// + /// This mission is created in the Echo mission planner + /// + [HttpGet] + [Route("missions/{missionId}")] + [ProducesResponseType(typeof(EchoMission), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [ProducesResponseType(StatusCodes.Status502BadGateway)] + public async Task> GetEchoMission([FromRoute] int missionId) + { + try { - _logger.LogWarning("Could not find echo mission with id={id}", missionId); - return NotFound("Echo mission not found"); + var mission = await _echoService.GetMissionById(missionId); + return Ok(mission); } - - _logger.LogError(e, "Error getting mission from Echo"); - return new StatusCodeResult(StatusCodes.Status502BadGateway); - } - catch (JsonException e) - { - _logger.LogError(e, "Error deserializing mission from Echo"); - return new StatusCodeResult(StatusCodes.Status500InternalServerError); - } - catch (InvalidDataException e) - { - string message = - "EchoMission invalid: One or more tags are missing associated robot poses."; - _logger.LogError(e, message); - return StatusCode(StatusCodes.Status502BadGateway, message); - } - } - - [HttpPost] - [Route("robot-poses/{poseId}")] - [ProducesResponseType(typeof(EchoPoseResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - [ProducesResponseType(StatusCodes.Status502BadGateway)] - public async Task> GetPositionsFromPoseId([FromRoute] int poseId) - { - try - { - var poses = await _echoService.GetRobotPoseFromPoseId(poseId); - return Ok(poses); - } - catch (HttpRequestException e) - { - if (e.StatusCode.HasValue && (int)e.StatusCode.Value == 404) + catch (HttpRequestException e) { - _logger.LogWarning("Error in echopose"); - return NotFound("Echo pose not found"); + if (e.StatusCode.HasValue && (int)e.StatusCode.Value == 404) + { + _logger.LogWarning("Could not find echo mission with id={id}", missionId); + return NotFound("Echo mission not found"); + } + + _logger.LogError(e, "Error getting mission from Echo"); + return new StatusCodeResult(StatusCodes.Status502BadGateway); + } + catch (JsonException e) + { + _logger.LogError(e, "Error deserializing mission from Echo"); + return new StatusCodeResult(StatusCodes.Status500InternalServerError); + } + catch (InvalidDataException e) + { + string message = + "EchoMission invalid: One or more tags are missing associated robot poses."; + _logger.LogError(e, message); + return StatusCode(StatusCodes.Status502BadGateway, message); } - - _logger.LogError(e, "Error getting position from Echo"); - return new StatusCodeResult(StatusCodes.Status502BadGateway); - } - catch (JsonException e) - { - _logger.LogError(e, "Error deserializing position from Echo"); - return new StatusCodeResult(StatusCodes.Status500InternalServerError); } - } - /// - /// Get selected information on all the plants in Echo - /// - [HttpGet] - [Route("plants")] - [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - [ProducesResponseType(StatusCodes.Status502BadGateway)] - public async Task> GetEchoPlantInfos() - { - try - { - var echoPlantInfos = await _echoService.GetEchoPlantInfos(); - return Ok(echoPlantInfos); - } - catch (HttpRequestException e) - { - _logger.LogError(e, "Error getting plant info from Echo"); - return new StatusCodeResult(StatusCodes.Status502BadGateway); - } - catch (JsonException e) - { - _logger.LogError(e, "Error deserializing plant info response from Echo"); - return new StatusCodeResult(StatusCodes.Status500InternalServerError); + /// + /// Get selected information on all the plants in Echo + /// + [HttpGet] + [Route("plants")] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [ProducesResponseType(StatusCodes.Status502BadGateway)] + public async Task> GetEchoPlantInfos() + { + try + { + var echoPlantInfos = await _echoService.GetEchoPlantInfos(); + return Ok(echoPlantInfos); + } + catch (HttpRequestException e) + { + _logger.LogError(e, "Error getting plant info from Echo"); + return new StatusCodeResult(StatusCodes.Status502BadGateway); + } + catch (JsonException e) + { + _logger.LogError(e, "Error deserializing plant info response from Echo"); + return new StatusCodeResult(StatusCodes.Status500InternalServerError); + } } - } - /// - /// Get all plants associated with an active robot. - /// - /// - /// Retrieves the plants that have an active robot - /// - [HttpGet] - [Authorize(Roles = Role.User)] - [Route("active-plants")] - [ProducesResponseType(typeof(IList), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task>> GetActivePlants() - { - var plants = await _robotService.ReadAllActivePlants(); - - if (plants == null) - { - _logger.LogWarning("Could not retrieve robot plants information"); - throw new RobotInformationNotAvailableException("Could not retrieve robot plants information"); - } + /// + /// Get all plants associated with an active robot. + /// + /// + /// Retrieves the plants that have an active robot + /// + [HttpGet] + [Authorize(Roles = Role.User)] + [Route("active-plants")] + [ProducesResponseType(typeof(IList), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task>> GetActivePlants() + { + var plants = await _robotService.ReadAllActivePlants(); + + if (plants == null) + { + _logger.LogWarning("Could not retrieve robot plants information"); + throw new RobotInformationNotAvailableException("Could not retrieve robot plants information"); + } - plants = plants.Select(p => p.ToLower(CultureInfo.CurrentCulture)); + plants = plants.Select(p => p.ToLower(CultureInfo.CurrentCulture)); - try - { - var echoPlantInfos = await _echoService.GetEchoPlantInfos(); + try + { + var echoPlantInfos = await _echoService.GetEchoPlantInfos(); - echoPlantInfos = echoPlantInfos.Where(p => plants.Contains(p.PlantCode.ToLower(CultureInfo.CurrentCulture))).ToList(); - return Ok(echoPlantInfos); - } - catch (HttpRequestException e) - { - _logger.LogError(e, "Error getting plant info from Echo"); - return new StatusCodeResult(StatusCodes.Status502BadGateway); - } - catch (JsonException e) - { - _logger.LogError(e, "Error deserializing plant info response from Echo"); - return new StatusCodeResult(StatusCodes.Status500InternalServerError); + echoPlantInfos = echoPlantInfos.Where(p => plants.Contains(p.PlantCode.ToLower(CultureInfo.CurrentCulture))).ToList(); + return Ok(echoPlantInfos); + } + catch (HttpRequestException e) + { + _logger.LogError(e, "Error getting plant info from Echo"); + return new StatusCodeResult(StatusCodes.Status502BadGateway); + } + catch (JsonException e) + { + _logger.LogError(e, "Error deserializing plant info response from Echo"); + return new StatusCodeResult(StatusCodes.Status500InternalServerError); + } } } } diff --git a/backend/api/Controllers/Models/EchoMissionResponse.cs b/backend/api/Controllers/Models/EchoMissionResponse.cs index bfd3203e2..af280cfd8 100644 --- a/backend/api/Controllers/Models/EchoMissionResponse.cs +++ b/backend/api/Controllers/Models/EchoMissionResponse.cs @@ -56,6 +56,10 @@ public class PlanItem [JsonPropertyName("poseId")] public int? PoseId { get; set; } + [JsonPropertyName("pose")] + public EchoPose EchoPose { get; set; } + + [JsonPropertyName("inspectionPoint")] public InspectionPoint InspectionPoint { get; set; } } @@ -74,6 +78,30 @@ public class SensorType public int PlanItemId { get; set; } } + public class EchoPose + { + [JsonPropertyName("poseId")] + public int PoseId { get; set; } + + [JsonPropertyName("installationCode")] + public string InstallationCode { get; set; } + + [JsonPropertyName("tag")] + public string Tag { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("position")] + public EnuPosition Position { get; set; } + + [JsonPropertyName("robotBodyDirectionDegrees")] + public float RobotBodyDirectionDegrees { get; set; } + + [JsonPropertyName("isDefault")] + public bool IsDefault { get; set; } + } + public class InspectionPoint { [JsonPropertyName("inspectionPointId")] diff --git a/backend/api/Services/EchoService.cs b/backend/api/Services/EchoService.cs index 45e9c169a..22d903d69 100644 --- a/backend/api/Services/EchoService.cs +++ b/backend/api/Services/EchoService.cs @@ -1,7 +1,6 @@ using System.Text.Json; using Api.Controllers.Models; using Api.Database.Models; -using Api.Services.Models; using Api.Utilities; using Microsoft.Identity.Abstractions; using Microsoft.IdentityModel.Tokens; @@ -14,7 +13,6 @@ public interface IEchoService public Task GetMissionById(int missionId); public Task> GetEchoPlantInfos(); - public Task GetRobotPoseFromPoseId(int poseId); } public class EchoService : IEchoService @@ -70,7 +68,7 @@ public async Task GetMissionById(int missionId) response.EnsureSuccessStatusCode(); var echoMission = await response.Content.ReadFromJsonAsync() ?? throw new JsonException("Failed to deserialize mission from Echo"); - var processedEchoMission = ProcessEchoMission(echoMission) ?? throw new InvalidDataException($"EchoMission with id: {missionId} is invalid."); + var processedEchoMission = ProcessEchoMission(echoMission) ?? throw new InvalidDataException($"EchoMission with id: {missionId} is invalid"); return processedEchoMission; } @@ -94,23 +92,7 @@ public async Task> GetEchoPlantInfos() return installations; } - public async Task GetRobotPoseFromPoseId(int poseId) - { - string relativePath = $"/robots/pose/{poseId}"; - var response = await _echoApi.CallApiForUserAsync( - ServiceName, - options => - { - options.HttpMethod = HttpMethod.Get; - options.RelativePath = relativePath; - } - ); - response.EnsureSuccessStatusCode(); - var echoPoseResponse = await response.Content.ReadFromJsonAsync() ?? throw new JsonException("Failed to deserialize robot pose from Echo"); - return echoPoseResponse; - } - - private List ProcessPlanItems(List planItems, string installationCode) + private static List ProcessPlanItems(List planItems, string installationCode) { var tags = new List(); @@ -119,10 +101,10 @@ private List ProcessPlanItems(List planItems, string installa if (planItem.PoseId is null) { string message = - $"Invalid EchoMission: {planItem.Tag} has no associated pose id."; + $"Invalid EchoMission: {planItem.Tag} has no associated pose id"; throw new InvalidDataException(message); } - var robotPose = GetRobotPoseFromPoseId(planItem.PoseId.Value).Result; + var tag = new EchoTag { Id = planItem.Id, @@ -130,8 +112,8 @@ private List ProcessPlanItems(List planItems, string installa PoseId = planItem.PoseId.Value, PlanOrder = planItem.SortingOrder, Pose = new Pose( - robotPose.Position, - robotPose.RobotBodyDirectionDegrees * MathF.PI / 180 + planItem.EchoPose.Position, + planItem.EchoPose.RobotBodyDirectionDegrees * MathF.PI / 180 ), URL = new Uri( $"https://stid.equinor.com/{installationCode}/tag?tagNo={planItem.Tag}" @@ -165,16 +147,14 @@ private List ProcessAvailableEchoMission(List ProcessAvailableEchoMission(List echoPlantInfoResponse var echoPlantInfo = new EchoPlantInfo { - PlantCode = plant.InstallationCode, - ProjectDescription = plant.ProjectDescription + PlantCode = plant.InstallationCode, ProjectDescription = plant.ProjectDescription }; echoPlantInfos.Add(echoPlantInfo); diff --git a/backend/api/Services/Models/EchoPoseResponse.cs b/backend/api/Services/Models/EchoPoseResponse.cs index 210ae11ea..cd9c1aa04 100644 --- a/backend/api/Services/Models/EchoPoseResponse.cs +++ b/backend/api/Services/Models/EchoPoseResponse.cs @@ -3,24 +3,6 @@ using Api.Database.Models; namespace Api.Services.Models { - public class EchoPoseResponse - { - [JsonPropertyName("poseId")] - public int PoseId { get; set; } - [JsonPropertyName("installationCode")] - public string InstallationCode { get; set; } - [JsonPropertyName("tag")] - public string Tag { get; set; } - [JsonPropertyName("name")] - public string Name { get; set; } - [JsonPropertyName("position")] - public EnuPosition Position { get; set; } - - [JsonPropertyName("robotBodyDirectionDegrees")] - public float RobotBodyDirectionDegrees { get; set; } - [JsonPropertyName("isDefault")] - public bool IsDefault { get; set; } - } public class EnuPosition { public EnuPosition(float east, float north, float up) @@ -41,9 +23,4 @@ public Position ToPosition() return new Position(East, North, Up); } } - public class EchoPoseRequestBody - { - public string InstallationCode { get; set; } - public List Tags { get; set; } - } } From 4ce7e961ad8da0e37a867696cef5180d3207a7f6 Mon Sep 17 00:00:00 2001 From: aestene Date: Fri, 13 Oct 2023 15:09:23 +0200 Subject: [PATCH 3/9] Override equals method for position object --- .../api/Controllers/Models/EchoInspection.cs | 2 +- backend/api/Database/Models/Pose.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/backend/api/Controllers/Models/EchoInspection.cs b/backend/api/Controllers/Models/EchoInspection.cs index 2cf7ca04d..526ce2044 100644 --- a/backend/api/Controllers/Models/EchoInspection.cs +++ b/backend/api/Controllers/Models/EchoInspection.cs @@ -58,7 +58,7 @@ public bool Equals(EchoInspection? e1, EchoInspection? e2) return e1.InspectionType == e2.InspectionType && e1.TimeInSeconds == e2.TimeInSeconds - && e1.InspectionPoint == e2.InspectionPoint; + && e1.InspectionPoint.Equals(e2.InspectionPoint); } public int GetHashCode(EchoInspection e) diff --git a/backend/api/Database/Models/Pose.cs b/backend/api/Database/Models/Pose.cs index 1135d703a..2424765ea 100644 --- a/backend/api/Database/Models/Pose.cs +++ b/backend/api/Database/Models/Pose.cs @@ -76,9 +76,27 @@ public Position(float x = 0, float y = 0, float z = 0) Y = y; Z = z; } + public float X { get; set; } public float Y { get; set; } public float Z { get; set; } + + public override bool Equals(object obj) + { + if (obj is not Position position) { return false; } + const float Tolerance = 1e-6F; + + if (MathF.Abs(position.X - X) > Tolerance) { return false; } + if (MathF.Abs(position.Y - Y) > Tolerance) { return false; } + if (MathF.Abs(position.Z - Z) > Tolerance) { return false; } + + return true; + } + + public override int GetHashCode() + { + throw new NotImplementedException(); + } } [Owned] From 55cd4db1229c9cf20d68ceadb68640a401942a70 Mon Sep 17 00:00:00 2001 From: aestene Date: Fri, 13 Oct 2023 15:10:28 +0200 Subject: [PATCH 4/9] Refactor orientation equality override --- backend/api/Database/Models/Pose.cs | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/backend/api/Database/Models/Pose.cs b/backend/api/Database/Models/Pose.cs index 2424765ea..6b5e162a0 100644 --- a/backend/api/Database/Models/Pose.cs +++ b/backend/api/Database/Models/Pose.cs @@ -31,25 +31,13 @@ public Orientation(float x = 0, float y = 0, float z = 0, float w = 1) public override bool Equals(object obj) { - if (obj is not Orientation) { return false; } + if (obj is not Orientation orientation) { return false; } const float Tolerance = 1e-6F; - var orientation = (Orientation)obj; - if (MathF.Abs(orientation.X - X) > Tolerance) - { - return false; - } - if (MathF.Abs(orientation.Y - Y) > Tolerance) - { - return false; - } - if (MathF.Abs(orientation.Z - Z) > Tolerance) - { - return false; - } - if (MathF.Abs(orientation.W - W) > Tolerance) - { - return false; - } + if (MathF.Abs(orientation.X - X) > Tolerance) { return false; } + if (MathF.Abs(orientation.Y - Y) > Tolerance) { return false; } + if (MathF.Abs(orientation.Z - Z) > Tolerance) { return false; } + if (MathF.Abs(orientation.W - W) > Tolerance) { return false; } + return true; } @@ -161,10 +149,7 @@ public Orientation AxisAngleToQuaternion(float echoAngle) var quaternion = new Orientation { - X = 0, - Y = 0, - Z = MathF.Sin(angle / 2), - W = MathF.Cos(angle / 2) + X = 0, Y = 0, Z = MathF.Sin(angle / 2), W = MathF.Cos(angle / 2) }; return quaternion; From b807b37f52696cf8447fd07aa713b0dd8f4a0a8c Mon Sep 17 00:00:00 2001 From: aestene Date: Fri, 13 Oct 2023 15:17:02 +0200 Subject: [PATCH 5/9] Rename file to EnuPosition --- .../api/Services/Models/{EchoPoseResponse.cs => EnuPosition.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename backend/api/Services/Models/{EchoPoseResponse.cs => EnuPosition.cs} (100%) diff --git a/backend/api/Services/Models/EchoPoseResponse.cs b/backend/api/Services/Models/EnuPosition.cs similarity index 100% rename from backend/api/Services/Models/EchoPoseResponse.cs rename to backend/api/Services/Models/EnuPosition.cs From fb7355d6e5c0979d1eb25b2e3f720e31045a6bd9 Mon Sep 17 00:00:00 2001 From: aestene Date: Fri, 13 Oct 2023 15:18:50 +0200 Subject: [PATCH 6/9] Format unnecessary lines --- backend/api/Controllers/Models/EchoInspection.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/backend/api/Controllers/Models/EchoInspection.cs b/backend/api/Controllers/Models/EchoInspection.cs index 526ce2044..62daa58c4 100644 --- a/backend/api/Controllers/Models/EchoInspection.cs +++ b/backend/api/Controllers/Models/EchoInspection.cs @@ -46,15 +46,9 @@ public class EchoInspectionComparer : IEqualityComparer { public bool Equals(EchoInspection? e1, EchoInspection? e2) { - if (ReferenceEquals(e1, e2)) - { - return true; - } + if (ReferenceEquals(e1, e2)) { return true; } - if (e2 is null || e1 is null) - { - return false; - } + if (e2 is null || e1 is null) { return false; } return e1.InspectionType == e2.InspectionType && e1.TimeInSeconds == e2.TimeInSeconds From d6301225f56849e17531ce4105dcc71c1cef1f2b Mon Sep 17 00:00:00 2001 From: aestene Date: Wed, 18 Oct 2023 10:17:43 +0200 Subject: [PATCH 7/9] Add inspection target to custom mission query --- backend/api.test/Client/AreaTests.cs | 16 ++++++++++++++-- .../api/Controllers/Models/CustomMissionQuery.cs | 2 ++ backend/api/Database/Models/Inspection.cs | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/backend/api.test/Client/AreaTests.cs b/backend/api.test/Client/AreaTests.cs index f97cb2dc1..1e41160f7 100644 --- a/backend/api.test/Client/AreaTests.cs +++ b/backend/api.test/Client/AreaTests.cs @@ -171,11 +171,23 @@ public async Task GetMissionsInAreaTest() var inspections = new List { - new() { AnalysisType = AnalysisType.CarSeal, InspectionType = InspectionType.Image} + new() + { + AnalysisType = AnalysisType.CarSeal, + InspectionTarget = new Position(), + InspectionType = InspectionType.Image + } }; var tasks = new List { - new() { Inspections = inspections, InspectionTarget = new Position(), TagId = "test", RobotPose = new Pose(), TaskOrder = 0} + new() + { + Inspections = inspections, + InspectionTarget = new Position(), + TagId = "test", + RobotPose = new Pose(), + TaskOrder = 0 + } }; var missionQuery = new CustomMissionQuery { diff --git a/backend/api/Controllers/Models/CustomMissionQuery.cs b/backend/api/Controllers/Models/CustomMissionQuery.cs index 723a3759b..a053d3599 100644 --- a/backend/api/Controllers/Models/CustomMissionQuery.cs +++ b/backend/api/Controllers/Models/CustomMissionQuery.cs @@ -5,6 +5,8 @@ public struct CustomInspectionQuery { public InspectionType InspectionType { get; set; } + public Position InspectionTarget { get; set; } + public float? VideoDuration { get; set; } public AnalysisType? AnalysisType { get; set; } diff --git a/backend/api/Database/Models/Inspection.cs b/backend/api/Database/Models/Inspection.cs index 065556008..019e67032 100644 --- a/backend/api/Database/Models/Inspection.cs +++ b/backend/api/Database/Models/Inspection.cs @@ -27,6 +27,7 @@ public Inspection(EchoInspection echoInspection) public Inspection(CustomInspectionQuery inspectionQuery) { InspectionType = inspectionQuery.InspectionType; + InspectionTarget = inspectionQuery.InspectionTarget; VideoDuration = inspectionQuery.VideoDuration; AnalysisType = inspectionQuery.AnalysisType; Status = InspectionStatus.NotStarted; From 8e35736c9fcbeddf136bad1858bdedddd6bbc306 Mon Sep 17 00:00:00 2001 From: aestene Date: Wed, 18 Oct 2023 11:12:03 +0200 Subject: [PATCH 8/9] Fix formatting --- backend/api/Controllers/EchoController.cs | 60 +++++++++---------- .../api/Controllers/Models/EchoInspection.cs | 2 +- backend/api/Database/Models/Pose.cs | 5 +- backend/api/Services/EchoService.cs | 7 ++- 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/backend/api/Controllers/EchoController.cs b/backend/api/Controllers/EchoController.cs index 76f59f054..5a378de90 100644 --- a/backend/api/Controllers/EchoController.cs +++ b/backend/api/Controllers/EchoController.cs @@ -25,38 +25,38 @@ public EchoController(ILogger logger, IEchoService echoService, _robotService = robotService; } - /// - /// List all available Echo missions for the installation - /// - /// - /// These missions are created in the Echo mission planner - /// - [HttpGet] - [Route("available-missions/{plantCode}")] - [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - [ProducesResponseType(StatusCodes.Status502BadGateway)] - public async Task>> GetAvailableEchoMissions([FromRoute] string? plantCode) - { - try - { - var missions = await _echoService.GetAvailableMissions(plantCode); - return Ok(missions); - } - catch (HttpRequestException e) - { - _logger.LogError(e, "Error retrieving missions from Echo"); - return new StatusCodeResult(StatusCodes.Status502BadGateway); - } - catch (JsonException e) + /// + /// List all available Echo missions for the installation + /// + /// + /// These missions are created in the Echo mission planner + /// + [HttpGet] + [Route("available-missions/{plantCode}")] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [ProducesResponseType(StatusCodes.Status502BadGateway)] + public async Task>> GetAvailableEchoMissions([FromRoute] string? plantCode) { - _logger.LogError(e, "Error retrieving missions from Echo"); - return new StatusCodeResult(StatusCodes.Status500InternalServerError); + try + { + var missions = await _echoService.GetAvailableMissions(plantCode); + return Ok(missions); + } + catch (HttpRequestException e) + { + _logger.LogError(e, "Error retrieving missions from Echo"); + return new StatusCodeResult(StatusCodes.Status502BadGateway); + } + catch (JsonException e) + { + _logger.LogError(e, "Error retrieving missions from Echo"); + return new StatusCodeResult(StatusCodes.Status500InternalServerError); + } } - } /// /// Lookup Echo mission by Id diff --git a/backend/api/Controllers/Models/EchoInspection.cs b/backend/api/Controllers/Models/EchoInspection.cs index 62daa58c4..eabaa8b26 100644 --- a/backend/api/Controllers/Models/EchoInspection.cs +++ b/backend/api/Controllers/Models/EchoInspection.cs @@ -57,7 +57,7 @@ public bool Equals(EchoInspection? e1, EchoInspection? e2) public int GetHashCode(EchoInspection e) { - // We cannot incorporate TimeInSeconds here are SQL queries do not handle + // We cannot incorporate TimeInSeconds here as SQL queries do not handle // nullables even with short circuiting logic return (int)e.InspectionType; } diff --git a/backend/api/Database/Models/Pose.cs b/backend/api/Database/Models/Pose.cs index 6b5e162a0..fba59b218 100644 --- a/backend/api/Database/Models/Pose.cs +++ b/backend/api/Database/Models/Pose.cs @@ -149,7 +149,10 @@ public Orientation AxisAngleToQuaternion(float echoAngle) var quaternion = new Orientation { - X = 0, Y = 0, Z = MathF.Sin(angle / 2), W = MathF.Cos(angle / 2) + X = 0, + Y = 0, + Z = MathF.Sin(angle / 2), + W = MathF.Cos(angle / 2) }; return quaternion; diff --git a/backend/api/Services/EchoService.cs b/backend/api/Services/EchoService.cs index 22d903d69..c562d49b7 100644 --- a/backend/api/Services/EchoService.cs +++ b/backend/api/Services/EchoService.cs @@ -147,7 +147,9 @@ private List ProcessAvailableEchoMission(List echoPlantInfoResponse var echoPlantInfo = new EchoPlantInfo { - PlantCode = plant.InstallationCode, ProjectDescription = plant.ProjectDescription + PlantCode = plant.InstallationCode, + ProjectDescription = plant.ProjectDescription }; echoPlantInfos.Add(echoPlantInfo); From ec48d41668a080c5e2e82ca87a2bc14fed3418a7 Mon Sep 17 00:00:00 2001 From: aestene Date: Tue, 14 Nov 2023 15:20:05 +0100 Subject: [PATCH 9/9] Add migrations for inspection target --- ...ddInspectionTargetToInspection.Designer.cs | 1312 +++++++++++++++++ ...4141124_AddInspectionTargetToInspection.cs | 133 ++ .../FlotillaDbContextModelSnapshot.cs | 408 ++--- 3 files changed, 1668 insertions(+), 185 deletions(-) create mode 100644 backend/api/Migrations/20231114141124_AddInspectionTargetToInspection.Designer.cs create mode 100644 backend/api/Migrations/20231114141124_AddInspectionTargetToInspection.cs diff --git a/backend/api/Migrations/20231114141124_AddInspectionTargetToInspection.Designer.cs b/backend/api/Migrations/20231114141124_AddInspectionTargetToInspection.Designer.cs new file mode 100644 index 000000000..05c054ff4 --- /dev/null +++ b/backend/api/Migrations/20231114141124_AddInspectionTargetToInspection.Designer.cs @@ -0,0 +1,1312 @@ +// +using System; +using Api.Database.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Api.Migrations +{ + [DbContext(typeof(FlotillaDbContext))] + [Migration("20231114141124_AddInspectionTargetToInspection")] + partial class AddInspectionTargetToInspection + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Api.Database.Models.Area", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("DeckId") + .HasColumnType("text"); + + b.Property("DefaultLocalizationPoseId") + .HasColumnType("text"); + + b.Property("InstallationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PlantId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DeckId"); + + b.HasIndex("DefaultLocalizationPoseId"); + + b.HasIndex("InstallationId"); + + b.HasIndex("PlantId"); + + b.ToTable("Areas"); + }); + + modelBuilder.Entity("Api.Database.Models.Deck", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("DefaultLocalizationPoseId") + .HasColumnType("text"); + + b.Property("InstallationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PlantId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DefaultLocalizationPoseId"); + + b.HasIndex("InstallationId"); + + b.HasIndex("PlantId"); + + b.ToTable("Decks"); + }); + + modelBuilder.Entity("Api.Database.Models.DefaultLocalizationPose", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DefaultLocalizationPoses"); + }); + + modelBuilder.Entity("Api.Database.Models.Inspection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("AnalysisType") + .HasColumnType("text"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("InspectionType") + .IsRequired() + .HasColumnType("text"); + + b.Property("InspectionUrl") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("IsarStepId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("MissionTaskId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("VideoDuration") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("MissionTaskId"); + + b.ToTable("Inspections"); + }); + + modelBuilder.Entity("Api.Database.Models.Installation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("InstallationCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("InstallationCode") + .IsUnique(); + + b.ToTable("Installations"); + }); + + modelBuilder.Entity("Api.Database.Models.MissionDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("AreaId") + .HasColumnType("text"); + + b.Property("Comment") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("InspectionFrequency") + .HasColumnType("bigint"); + + b.Property("InstallationCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsDeprecated") + .HasColumnType("boolean"); + + b.Property("LastSuccessfulRunId") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("SourceId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("AreaId"); + + b.HasIndex("LastSuccessfulRunId"); + + b.HasIndex("SourceId"); + + b.ToTable("MissionDefinitions"); + }); + + modelBuilder.Entity("Api.Database.Models.MissionRun", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("AreaId") + .HasColumnType("text"); + + b.Property("Comment") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Description") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("DesiredStartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("EstimatedDuration") + .HasColumnType("bigint"); + + b.Property("InstallationCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsarMissionId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("MissionId") + .HasColumnType("text"); + + b.Property("MissionRunPriority") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RobotId") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatusReason") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.HasKey("Id"); + + b.HasIndex("AreaId"); + + b.HasIndex("RobotId"); + + b.ToTable("MissionRuns"); + }); + + modelBuilder.Entity("Api.Database.Models.MissionTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("EchoPoseId") + .HasColumnType("integer"); + + b.Property("EchoTagLink") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("IsarTaskId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("MissionRunId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("TagId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("TaskOrder") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MissionRunId"); + + b.ToTable("MissionTasks"); + }); + + modelBuilder.Entity("Api.Database.Models.Plant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("InstallationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PlantCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.HasKey("Id"); + + b.HasIndex("InstallationId"); + + b.HasIndex("PlantCode") + .IsUnique(); + + b.ToTable("Plants"); + }); + + modelBuilder.Entity("Api.Database.Models.Robot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("BatteryLevel") + .HasColumnType("real"); + + b.Property("CurrentAreaId") + .HasColumnType("text"); + + b.Property("CurrentInstallation") + .IsRequired() + .HasColumnType("text"); + + b.Property("CurrentMissionId") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Host") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsarId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("MissionQueueFrozen") + .HasColumnType("boolean"); + + b.Property("ModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Port") + .HasColumnType("integer"); + + b.Property("PressureLevel") + .HasColumnType("real"); + + b.Property("SerialNumber") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CurrentAreaId"); + + b.HasIndex("ModelId"); + + b.ToTable("Robots"); + }); + + modelBuilder.Entity("Api.Database.Models.RobotBatteryTimeseries", b => + { + b.Property("BatteryLevel") + .HasColumnType("real"); + + b.Property("MissionId") + .HasColumnType("text"); + + b.Property("RobotId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Time") + .HasColumnType("timestamp with time zone"); + + b.ToTable("RobotBatteryTimeseries"); + }); + + modelBuilder.Entity("Api.Database.Models.RobotModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("AverageDurationPerTag") + .HasColumnType("real"); + + b.Property("BatteryWarningThreshold") + .HasColumnType("real"); + + b.Property("LowerPressureWarningThreshold") + .HasColumnType("real"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpperPressureWarningThreshold") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("Type") + .IsUnique(); + + b.ToTable("RobotModels"); + }); + + modelBuilder.Entity("Api.Database.Models.RobotPoseTimeseries", b => + { + b.Property("MissionId") + .HasColumnType("text"); + + b.Property("OrientationW") + .HasColumnType("real"); + + b.Property("OrientationX") + .HasColumnType("real"); + + b.Property("OrientationY") + .HasColumnType("real"); + + b.Property("OrientationZ") + .HasColumnType("real"); + + b.Property("PositionX") + .HasColumnType("real"); + + b.Property("PositionY") + .HasColumnType("real"); + + b.Property("PositionZ") + .HasColumnType("real"); + + b.Property("RobotId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Time") + .HasColumnType("timestamp with time zone"); + + b.ToTable("RobotPoseTimeseries"); + }); + + modelBuilder.Entity("Api.Database.Models.RobotPressureTimeseries", b => + { + b.Property("MissionId") + .HasColumnType("text"); + + b.Property("Pressure") + .HasColumnType("real"); + + b.Property("RobotId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Time") + .HasColumnType("timestamp with time zone"); + + b.ToTable("RobotPressureTimeseries"); + }); + + modelBuilder.Entity("Api.Database.Models.SafePosition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("AreaId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("AreaId"); + + b.ToTable("SafePositions"); + }); + + modelBuilder.Entity("Api.Database.Models.Source", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("SourceId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Sources"); + }); + + modelBuilder.Entity("Api.Database.Models.Area", b => + { + b.HasOne("Api.Database.Models.Deck", "Deck") + .WithMany() + .HasForeignKey("DeckId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Api.Database.Models.DefaultLocalizationPose", "DefaultLocalizationPose") + .WithMany() + .HasForeignKey("DefaultLocalizationPoseId"); + + b.HasOne("Api.Database.Models.Installation", "Installation") + .WithMany() + .HasForeignKey("InstallationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Api.Database.Models.Plant", "Plant") + .WithMany() + .HasForeignKey("PlantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.OwnsOne("Api.Database.Models.MapMetadata", "MapMetadata", b1 => + { + b1.Property("AreaId") + .HasColumnType("text"); + + b1.Property("MapName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b1.HasKey("AreaId"); + + b1.ToTable("Areas"); + + b1.WithOwner() + .HasForeignKey("AreaId"); + + b1.OwnsOne("Api.Database.Models.Boundary", "Boundary", b2 => + { + b2.Property("MapMetadataAreaId") + .HasColumnType("text"); + + b2.Property("X1") + .HasColumnType("double precision"); + + b2.Property("X2") + .HasColumnType("double precision"); + + b2.Property("Y1") + .HasColumnType("double precision"); + + b2.Property("Y2") + .HasColumnType("double precision"); + + b2.Property("Z1") + .HasColumnType("double precision"); + + b2.Property("Z2") + .HasColumnType("double precision"); + + b2.HasKey("MapMetadataAreaId"); + + b2.ToTable("Areas"); + + b2.WithOwner() + .HasForeignKey("MapMetadataAreaId"); + }); + + b1.OwnsOne("Api.Database.Models.TransformationMatrices", "TransformationMatrices", b2 => + { + b2.Property("MapMetadataAreaId") + .HasColumnType("text"); + + b2.Property("C1") + .HasColumnType("double precision"); + + b2.Property("C2") + .HasColumnType("double precision"); + + b2.Property("D1") + .HasColumnType("double precision"); + + b2.Property("D2") + .HasColumnType("double precision"); + + b2.HasKey("MapMetadataAreaId"); + + b2.ToTable("Areas"); + + b2.WithOwner() + .HasForeignKey("MapMetadataAreaId"); + }); + + b1.Navigation("Boundary") + .IsRequired(); + + b1.Navigation("TransformationMatrices") + .IsRequired(); + }); + + b.Navigation("Deck"); + + b.Navigation("DefaultLocalizationPose"); + + b.Navigation("Installation"); + + b.Navigation("MapMetadata") + .IsRequired(); + + b.Navigation("Plant"); + }); + + modelBuilder.Entity("Api.Database.Models.Deck", b => + { + b.HasOne("Api.Database.Models.DefaultLocalizationPose", "DefaultLocalizationPose") + .WithMany() + .HasForeignKey("DefaultLocalizationPoseId"); + + b.HasOne("Api.Database.Models.Installation", "Installation") + .WithMany() + .HasForeignKey("InstallationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Api.Database.Models.Plant", "Plant") + .WithMany() + .HasForeignKey("PlantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DefaultLocalizationPose"); + + b.Navigation("Installation"); + + b.Navigation("Plant"); + }); + + modelBuilder.Entity("Api.Database.Models.DefaultLocalizationPose", b => + { + b.OwnsOne("Api.Database.Models.Pose", "Pose", b1 => + { + b1.Property("DefaultLocalizationPoseId") + .HasColumnType("text"); + + b1.HasKey("DefaultLocalizationPoseId"); + + b1.ToTable("DefaultLocalizationPoses"); + + b1.WithOwner() + .HasForeignKey("DefaultLocalizationPoseId"); + + b1.OwnsOne("Api.Database.Models.Orientation", "Orientation", b2 => + { + b2.Property("PoseDefaultLocalizationPoseId") + .HasColumnType("text"); + + b2.Property("W") + .HasColumnType("real"); + + b2.Property("X") + .HasColumnType("real"); + + b2.Property("Y") + .HasColumnType("real"); + + b2.Property("Z") + .HasColumnType("real"); + + b2.HasKey("PoseDefaultLocalizationPoseId"); + + b2.ToTable("DefaultLocalizationPoses"); + + b2.WithOwner() + .HasForeignKey("PoseDefaultLocalizationPoseId"); + }); + + b1.OwnsOne("Api.Database.Models.Position", "Position", b2 => + { + b2.Property("PoseDefaultLocalizationPoseId") + .HasColumnType("text"); + + b2.Property("X") + .HasColumnType("real"); + + b2.Property("Y") + .HasColumnType("real"); + + b2.Property("Z") + .HasColumnType("real"); + + b2.HasKey("PoseDefaultLocalizationPoseId"); + + b2.ToTable("DefaultLocalizationPoses"); + + b2.WithOwner() + .HasForeignKey("PoseDefaultLocalizationPoseId"); + }); + + b1.Navigation("Orientation") + .IsRequired(); + + b1.Navigation("Position") + .IsRequired(); + }); + + b.Navigation("Pose") + .IsRequired(); + }); + + modelBuilder.Entity("Api.Database.Models.Inspection", b => + { + b.HasOne("Api.Database.Models.MissionTask", null) + .WithMany("Inspections") + .HasForeignKey("MissionTaskId"); + + b.OwnsOne("Api.Database.Models.Position", "InspectionTarget", b1 => + { + b1.Property("InspectionId") + .HasColumnType("text"); + + b1.Property("X") + .HasColumnType("real"); + + b1.Property("Y") + .HasColumnType("real"); + + b1.Property("Z") + .HasColumnType("real"); + + b1.HasKey("InspectionId"); + + b1.ToTable("Inspections"); + + b1.WithOwner() + .HasForeignKey("InspectionId"); + }); + + b.OwnsMany("Api.Database.Models.InspectionFindings", "InspectionFindings", b1 => + { + b1.Property("InspectionId") + .HasColumnType("text"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Area") + .IsRequired() + .HasColumnType("text"); + + b1.Property("FindingsTag") + .IsRequired() + .HasColumnType("text"); + + b1.Property("InspectionDate") + .IsRequired() + .HasColumnType("text"); + + b1.Property("RobotName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("InspectionId", "Id"); + + b1.ToTable("InspectionFindings"); + + b1.WithOwner() + .HasForeignKey("InspectionId"); + }); + + b.Navigation("InspectionFindings"); + + b.Navigation("InspectionTarget") + .IsRequired(); + }); + + modelBuilder.Entity("Api.Database.Models.MissionDefinition", b => + { + b.HasOne("Api.Database.Models.Area", "Area") + .WithMany() + .HasForeignKey("AreaId"); + + b.HasOne("Api.Database.Models.MissionRun", "LastSuccessfulRun") + .WithMany() + .HasForeignKey("LastSuccessfulRunId"); + + b.HasOne("Api.Database.Models.Source", "Source") + .WithMany() + .HasForeignKey("SourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Area"); + + b.Navigation("LastSuccessfulRun"); + + b.Navigation("Source"); + }); + + modelBuilder.Entity("Api.Database.Models.MissionRun", b => + { + b.HasOne("Api.Database.Models.Area", "Area") + .WithMany() + .HasForeignKey("AreaId"); + + b.HasOne("Api.Database.Models.Robot", "Robot") + .WithMany() + .HasForeignKey("RobotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Api.Database.Models.MapMetadata", "Map", b1 => + { + b1.Property("MissionRunId") + .HasColumnType("text"); + + b1.Property("MapName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b1.HasKey("MissionRunId"); + + b1.ToTable("MissionRuns"); + + b1.WithOwner() + .HasForeignKey("MissionRunId"); + + b1.OwnsOne("Api.Database.Models.Boundary", "Boundary", b2 => + { + b2.Property("MapMetadataMissionRunId") + .HasColumnType("text"); + + b2.Property("X1") + .HasColumnType("double precision"); + + b2.Property("X2") + .HasColumnType("double precision"); + + b2.Property("Y1") + .HasColumnType("double precision"); + + b2.Property("Y2") + .HasColumnType("double precision"); + + b2.Property("Z1") + .HasColumnType("double precision"); + + b2.Property("Z2") + .HasColumnType("double precision"); + + b2.HasKey("MapMetadataMissionRunId"); + + b2.ToTable("MissionRuns"); + + b2.WithOwner() + .HasForeignKey("MapMetadataMissionRunId"); + }); + + b1.OwnsOne("Api.Database.Models.TransformationMatrices", "TransformationMatrices", b2 => + { + b2.Property("MapMetadataMissionRunId") + .HasColumnType("text"); + + b2.Property("C1") + .HasColumnType("double precision"); + + b2.Property("C2") + .HasColumnType("double precision"); + + b2.Property("D1") + .HasColumnType("double precision"); + + b2.Property("D2") + .HasColumnType("double precision"); + + b2.HasKey("MapMetadataMissionRunId"); + + b2.ToTable("MissionRuns"); + + b2.WithOwner() + .HasForeignKey("MapMetadataMissionRunId"); + }); + + b1.Navigation("Boundary") + .IsRequired(); + + b1.Navigation("TransformationMatrices") + .IsRequired(); + }); + + b.Navigation("Area"); + + b.Navigation("Map"); + + b.Navigation("Robot"); + }); + + modelBuilder.Entity("Api.Database.Models.MissionTask", b => + { + b.HasOne("Api.Database.Models.MissionRun", null) + .WithMany("Tasks") + .HasForeignKey("MissionRunId"); + + b.OwnsOne("Api.Database.Models.Position", "InspectionTarget", b1 => + { + b1.Property("MissionTaskId") + .HasColumnType("text"); + + b1.Property("X") + .HasColumnType("real"); + + b1.Property("Y") + .HasColumnType("real"); + + b1.Property("Z") + .HasColumnType("real"); + + b1.HasKey("MissionTaskId"); + + b1.ToTable("MissionTasks"); + + b1.WithOwner() + .HasForeignKey("MissionTaskId"); + }); + + b.OwnsOne("Api.Database.Models.Pose", "RobotPose", b1 => + { + b1.Property("MissionTaskId") + .HasColumnType("text"); + + b1.HasKey("MissionTaskId"); + + b1.ToTable("MissionTasks"); + + b1.WithOwner() + .HasForeignKey("MissionTaskId"); + + b1.OwnsOne("Api.Database.Models.Orientation", "Orientation", b2 => + { + b2.Property("PoseMissionTaskId") + .HasColumnType("text"); + + b2.Property("W") + .HasColumnType("real"); + + b2.Property("X") + .HasColumnType("real"); + + b2.Property("Y") + .HasColumnType("real"); + + b2.Property("Z") + .HasColumnType("real"); + + b2.HasKey("PoseMissionTaskId"); + + b2.ToTable("MissionTasks"); + + b2.WithOwner() + .HasForeignKey("PoseMissionTaskId"); + }); + + b1.OwnsOne("Api.Database.Models.Position", "Position", b2 => + { + b2.Property("PoseMissionTaskId") + .HasColumnType("text"); + + b2.Property("X") + .HasColumnType("real"); + + b2.Property("Y") + .HasColumnType("real"); + + b2.Property("Z") + .HasColumnType("real"); + + b2.HasKey("PoseMissionTaskId"); + + b2.ToTable("MissionTasks"); + + b2.WithOwner() + .HasForeignKey("PoseMissionTaskId"); + }); + + b1.Navigation("Orientation") + .IsRequired(); + + b1.Navigation("Position") + .IsRequired(); + }); + + b.Navigation("InspectionTarget") + .IsRequired(); + + b.Navigation("RobotPose") + .IsRequired(); + }); + + modelBuilder.Entity("Api.Database.Models.Plant", b => + { + b.HasOne("Api.Database.Models.Installation", "Installation") + .WithMany() + .HasForeignKey("InstallationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Installation"); + }); + + modelBuilder.Entity("Api.Database.Models.Robot", b => + { + b.HasOne("Api.Database.Models.Area", "CurrentArea") + .WithMany() + .HasForeignKey("CurrentAreaId"); + + b.HasOne("Api.Database.Models.RobotModel", "Model") + .WithMany() + .HasForeignKey("ModelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Api.Database.Models.Pose", "Pose", b1 => + { + b1.Property("RobotId") + .HasColumnType("text"); + + b1.HasKey("RobotId"); + + b1.ToTable("Robots"); + + b1.WithOwner() + .HasForeignKey("RobotId"); + + b1.OwnsOne("Api.Database.Models.Orientation", "Orientation", b2 => + { + b2.Property("PoseRobotId") + .HasColumnType("text"); + + b2.Property("W") + .HasColumnType("real"); + + b2.Property("X") + .HasColumnType("real"); + + b2.Property("Y") + .HasColumnType("real"); + + b2.Property("Z") + .HasColumnType("real"); + + b2.HasKey("PoseRobotId"); + + b2.ToTable("Robots"); + + b2.WithOwner() + .HasForeignKey("PoseRobotId"); + }); + + b1.OwnsOne("Api.Database.Models.Position", "Position", b2 => + { + b2.Property("PoseRobotId") + .HasColumnType("text"); + + b2.Property("X") + .HasColumnType("real"); + + b2.Property("Y") + .HasColumnType("real"); + + b2.Property("Z") + .HasColumnType("real"); + + b2.HasKey("PoseRobotId"); + + b2.ToTable("Robots"); + + b2.WithOwner() + .HasForeignKey("PoseRobotId"); + }); + + b1.Navigation("Orientation") + .IsRequired(); + + b1.Navigation("Position") + .IsRequired(); + }); + + b.OwnsMany("Api.Database.Models.VideoStream", "VideoStreams", b1 => + { + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b1.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b1.Property("RobotId") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ShouldRotate270Clockwise") + .HasColumnType("boolean"); + + b1.Property("Type") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b1.Property("Url") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b1.HasKey("Id"); + + b1.HasIndex("RobotId"); + + b1.ToTable("VideoStream"); + + b1.WithOwner() + .HasForeignKey("RobotId"); + }); + + b.Navigation("CurrentArea"); + + b.Navigation("Model"); + + b.Navigation("Pose") + .IsRequired(); + + b.Navigation("VideoStreams"); + }); + + modelBuilder.Entity("Api.Database.Models.SafePosition", b => + { + b.HasOne("Api.Database.Models.Area", null) + .WithMany("SafePositions") + .HasForeignKey("AreaId"); + + b.OwnsOne("Api.Database.Models.Pose", "Pose", b1 => + { + b1.Property("SafePositionId") + .HasColumnType("text"); + + b1.HasKey("SafePositionId"); + + b1.ToTable("SafePositions"); + + b1.WithOwner() + .HasForeignKey("SafePositionId"); + + b1.OwnsOne("Api.Database.Models.Orientation", "Orientation", b2 => + { + b2.Property("PoseSafePositionId") + .HasColumnType("text"); + + b2.Property("W") + .HasColumnType("real"); + + b2.Property("X") + .HasColumnType("real"); + + b2.Property("Y") + .HasColumnType("real"); + + b2.Property("Z") + .HasColumnType("real"); + + b2.HasKey("PoseSafePositionId"); + + b2.ToTable("SafePositions"); + + b2.WithOwner() + .HasForeignKey("PoseSafePositionId"); + }); + + b1.OwnsOne("Api.Database.Models.Position", "Position", b2 => + { + b2.Property("PoseSafePositionId") + .HasColumnType("text"); + + b2.Property("X") + .HasColumnType("real"); + + b2.Property("Y") + .HasColumnType("real"); + + b2.Property("Z") + .HasColumnType("real"); + + b2.HasKey("PoseSafePositionId"); + + b2.ToTable("SafePositions"); + + b2.WithOwner() + .HasForeignKey("PoseSafePositionId"); + }); + + b1.Navigation("Orientation") + .IsRequired(); + + b1.Navigation("Position") + .IsRequired(); + }); + + b.Navigation("Pose") + .IsRequired(); + }); + + modelBuilder.Entity("Api.Database.Models.Area", b => + { + b.Navigation("SafePositions"); + }); + + modelBuilder.Entity("Api.Database.Models.MissionRun", b => + { + b.Navigation("Tasks"); + }); + + modelBuilder.Entity("Api.Database.Models.MissionTask", b => + { + b.Navigation("Inspections"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/api/Migrations/20231114141124_AddInspectionTargetToInspection.cs b/backend/api/Migrations/20231114141124_AddInspectionTargetToInspection.cs new file mode 100644 index 000000000..f2761f87e --- /dev/null +++ b/backend/api/Migrations/20231114141124_AddInspectionTargetToInspection.cs @@ -0,0 +1,133 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Migrations +{ + /// + public partial class AddInspectionTargetToInspection : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Inspections_MissionTasks_MissionTaskId", + table: "Inspections"); + + migrationBuilder.DropForeignKey( + name: "FK_MissionTasks_MissionRuns_MissionRunId", + table: "MissionTasks"); + + migrationBuilder.AlterColumn( + name: "MissionRunId", + table: "MissionTasks", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AlterColumn( + name: "MissionTaskId", + table: "Inspections", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AddColumn( + name: "InspectionTarget_X", + table: "Inspections", + type: "real", + nullable: false, + defaultValue: 0f); + + migrationBuilder.AddColumn( + name: "InspectionTarget_Y", + table: "Inspections", + type: "real", + nullable: false, + defaultValue: 0f); + + migrationBuilder.AddColumn( + name: "InspectionTarget_Z", + table: "Inspections", + type: "real", + nullable: false, + defaultValue: 0f); + + migrationBuilder.AddForeignKey( + name: "FK_Inspections_MissionTasks_MissionTaskId", + table: "Inspections", + column: "MissionTaskId", + principalTable: "MissionTasks", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_MissionTasks_MissionRuns_MissionRunId", + table: "MissionTasks", + column: "MissionRunId", + principalTable: "MissionRuns", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Inspections_MissionTasks_MissionTaskId", + table: "Inspections"); + + migrationBuilder.DropForeignKey( + name: "FK_MissionTasks_MissionRuns_MissionRunId", + table: "MissionTasks"); + + migrationBuilder.DropColumn( + name: "InspectionTarget_X", + table: "Inspections"); + + migrationBuilder.DropColumn( + name: "InspectionTarget_Y", + table: "Inspections"); + + migrationBuilder.DropColumn( + name: "InspectionTarget_Z", + table: "Inspections"); + + migrationBuilder.AlterColumn( + name: "MissionRunId", + table: "MissionTasks", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "MissionTaskId", + table: "Inspections", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Inspections_MissionTasks_MissionTaskId", + table: "Inspections", + column: "MissionTaskId", + principalTable: "MissionTasks", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_MissionTasks_MissionRuns_MissionRunId", + table: "MissionTasks", + column: "MissionRunId", + principalTable: "MissionRuns", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs b/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs index 67b9f092d..6d1c2c68c 100644 --- a/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs +++ b/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs @@ -104,6 +104,50 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("DefaultLocalizationPoses"); }); + modelBuilder.Entity("Api.Database.Models.Inspection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("AnalysisType") + .HasColumnType("text"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("InspectionType") + .IsRequired() + .HasColumnType("text"); + + b.Property("InspectionUrl") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("IsarStepId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("MissionTaskId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("VideoDuration") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("MissionTaskId"); + + b.ToTable("Inspections"); + }); + modelBuilder.Entity("Api.Database.Models.Installation", b => { b.Property("Id") @@ -245,6 +289,54 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("MissionRuns"); }); + modelBuilder.Entity("Api.Database.Models.MissionTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("EchoPoseId") + .HasColumnType("integer"); + + b.Property("EchoTagLink") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("IsarTaskId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("MissionRunId") + .HasColumnType("text"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("TagId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("TaskOrder") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MissionRunId"); + + b.ToTable("MissionTasks"); + }); + modelBuilder.Entity("Api.Database.Models.Plant", b => { b.Property("Id") @@ -692,6 +784,75 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("Api.Database.Models.Inspection", b => + { + b.HasOne("Api.Database.Models.MissionTask", null) + .WithMany("Inspections") + .HasForeignKey("MissionTaskId"); + + b.OwnsOne("Api.Database.Models.Position", "InspectionTarget", b1 => + { + b1.Property("InspectionId") + .HasColumnType("text"); + + b1.Property("X") + .HasColumnType("real"); + + b1.Property("Y") + .HasColumnType("real"); + + b1.Property("Z") + .HasColumnType("real"); + + b1.HasKey("InspectionId"); + + b1.ToTable("Inspections"); + + b1.WithOwner() + .HasForeignKey("InspectionId"); + }); + + b.OwnsMany("Api.Database.Models.InspectionFindings", "InspectionFindings", b1 => + { + b1.Property("InspectionId") + .HasColumnType("text"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Area") + .IsRequired() + .HasColumnType("text"); + + b1.Property("FindingsTag") + .IsRequired() + .HasColumnType("text"); + + b1.Property("InspectionDate") + .IsRequired() + .HasColumnType("text"); + + b1.Property("RobotName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("InspectionId", "Id"); + + b1.ToTable("InspectionFindings"); + + b1.WithOwner() + .HasForeignKey("InspectionId"); + }); + + b.Navigation("InspectionFindings"); + + b.Navigation("InspectionTarget") + .IsRequired(); + }); + modelBuilder.Entity("Api.Database.Models.MissionDefinition", b => { b.HasOne("Api.Database.Models.Area", "Area") @@ -807,144 +968,81 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); - b.OwnsMany("Api.Database.Models.MissionTask", "Tasks", b1 => - { - b1.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("text"); - - b1.Property("Description") - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b1.Property("EchoPoseId") - .HasColumnType("integer"); - - b1.Property("EchoTagLink") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b1.Property("EndTime") - .HasColumnType("timestamp with time zone"); + b.Navigation("Area"); - b1.Property("IsarTaskId") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); + b.Navigation("Map"); - b1.Property("MissionRunId") - .HasColumnType("text"); + b.Navigation("Robot"); + }); - b1.Property("StartTime") - .HasColumnType("timestamp with time zone"); + modelBuilder.Entity("Api.Database.Models.MissionTask", b => + { + b.HasOne("Api.Database.Models.MissionRun", null) + .WithMany("Tasks") + .HasForeignKey("MissionRunId"); - b1.Property("Status") - .IsRequired() + b.OwnsOne("Api.Database.Models.Position", "InspectionTarget", b1 => + { + b1.Property("MissionTaskId") .HasColumnType("text"); - b1.Property("TagId") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); + b1.Property("X") + .HasColumnType("real"); - b1.Property("TaskOrder") - .HasColumnType("integer"); + b1.Property("Y") + .HasColumnType("real"); - b1.HasKey("Id"); + b1.Property("Z") + .HasColumnType("real"); - b1.HasIndex("MissionRunId"); + b1.HasKey("MissionTaskId"); b1.ToTable("MissionTasks"); b1.WithOwner() - .HasForeignKey("MissionRunId"); - - b1.OwnsMany("Api.Database.Models.Inspection", "Inspections", b2 => - { - b2.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("text"); - - b2.Property("AnalysisType") - .HasColumnType("text"); + .HasForeignKey("MissionTaskId"); + }); - b2.Property("EndTime") - .HasColumnType("timestamp with time zone"); + b.OwnsOne("Api.Database.Models.Pose", "RobotPose", b1 => + { + b1.Property("MissionTaskId") + .HasColumnType("text"); - b2.Property("InspectionType") - .IsRequired() - .HasColumnType("text"); + b1.HasKey("MissionTaskId"); - b2.Property("InspectionUrl") - .HasMaxLength(250) - .HasColumnType("character varying(250)"); + b1.ToTable("MissionTasks"); - b2.Property("IsarStepId") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); + b1.WithOwner() + .HasForeignKey("MissionTaskId"); - b2.Property("MissionTaskId") - .IsRequired() + b1.OwnsOne("Api.Database.Models.Orientation", "Orientation", b2 => + { + b2.Property("PoseMissionTaskId") .HasColumnType("text"); - b2.Property("StartTime") - .HasColumnType("timestamp with time zone"); + b2.Property("W") + .HasColumnType("real"); - b2.Property("Status") - .IsRequired() - .HasColumnType("text"); + b2.Property("X") + .HasColumnType("real"); - b2.Property("VideoDuration") + b2.Property("Y") .HasColumnType("real"); - b2.HasKey("Id"); + b2.Property("Z") + .HasColumnType("real"); - b2.HasIndex("MissionTaskId"); + b2.HasKey("PoseMissionTaskId"); - b2.ToTable("Inspections"); + b2.ToTable("MissionTasks"); b2.WithOwner() - .HasForeignKey("MissionTaskId"); - - b2.OwnsMany("Api.Database.Models.InspectionFindings", "InspectionFindings", b3 => - { - b3.Property("InspectionId") - .HasColumnType("text"); - - b3.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b3.Property("Id")); - - b3.Property("Area") - .IsRequired() - .HasColumnType("text"); - - b3.Property("FindingsTag") - .IsRequired() - .HasColumnType("text"); - - b3.Property("InspectionDate") - .IsRequired() - .HasColumnType("text"); - - b3.Property("RobotName") - .IsRequired() - .HasColumnType("text"); - - b3.HasKey("InspectionId", "Id"); - - b3.ToTable("InspectionFindings"); - - b3.WithOwner() - .HasForeignKey("InspectionId"); - }); - - b2.Navigation("InspectionFindings"); + .HasForeignKey("PoseMissionTaskId"); }); - b1.OwnsOne("Api.Database.Models.Position", "InspectionTarget", b2 => + b1.OwnsOne("Api.Database.Models.Position", "Position", b2 => { - b2.Property("MissionTaskId") + b2.Property("PoseMissionTaskId") .HasColumnType("text"); b2.Property("X") @@ -956,96 +1054,26 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Z") .HasColumnType("real"); - b2.HasKey("MissionTaskId"); + b2.HasKey("PoseMissionTaskId"); b2.ToTable("MissionTasks"); b2.WithOwner() - .HasForeignKey("MissionTaskId"); - }); - - b1.OwnsOne("Api.Database.Models.Pose", "RobotPose", b2 => - { - b2.Property("MissionTaskId") - .HasColumnType("text"); - - b2.HasKey("MissionTaskId"); - - b2.ToTable("MissionTasks"); - - b2.WithOwner() - .HasForeignKey("MissionTaskId"); - - b2.OwnsOne("Api.Database.Models.Orientation", "Orientation", b3 => - { - b3.Property("PoseMissionTaskId") - .HasColumnType("text"); - - b3.Property("W") - .HasColumnType("real"); - - b3.Property("X") - .HasColumnType("real"); - - b3.Property("Y") - .HasColumnType("real"); - - b3.Property("Z") - .HasColumnType("real"); - - b3.HasKey("PoseMissionTaskId"); - - b3.ToTable("MissionTasks"); - - b3.WithOwner() - .HasForeignKey("PoseMissionTaskId"); - }); - - b2.OwnsOne("Api.Database.Models.Position", "Position", b3 => - { - b3.Property("PoseMissionTaskId") - .HasColumnType("text"); - - b3.Property("X") - .HasColumnType("real"); - - b3.Property("Y") - .HasColumnType("real"); - - b3.Property("Z") - .HasColumnType("real"); - - b3.HasKey("PoseMissionTaskId"); - - b3.ToTable("MissionTasks"); - - b3.WithOwner() - .HasForeignKey("PoseMissionTaskId"); - }); - - b2.Navigation("Orientation") - .IsRequired(); - - b2.Navigation("Position") - .IsRequired(); + .HasForeignKey("PoseMissionTaskId"); }); - b1.Navigation("InspectionTarget") + b1.Navigation("Orientation") .IsRequired(); - b1.Navigation("Inspections"); - - b1.Navigation("RobotPose") + b1.Navigation("Position") .IsRequired(); }); - b.Navigation("Area"); - - b.Navigation("Map"); - - b.Navigation("Robot"); + b.Navigation("InspectionTarget") + .IsRequired(); - b.Navigation("Tasks"); + b.Navigation("RobotPose") + .IsRequired(); }); modelBuilder.Entity("Api.Database.Models.Plant", b => @@ -1265,6 +1293,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Navigation("SafePositions"); }); + + modelBuilder.Entity("Api.Database.Models.MissionRun", b => + { + b.Navigation("Tasks"); + }); + + modelBuilder.Entity("Api.Database.Models.MissionTask", b => + { + b.Navigation("Inspections"); + }); #pragma warning restore 612, 618 } }