diff --git a/backend/api/Database/Models/MissionRun.cs b/backend/api/Database/Models/MissionRun.cs
index 67f215874..477d893c4 100644
--- a/backend/api/Database/Models/MissionRun.cs
+++ b/backend/api/Database/Models/MissionRun.cs
@@ -95,6 +95,8 @@ or MissionStatus.PartiallySuccessful
[MaxLength(200)]
public string Name { get; set; }
+ public bool IsDeprecated { get; set; }
+
public void UpdateWithIsarInfo(IsarMission isarMission)
{
IsarMissionId = isarMission.IsarMissionId;
diff --git a/backend/api/EventHandlers/MissionEventHandler.cs b/backend/api/EventHandlers/MissionEventHandler.cs
index d2146c7d4..374c3765a 100644
--- a/backend/api/EventHandlers/MissionEventHandler.cs
+++ b/backend/api/EventHandlers/MissionEventHandler.cs
@@ -333,7 +333,7 @@ public async Task CancelReturnToHomeOnNewMissionSchedule(MissionRun missionRun)
missionTask.Status = Database.Models.TaskStatus.Cancelled;
}
returnToHomeMission.Status = MissionStatus.Cancelled;
- await MissionService.Update(returnToHomeMission);
+ await MissionService.UpdateMissionRunProperty(returnToHomeMission.Id, "Status", MissionStatus.Cancelled);
}
if (existingReturnToHomeMissions.Count > 1)
diff --git a/backend/api/Migrations/20240717092121_AddMissionRunIsDeprecated.Designer.cs b/backend/api/Migrations/20240717092121_AddMissionRunIsDeprecated.Designer.cs
new file mode 100644
index 000000000..0e3bdca26
--- /dev/null
+++ b/backend/api/Migrations/20240717092121_AddMissionRunIsDeprecated.Designer.cs
@@ -0,0 +1,1357 @@
+//
+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("20240717092121_AddMissionRunIsDeprecated")]
+ partial class AddMissionRunIsDeprecated
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Api.Database.Models.AccessRole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("text");
+
+ b.Property("AccessLevel")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("InstallationId")
+ .HasColumnType("text");
+
+ b.Property("RoleName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstallationId");
+
+ b.ToTable("AccessRoles");
+ });
+
+ modelBuilder.Entity("Api.Database.Models.Area", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("text");
+
+ b.Property("DeckId")
+ .IsRequired()
+ .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.Property("DockingEnabled")
+ .HasColumnType("boolean");
+
+ 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")
+ .IsRequired()
+ .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.InspectionFinding", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("text");
+
+ b.Property("Finding")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("InspectionDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("InspectionId")
+ .HasColumnType("text");
+
+ b.Property("IsarStepId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InspectionId");
+
+ b.ToTable("InspectionFindings");
+ });
+
+ 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")
+ .IsRequired()
+ .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")
+ .IsRequired()
+ .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("IsDeprecated")
+ .HasColumnType("boolean");
+
+ b.Property("IsarMissionId")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("MissionId")
+ .HasColumnType("text");
+
+ b.Property("MissionRunType")
+ .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.Property("Type")
+ .IsRequired()
+ .HasColumnType("text");
+
+ 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("CurrentInstallationId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CurrentMissionId")
+ .HasColumnType("text");
+
+ b.Property("Deprecated")
+ .HasColumnType("boolean");
+
+ b.Property("Host")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("IsarConnected")
+ .HasColumnType("boolean");
+
+ 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("RobotCapabilities")
+ .HasColumnType("text");
+
+ b.Property("SerialNumber")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CurrentAreaId");
+
+ b.HasIndex("CurrentInstallationId");
+
+ 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.AccessRole", b =>
+ {
+ b.HasOne("Api.Database.Models.Installation", "Installation")
+ .WithMany()
+ .HasForeignKey("InstallationId");
+
+ b.Navigation("Installation");
+ });
+
+ modelBuilder.Entity("Api.Database.Models.Area", b =>
+ {
+ b.HasOne("Api.Database.Models.Deck", "Deck")
+ .WithMany()
+ .HasForeignKey("DeckId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ 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.Navigation("InspectionTarget")
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Api.Database.Models.InspectionFinding", b =>
+ {
+ b.HasOne("Api.Database.Models.Inspection", null)
+ .WithMany("InspectionFindings")
+ .HasForeignKey("InspectionId");
+ });
+
+ modelBuilder.Entity("Api.Database.Models.MissionDefinition", b =>
+ {
+ b.HasOne("Api.Database.Models.Area", "Area")
+ .WithMany()
+ .HasForeignKey("AreaId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ 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")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ 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.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("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.Installation", "CurrentInstallation")
+ .WithMany()
+ .HasForeignKey("CurrentInstallationId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ 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("CurrentInstallation");
+
+ 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.Inspection", b =>
+ {
+ b.Navigation("InspectionFindings");
+ });
+
+ 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/20240717092121_AddMissionRunIsDeprecated.cs b/backend/api/Migrations/20240717092121_AddMissionRunIsDeprecated.cs
new file mode 100644
index 000000000..f2c392bb1
--- /dev/null
+++ b/backend/api/Migrations/20240717092121_AddMissionRunIsDeprecated.cs
@@ -0,0 +1,29 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Api.Migrations
+{
+ ///
+ public partial class AddMissionRunIsDeprecated : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "IsDeprecated",
+ table: "MissionRuns",
+ type: "boolean",
+ nullable: false,
+ defaultValue: false);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "IsDeprecated",
+ table: "MissionRuns");
+ }
+ }
+}
diff --git a/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs b/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs
index 15c54359d..d5f381302 100644
--- a/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs
+++ b/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs
@@ -307,6 +307,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasMaxLength(200)
.HasColumnType("character varying(200)");
+ b.Property("IsDeprecated")
+ .HasColumnType("boolean");
+
b.Property("IsarMissionId")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
diff --git a/backend/api/Services/MissionRunService.cs b/backend/api/Services/MissionRunService.cs
index 42a129207..41ac44e79 100644
--- a/backend/api/Services/MissionRunService.cs
+++ b/backend/api/Services/MissionRunService.cs
@@ -54,6 +54,8 @@ MissionStatus missionStatus
);
public Task Delete(string id);
public Task OngoingMission(string robotId);
+ public Task UpdateMissionRunProperty(string missionRunId, string propertyName, object? value);
+
}
[SuppressMessage(
@@ -282,9 +284,7 @@ public async Task Update(MissionRun missionRun)
return null;
}
- context.Entry(missionRun.Status).State = EntityState.Unchanged;
- context.MissionRuns.Remove(missionRun);
- await ApplyDatabaseUpdate(missionRun.Area?.Installation);
+ await UpdateMissionRunProperty(missionRun.Id, "IsDeprecated", true);
_ = signalRService.SendMessageAsync("Mission run deleted", missionRun?.Area?.Installation, missionRun != null ? new MissionRunResponse(missionRun) : null);
return missionRun;
@@ -325,7 +325,8 @@ private IQueryable GetMissionRunsWithSubModels()
.Include(missionRun => missionRun.Tasks)
.ThenInclude(task => task.Inspections)
.ThenInclude(inspections => inspections.InspectionFindings)
- .Where((m) => m.Area == null || accessibleInstallationCodes.Result.Contains(m.Area.Installation.InstallationCode.ToUpper())); ;
+ .Where((m) => m.Area == null || accessibleInstallationCodes.Result.Contains(m.Area.Installation.InstallationCode.ToUpper()))
+ .Where((m) => m.IsDeprecated == false); ;
}
protected virtual void OnMissionRunCreated(MissionRunCreatedEventArgs e)
@@ -546,9 +547,7 @@ public async Task UpdateMissionRunType(string missionRunId, MissionR
throw new MissionRunNotFoundException(errorMessage);
}
- missionRun.MissionRunType = missionRunType;
-
- return await Update(missionRun);
+ return await UpdateMissionRunProperty(missionRun.Id, "MissionRunType", missionRunType);
}
@@ -564,7 +563,7 @@ public async Task UpdateMissionRunStatusByIsarMissionId(string isarM
missionRun.Status = missionStatus;
- missionRun = await Update(missionRun);
+ missionRun = await UpdateMissionRunProperty(missionRun.Id, "MissionStatus", missionStatus);
if (missionRun.Status == MissionStatus.Failed) { _ = signalRService.SendMessageAsync("Mission run failed", missionRun?.Area?.Installation, missionRun != null ? new MissionRunResponse(missionRun) : null); }
return missionRun!;
@@ -572,5 +571,29 @@ public async Task UpdateMissionRunStatusByIsarMissionId(string isarM
#endregion ISAR Specific methods
+
+ public async Task UpdateMissionRunProperty(string missionRunId, string propertyName, object? value)
+ {
+ var missionRun = await ReadById(missionRunId);
+ if (missionRun is null)
+ {
+ string errorMessage = $"Mission with ID {missionRunId} was not found in the database";
+ logger.LogError("{Message}", errorMessage);
+ throw new MissionRunNotFoundException(errorMessage);
+ }
+
+ foreach (var property in typeof(MissionRun).GetProperties())
+ {
+ if (property.Name == propertyName)
+ {
+ logger.LogInformation("Setting {missionRunName} field {propertyName} from {oldValue} to {NewValue}", missionRun.Name, propertyName, property.GetValue(missionRun), value);
+ property.SetValue(missionRun, value);
+ }
+ }
+
+ try { missionRun = await Update(missionRun); }
+ catch (InvalidOperationException e) { logger.LogError(e, "Failed to update {missionRunName}", missionRun.Name); };
+ return missionRun;
+ }
}
}
diff --git a/backend/api/Services/MissionSchedulingService.cs b/backend/api/Services/MissionSchedulingService.cs
index 572ad852b..bb68d984a 100644
--- a/backend/api/Services/MissionSchedulingService.cs
+++ b/backend/api/Services/MissionSchedulingService.cs
@@ -136,9 +136,8 @@ or MissionRunNotFoundException
missionRun.Id,
ex.Message
);
- missionRun.Status = NewStatus;
- missionRun.StatusReason = $"Failed to start: '{ex.Message}'";
- await missionRunService.Update(missionRun);
+ await missionRunService.UpdateMissionRunProperty(missionRun.Id, "Status", NewStatus);
+ await missionRunService.UpdateMissionRunProperty(missionRun.Id, "StatusReason", ex.Message);
}
}
@@ -258,9 +257,8 @@ public async Task AbortAllScheduledMissions(string robotId, string? abortReason)
foreach (var pendingMissionRun in pendingMissionRuns)
{
- pendingMissionRun.Status = MissionStatus.Aborted;
- pendingMissionRun.StatusReason = abortReason;
- await missionRunService.Update(pendingMissionRun);
+ await missionRunService.UpdateMissionRunProperty(pendingMissionRun.Id, "Status", MissionStatus.Aborted);
+ await missionRunService.UpdateMissionRunProperty(pendingMissionRun.Id, "StatusReason", abortReason);
}
}
@@ -443,8 +441,7 @@ private async Task StartMissionRun(MissionRun queuedMissionRun)
}
missionRun.UpdateWithIsarInfo(isarMission);
- missionRun.Status = MissionStatus.Ongoing;
- await missionRunService.Update(missionRun);
+ await missionRunService.UpdateMissionRunProperty(missionRun.Id, "Status", MissionStatus.Ongoing);
robot.Status = RobotStatus.Busy;
await robotService.UpdateRobotStatus(robot.Id, RobotStatus.Busy);