From 28e3d317046ab8c7d9bbf0dc8fb7e7f7a52c44d8 Mon Sep 17 00:00:00 2001 From: aestene Date: Fri, 17 Jan 2025 12:41:11 +0100 Subject: [PATCH] Remove timeseries from postgreSQL database This functionality has not been used and should thus be removed. In addition, it simplifies an upcoming implementation of postgreSQL database as part of tests. --- .../api/Controllers/TimeseriesController.cs | 111 ------ .../api/Database/Context/FlotillaDbContext.cs | 7 - .../Database/Models/RobotBatteryTimeseries.cs | 12 - .../Database/Models/RobotPoseTimeseries.cs | 45 --- .../Models/RobotPressureTimeseries.cs | 12 - backend/api/Database/Models/TimeseriesBase.cs | 17 - .../20241210093952_Reset-Structure.cs | 50 --- .../FlotillaDbContextModelSnapshot.cs | 72 ---- backend/api/Program.cs | 4 - backend/api/Services/TimeseriesService.cs | 340 ------------------ .../api/Services/TimeseriesServiceSqlLite.cs | 106 ------ 11 files changed, 776 deletions(-) delete mode 100644 backend/api/Controllers/TimeseriesController.cs delete mode 100644 backend/api/Database/Models/RobotBatteryTimeseries.cs delete mode 100644 backend/api/Database/Models/RobotPoseTimeseries.cs delete mode 100644 backend/api/Database/Models/RobotPressureTimeseries.cs delete mode 100644 backend/api/Database/Models/TimeseriesBase.cs delete mode 100644 backend/api/Services/TimeseriesService.cs delete mode 100644 backend/api/Services/TimeseriesServiceSqlLite.cs diff --git a/backend/api/Controllers/TimeseriesController.cs b/backend/api/Controllers/TimeseriesController.cs deleted file mode 100644 index c7ec9bb7d..000000000 --- a/backend/api/Controllers/TimeseriesController.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Api.Controllers.Models; -using Api.Database.Models; -using Api.Services; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Api.Controllers -{ - [ApiController] - [Route("timeseries")] - [Authorize(Roles = Role.Any)] - public class TimeseriesController( - ILogger logger, - ITimeseriesService timeseriesService - ) : ControllerBase - { - /// - /// Get pressure timeseries - /// - /// - /// This query gets a paginated response of entries of the pressure timeseries - /// - [HttpGet] - [Route("pressure")] - [Authorize(Roles = Role.Any)] - [ProducesResponseType(typeof(IList), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task>> GetPressureTimeseries( - [FromQuery] TimeseriesQueryStringParameters queryStringParameters - ) - { - try - { - var robotPressureTimeseries = - await timeseriesService.ReadAll(queryStringParameters); - return Ok(robotPressureTimeseries); - } - catch (Exception e) - { - logger.LogError(e, "Error during GET of robot pressure timeseries from database"); - throw; - } - } - - /// - /// Get battery timeseries - /// - /// - /// This query gets a paginated response of entries of the battery timeseries - /// - [HttpGet] - [Route("battery")] - [Authorize(Roles = Role.Any)] - [ProducesResponseType(typeof(IList), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task>> GetBatteryTimeseries( - [FromQuery] TimeseriesQueryStringParameters queryStringParameters - ) - { - try - { - var robotBatteryTimeseries = - await timeseriesService.ReadAll(queryStringParameters); - return Ok(robotBatteryTimeseries); - } - catch (Exception e) - { - logger.LogError(e, "Error during GET of robot battery timeseries from database"); - throw; - } - } - - /// - /// Get pose timeseries - /// - /// - /// This query gets a paginated response of entries of the pose timeseries - /// - [HttpGet] - [Route("pose")] - [Authorize(Roles = Role.Any)] - [ProducesResponseType(typeof(IList), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task>> GetPoseTimeseries( - [FromQuery] TimeseriesQueryStringParameters queryStringParameters - ) - { - try - { - var robotPoseTimeseries = await timeseriesService.ReadAll( - queryStringParameters - ); - return Ok(robotPoseTimeseries); - } - catch (Exception e) - { - logger.LogError(e, "Error during GET of robot pose timeseries from database"); - throw; - } - } - } -} diff --git a/backend/api/Database/Context/FlotillaDbContext.cs b/backend/api/Database/Context/FlotillaDbContext.cs index c938a0699..802b376c1 100644 --- a/backend/api/Database/Context/FlotillaDbContext.cs +++ b/backend/api/Database/Context/FlotillaDbContext.cs @@ -33,13 +33,6 @@ public FlotillaDbContext(DbContextOptions options) public DbSet UserInfos => Set(); public DbSet TagInspectionMetadata => Set(); - // Timeseries: - public DbSet RobotPressureTimeseries => - Set(); - public DbSet RobotBatteryTimeseries => - Set(); - public DbSet RobotPoseTimeseries => Set(); - protected override void OnModelCreating(ModelBuilder modelBuilder) { bool isSqlLite = Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite"; diff --git a/backend/api/Database/Models/RobotBatteryTimeseries.cs b/backend/api/Database/Models/RobotBatteryTimeseries.cs deleted file mode 100644 index dff849623..000000000 --- a/backend/api/Database/Models/RobotBatteryTimeseries.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Microsoft.EntityFrameworkCore; - -namespace Api.Database.Models -{ - [Keyless] - public class RobotBatteryTimeseries : TimeseriesBase - { - [Required] - public float BatteryLevel { get; set; } - } -} diff --git a/backend/api/Database/Models/RobotPoseTimeseries.cs b/backend/api/Database/Models/RobotPoseTimeseries.cs deleted file mode 100644 index 0c659459a..000000000 --- a/backend/api/Database/Models/RobotPoseTimeseries.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Microsoft.EntityFrameworkCore; - -namespace Api.Database.Models -{ - // Cannot use Pose as owned entity in keyless entity - // https://learn.microsoft.com/en-us/ef/core/modeling/keyless-entity-types?tabs=data-annotations - [Keyless] - public class RobotPoseTimeseries : TimeseriesBase - { - [Required] - public float PositionX { get; set; } - - [Required] - public float PositionY { get; set; } - - [Required] - public float PositionZ { get; set; } - - [Required] - public float OrientationX { get; set; } - - [Required] - public float OrientationY { get; set; } - - [Required] - public float OrientationZ { get; set; } - - [Required] - public float OrientationW { get; set; } - - public RobotPoseTimeseries(Pose pose) - { - PositionX = pose.Position.X; - PositionY = pose.Position.Y; - PositionZ = pose.Position.Z; - OrientationX = pose.Orientation.X; - OrientationY = pose.Orientation.Y; - OrientationZ = pose.Orientation.Z; - OrientationW = pose.Orientation.W; - } - - public RobotPoseTimeseries() { } - } -} diff --git a/backend/api/Database/Models/RobotPressureTimeseries.cs b/backend/api/Database/Models/RobotPressureTimeseries.cs deleted file mode 100644 index 034d05747..000000000 --- a/backend/api/Database/Models/RobotPressureTimeseries.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Microsoft.EntityFrameworkCore; - -namespace Api.Database.Models -{ - [Keyless] - public class RobotPressureTimeseries : TimeseriesBase - { - [Required] - public float Pressure { get; set; } - } -} diff --git a/backend/api/Database/Models/TimeseriesBase.cs b/backend/api/Database/Models/TimeseriesBase.cs deleted file mode 100644 index 3adcdbbdb..000000000 --- a/backend/api/Database/Models/TimeseriesBase.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -#pragma warning disable CS8618 -namespace Api.Database.Models -{ - public class TimeseriesBase - { - [Required] - public DateTime Time { get; set; } - - [Required] - [ForeignKey(nameof(Robot))] - public string RobotId { get; set; } - - public string? MissionId { get; set; } - } -} diff --git a/backend/api/Migrations/20241210093952_Reset-Structure.cs b/backend/api/Migrations/20241210093952_Reset-Structure.cs index 7349f2699..6be95b297 100644 --- a/backend/api/Migrations/20241210093952_Reset-Structure.cs +++ b/backend/api/Migrations/20241210093952_Reset-Structure.cs @@ -11,11 +11,6 @@ public partial class ResetStructure : Migration /// protected override void Up(MigrationBuilder migrationBuilder) { - // MANUALLY ADDED: - // Adding timescale extension to the database - migrationBuilder.Sql("CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;"); - // - migrationBuilder.CreateTable( name: "DefaultLocalizationPoses", columns: table => new @@ -70,19 +65,6 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_Installations", x => x.Id); }); - migrationBuilder.CreateTable( - name: "RobotBatteryTimeseries", - columns: table => new - { - BatteryLevel = table.Column(type: "real", nullable: false), - Time = table.Column(type: "timestamp with time zone", nullable: false), - RobotId = table.Column(type: "text", nullable: false), - MissionId = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - }); - migrationBuilder.CreateTable( name: "RobotModels", columns: table => new @@ -100,38 +82,6 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_RobotModels", x => x.Id); }); - migrationBuilder.CreateTable( - name: "RobotPoseTimeseries", - columns: table => new - { - PositionX = table.Column(type: "real", nullable: false), - PositionY = table.Column(type: "real", nullable: false), - PositionZ = table.Column(type: "real", nullable: false), - OrientationX = table.Column(type: "real", nullable: false), - OrientationY = table.Column(type: "real", nullable: false), - OrientationZ = table.Column(type: "real", nullable: false), - OrientationW = table.Column(type: "real", nullable: false), - Time = table.Column(type: "timestamp with time zone", nullable: false), - RobotId = table.Column(type: "text", nullable: false), - MissionId = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - }); - - migrationBuilder.CreateTable( - name: "RobotPressureTimeseries", - columns: table => new - { - Pressure = table.Column(type: "real", nullable: false), - Time = table.Column(type: "timestamp with time zone", nullable: false), - RobotId = table.Column(type: "text", nullable: false), - MissionId = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - }); - migrationBuilder.CreateTable( name: "Sources", columns: table => new diff --git a/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs b/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs index 65ef9475a..4b19e493b 100644 --- a/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs +++ b/backend/api/Migrations/FlotillaDbContextModelSnapshot.cs @@ -521,24 +521,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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") @@ -572,60 +554,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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.Source", b => { b.Property("Id") diff --git a/backend/api/Program.cs b/backend/api/Program.cs index ef0182c74..89e8300fd 100644 --- a/backend/api/Program.cs +++ b/backend/api/Program.cs @@ -98,10 +98,6 @@ .Configuration.GetSection("Database") .GetValue("UseInMemoryDatabase"); -if (useInMemoryDatabase) - builder.Services.AddScoped(); -else - builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/backend/api/Services/TimeseriesService.cs b/backend/api/Services/TimeseriesService.cs deleted file mode 100644 index 3f58f589d..000000000 --- a/backend/api/Services/TimeseriesService.cs +++ /dev/null @@ -1,340 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using Api.Controllers.Models; -using Api.Database.Context; -using Api.Database.Models; -using Api.Utilities; -using Microsoft.EntityFrameworkCore; -using Npgsql; - -namespace Api.Services -{ - public interface ITimeseriesService - { - public Task> ReadAll( - TimeseriesQueryStringParameters queryStringParameters - ) - where T : TimeseriesBase; - - public Task AddBatteryEntry(string currentMissionId, float batteryLevel, string robotId); - public Task AddPressureEntry(string currentMissionId, float pressureLevel, string robotId); - public Task AddPoseEntry(string currentMissionId, Pose robotPose, string robotId); - public Task Create(T newTimeseries) - where T : TimeseriesBase; - } - - [SuppressMessage( - "Globalization", - "CA1309:Use ordinal StringComparison", - Justification = "EF Core refrains from translating string comparison overloads to SQL" - )] - public class TimeseriesService : ITimeseriesService - { - private readonly FlotillaDbContext _context; - private readonly ILogger _logger; - private readonly NpgsqlDataSource _dataSource; - - public TimeseriesService(FlotillaDbContext context, ILogger logger) - { - string? connectionString = - context.Database.GetConnectionString() - ?? throw new NotSupportedException( - "Could not get connection string from EF core Database context - Cannot connect to Timeseries" - ); - var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); - _dataSource = dataSourceBuilder.Build(); - _logger = logger; - _context = context; - } - - public async Task AddBatteryEntry( - string currentMissionId, - float batteryLevel, - string robotId - ) - { - try - { - await Create( - new RobotBatteryTimeseries - { - MissionId = currentMissionId, - BatteryLevel = batteryLevel, - RobotId = robotId, - Time = DateTime.UtcNow, - } - ); - } - catch (NpgsqlException e) - { - _logger.LogError( - e, - "An error occurred setting battery level while connecting to the timeseries database" - ); - } - } - - public async Task AddPressureEntry( - string currentMissionId, - float pressureLevel, - string robotId - ) - { - try - { - await Create( - new RobotPressureTimeseries - { - MissionId = currentMissionId, - Pressure = pressureLevel, - RobotId = robotId, - Time = DateTime.UtcNow, - } - ); - } - catch (NpgsqlException e) - { - _logger.LogError( - e, - "An error occurred setting pressure level while connecting to the timeseries database" - ); - } - } - - public async Task AddPoseEntry(string currentMissionId, Pose robotPose, string robotId) - { - try - { - await Create( - new RobotPoseTimeseries(robotPose) - { - MissionId = currentMissionId, - RobotId = robotId, - Time = DateTime.UtcNow, - } - ); - } - catch (NpgsqlException e) - { - _logger.LogError( - e, - "An error occurred setting pose while connecting to the timeseries database" - ); - } - } - - public async Task> ReadAll( - TimeseriesQueryStringParameters queryStringParameters - ) - where T : TimeseriesBase - { - var query = _context.Set().AsQueryable(); - var filter = ConstructFilter(queryStringParameters); - - query = query.Where(filter); - query = query.OrderByDescending(timeseries => timeseries.Time); - - return await PagedList.ToPagedListAsync( - query.OrderByDescending(timeseries => timeseries.Time), - queryStringParameters.PageNumber, - queryStringParameters.PageSize - ); - } - - // Cannot use Entity framework to insert keyless entities into the timeseries database - // https://gibinfrancis.medium.com/timescale-db-with-ef-core-94c948829608 - // https://github.com/npgsql/npgsql - // Unfortunately need to use npgsql framework with heavy statements for this. - public async Task Create(T newTimeseries) - where T : TimeseriesBase - { - await using var connection = await _dataSource.OpenConnectionAsync(); - - string tableName = - _context.Set().EntityType.GetTableName() - ?? throw new NotImplementedException( - $"Class '{nameof(T)}' is not mapped to a table" - ); - - await using var command = new NpgsqlCommand(); - command.Connection = connection; - if (newTimeseries.MissionId is not null) - { - command.CommandText = - $"INSERT INTO \"{tableName}\"" - + $"(\"{nameof(newTimeseries.Time)}\"," - + GetColumnNames(newTimeseries) - + $"\"{nameof(newTimeseries.RobotId)}\"," - + $"\"{nameof(newTimeseries.MissionId)}\") " - + "VALUES " - + $"(@{nameof(newTimeseries.Time)}, " - + GetValueNames(newTimeseries) - + $"@{nameof(newTimeseries.RobotId)}, " - + $"@{nameof(newTimeseries.MissionId)})"; - - command.Parameters.AddWithValue( - nameof(newTimeseries.MissionId), - newTimeseries.MissionId - ); - } - else - { - command.CommandText = - $"INSERT INTO \"{tableName}\"" - + $"(\"{nameof(newTimeseries.Time)}\"," - + GetColumnNames(newTimeseries) - + $"\"{nameof(newTimeseries.RobotId)}\") " - + "VALUES " - + $"(@{nameof(newTimeseries.Time)}, " - + GetValueNames(newTimeseries) - + $"@{nameof(newTimeseries.RobotId)})"; - } - - command.Parameters.AddWithValue(nameof(newTimeseries.RobotId), newTimeseries.RobotId); - command.Parameters.AddWithValue(nameof(newTimeseries.Time), newTimeseries.Time); - - AddParameterValues(command.Parameters, newTimeseries); - - await command.ExecuteNonQueryAsync(); - - await connection.CloseAsync(); - - return newTimeseries; - } - - private static Expression> ConstructFilter( - TimeseriesQueryStringParameters parameters - ) - where T : TimeseriesBase - { - Expression> robotIdFilter = parameters.RobotId is null - ? timeseries => true - : timeseries => timeseries.RobotId.Equals(parameters.RobotId); - - Expression> missionIdFilter = parameters.MissionId is null - ? timeseries => true - : timeseries => - timeseries.MissionId == null - || timeseries.MissionId.Equals(parameters.MissionId); - - var minStartTime = DateTimeUtilities.UnixTimeStampToDateTime(parameters.MinTime); - var maxStartTime = DateTimeUtilities.UnixTimeStampToDateTime(parameters.MaxTime); - Expression> timeFilter = timeseries => - DateTime.Compare(timeseries.Time, minStartTime) >= 0 - && DateTime.Compare(timeseries.Time, maxStartTime) <= 0; - - // The parameter of the filter expression - var timeseries = Expression.Parameter(typeof(T)); - - // Combining the body of the filters to create the combined filter, using invoke to force parameter substitution - Expression body = Expression.AndAlso( - Expression.Invoke(robotIdFilter, timeseries), - Expression.AndAlso( - Expression.Invoke(missionIdFilter, timeseries), - Expression.Invoke(timeFilter, timeseries) - ) - ); - - // Constructing the resulting lambda expression by combining parameter and body - return Expression.Lambda>(body, timeseries); - } - - private static void AddParameterValues(NpgsqlParameterCollection parameters, T entity) - { - if (entity is RobotPressureTimeseries robotPressureTimeseries) - { - parameters.AddWithValue( - nameof(robotPressureTimeseries.Pressure), - robotPressureTimeseries.Pressure - ); - } - else if (entity is RobotBatteryTimeseries robotBatteryTimeseries) - { - parameters.AddWithValue( - nameof(robotBatteryTimeseries.BatteryLevel), - robotBatteryTimeseries.BatteryLevel - ); - } - else if (entity is RobotPoseTimeseries robotPoseTimeseries) - { - // Position - parameters.AddWithValue( - nameof(robotPoseTimeseries.PositionX), - robotPoseTimeseries.PositionX - ); - parameters.AddWithValue( - nameof(robotPoseTimeseries.PositionY), - robotPoseTimeseries.PositionY - ); - parameters.AddWithValue( - nameof(robotPoseTimeseries.PositionZ), - robotPoseTimeseries.PositionZ - ); - - // Orientation - parameters.AddWithValue( - nameof(robotPoseTimeseries.OrientationX), - robotPoseTimeseries.OrientationX - ); - parameters.AddWithValue( - nameof(robotPoseTimeseries.OrientationY), - robotPoseTimeseries.OrientationY - ); - parameters.AddWithValue( - nameof(robotPoseTimeseries.OrientationZ), - robotPoseTimeseries.OrientationZ - ); - parameters.AddWithValue( - nameof(robotPoseTimeseries.OrientationW), - robotPoseTimeseries.OrientationW - ); - } - else - { - throw new NotImplementedException( - $"No parameter values defined for timeseries type '{nameof(T)}'" - ); - } - } - - private static string GetColumnNames(T entity) - where T : TimeseriesBase - { - return GetContentNames(entity, "\"", "\""); - } - - private static string GetValueNames(T entity) - where T : TimeseriesBase - { - return GetContentNames(entity, "@"); - } - - private static string GetContentNames( - T entity, - string namePrefix = "", - string namePostfix = "" - ) - where T : TimeseriesBase - { - if (entity is RobotPressureTimeseries robotPressureTimeseries) - { - return $"{namePrefix}{nameof(robotPressureTimeseries.Pressure)}{namePostfix},"; - } - - if (entity is RobotBatteryTimeseries robotBatteryTimeseries) - { - return $"{namePrefix}{nameof(robotBatteryTimeseries.BatteryLevel)}{namePostfix},"; - } - - if (entity is RobotPoseTimeseries robotPoseTimeseries) - { - return $"{namePrefix}{nameof(robotPoseTimeseries.PositionX)}{namePostfix},{namePrefix}{nameof(robotPoseTimeseries.PositionY)}{namePostfix},{namePrefix}{nameof(robotPoseTimeseries.PositionZ)}{namePostfix}," - + $"{namePrefix}{nameof(robotPoseTimeseries.OrientationX)}{namePostfix},{namePrefix}{nameof(robotPoseTimeseries.OrientationY)}{namePostfix},{namePrefix}{nameof(robotPoseTimeseries.OrientationZ)}{namePostfix},{namePrefix}{nameof(robotPoseTimeseries.OrientationW)}{namePostfix},"; - } - - throw new NotImplementedException( - $"No content names defined for timeseries type '{nameof(T)}'" - ); - } - } -} diff --git a/backend/api/Services/TimeseriesServiceSqlLite.cs b/backend/api/Services/TimeseriesServiceSqlLite.cs deleted file mode 100644 index 99224cae5..000000000 --- a/backend/api/Services/TimeseriesServiceSqlLite.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using Api.Controllers.Models; -using Api.Database.Context; -using Api.Database.Models; -using Api.Utilities; - -namespace Api.Services -{ - /// - /// Uses only dotnet ef core, instead of the Npgsql package needed for the PostgreSql database - /// Cannot insert because it is a keyless entity - /// - [SuppressMessage( - "Globalization", - "CA1309:Use ordinal StringComparison", - Justification = "EF Core refrains from translating string comparison overloads to SQL" - )] - public class TimeseriesServiceSqlLite(FlotillaDbContext context) : ITimeseriesService - { - public async Task> ReadAll( - TimeseriesQueryStringParameters queryStringParameters - ) - where T : TimeseriesBase - { - var query = context.Set().AsQueryable(); - var filter = ConstructFilter(queryStringParameters); - - query = query.Where(filter); - query = query.OrderByDescending(timeseries => timeseries.Time); - - return await PagedList.ToPagedListAsync( - query.OrderByDescending(timeseries => timeseries.Time), - queryStringParameters.PageNumber, - queryStringParameters.PageSize - ); - } - - public async Task AddBatteryEntry( - string currentMissionId, - float batteryLevel, - string robotId - ) - { - await Task.CompletedTask; - } - - public async Task AddPressureEntry( - string currentMissionId, - float pressureLevel, - string robotId - ) - { - await Task.CompletedTask; - } - - public async Task AddPoseEntry(string currentMissionId, Pose robotPose, string robotId) - { - await Task.CompletedTask; - } - - public async Task Create(T newTimeseries) - where T : TimeseriesBase - { - await Task.CompletedTask; - return newTimeseries; - } - - private static Expression> ConstructFilter( - TimeseriesQueryStringParameters parameters - ) - where T : TimeseriesBase - { - Expression> robotIdFilter = parameters.RobotId is null - ? timeseries => true - : timeseries => timeseries.RobotId.Equals(parameters.RobotId); - - Expression> missionIdFilter = parameters.MissionId is null - ? timeseries => true - : timeseries => - timeseries.MissionId == null - || timeseries.MissionId.Equals(parameters.MissionId); - - var minStartTime = DateTimeUtilities.UnixTimeStampToDateTime(parameters.MinTime); - var maxStartTime = DateTimeUtilities.UnixTimeStampToDateTime(parameters.MaxTime); - Expression> timeFilter = timeseries => - DateTime.Compare(timeseries.Time, minStartTime) >= 0 - && DateTime.Compare(timeseries.Time, maxStartTime) <= 0; - - // The parameter of the filter expression - var timeseries = Expression.Parameter(typeof(T)); - - // Combining the body of the filters to create the combined filter, using invoke to force parameter substitution - Expression body = Expression.AndAlso( - Expression.Invoke(robotIdFilter, timeseries), - Expression.AndAlso( - Expression.Invoke(missionIdFilter, timeseries), - Expression.Invoke(timeFilter, timeseries) - ) - ); - - // Constructing the resulting lambda expression by combining parameter and body - return Expression.Lambda>(body, timeseries); - } - } -}