From afb1a16346f344c4b94516016b5f9082e30580c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 10 Mar 2024 14:52:39 +0100 Subject: [PATCH] feat(ChargingProcesses): Convert old chargingProcessesToNew --- .../TeslaSolarCharger/ChargingProcess.cs | 1 + ...ChargingProcessConvertedMarker.Designer.cs | 530 ++++++++++++++++++ ...35142_AddChargingProcessConvertedMarker.cs | 29 + .../TeslaSolarChargerContextModelSnapshot.cs | 3 + .../Server/Contracts/IChargingCostService.cs | 1 + TeslaSolarCharger/Server/Program.cs | 9 +- .../Server/Services/ChargingCostService.cs | 100 +++- .../Shared/Resources/Constants.cs | 1 + .../Shared/Resources/Contracts/IConstants.cs | 1 + 9 files changed, 672 insertions(+), 3 deletions(-) create mode 100644 TeslaSolarCharger.Model/Migrations/20240310135142_AddChargingProcessConvertedMarker.Designer.cs create mode 100644 TeslaSolarCharger.Model/Migrations/20240310135142_AddChargingProcessConvertedMarker.cs diff --git a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ChargingProcess.cs b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ChargingProcess.cs index e5ecfb5fd..dd2dca795 100644 --- a/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ChargingProcess.cs +++ b/TeslaSolarCharger.Model/Entities/TeslaSolarCharger/ChargingProcess.cs @@ -8,6 +8,7 @@ public class ChargingProcess public decimal? UsedGridEnergyKwh { get; set; } public decimal? UsedSolarEnergyKwh { get; set; } public decimal? Cost { get; set; } + public bool ConvertedFromOldStructure { get; set; } public int CarId { get; set; } diff --git a/TeslaSolarCharger.Model/Migrations/20240310135142_AddChargingProcessConvertedMarker.Designer.cs b/TeslaSolarCharger.Model/Migrations/20240310135142_AddChargingProcessConvertedMarker.Designer.cs new file mode 100644 index 000000000..930b5c394 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240310135142_AddChargingProcessConvertedMarker.Designer.cs @@ -0,0 +1,530 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TeslaSolarCharger.Model.EntityFramework; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + [DbContext(typeof(TeslaSolarChargerContext))] + [Migration("20240310135142_AddChargingProcessConvertedMarker")] + partial class AddChargingProcessConvertedMarker + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.2"); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.CachedCarState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("CarStateJson") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastUpdated") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("CachedCarStates"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargeMode") + .HasColumnType("INTEGER"); + + b.Property("ChargerActualCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerPhases") + .HasColumnType("INTEGER"); + + b.Property("ChargerPilotCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerRequestedCurrent") + .HasColumnType("INTEGER"); + + b.Property("ChargerVoltage") + .HasColumnType("INTEGER"); + + b.Property("ChargingPriority") + .HasColumnType("INTEGER"); + + b.Property("ClimateOn") + .HasColumnType("INTEGER"); + + b.Property("IgnoreLatestTimeToReachSocDate") + .HasColumnType("INTEGER"); + + b.Property("LatestTimeToReachSoC") + .HasColumnType("TEXT"); + + b.Property("Latitude") + .HasColumnType("REAL"); + + b.Property("Longitude") + .HasColumnType("REAL"); + + b.Property("MaximumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumAmpere") + .HasColumnType("INTEGER"); + + b.Property("MinimumSoc") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PluggedIn") + .HasColumnType("INTEGER"); + + b.Property("ShouldBeManaged") + .HasColumnType("INTEGER"); + + b.Property("ShouldSetChargeStartTimes") + .HasColumnType("INTEGER"); + + b.Property("SoC") + .HasColumnType("INTEGER"); + + b.Property("SocLimit") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TeslaFleetApiState") + .HasColumnType("INTEGER"); + + b.Property("TeslaMateCarId") + .HasColumnType("INTEGER"); + + b.Property("UsableEnergy") + .HasColumnType("INTEGER"); + + b.Property("Vin") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeslaMateCarId") + .IsUnique(); + + b.HasIndex("Vin") + .IsUnique(); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargePrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddSpotPriceToGridPrice") + .HasColumnType("INTEGER"); + + b.Property("EnergyProvider") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(6); + + b.Property("EnergyProviderConfiguration") + .HasColumnType("TEXT"); + + b.Property("GridPrice") + .HasColumnType("TEXT"); + + b.Property("SolarPrice") + .HasColumnType("TEXT"); + + b.Property("SpotPriceCorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("ValidSince") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ChargePrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("GridPower") + .HasColumnType("INTEGER"); + + b.Property("SolarPower") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ChargingProcessId"); + + b.ToTable("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ConvertedFromOldStructure") + .HasColumnType("INTEGER"); + + b.Property("Cost") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UsedGridEnergyKwh") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergyKwh") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AverageSpotPrice") + .HasColumnType("TEXT"); + + b.Property("CalculatedPrice") + .HasColumnType("TEXT"); + + b.Property("CarId") + .HasColumnType("INTEGER"); + + b.Property("ChargingProcessId") + .HasColumnType("INTEGER"); + + b.Property("UsedGridEnergy") + .HasColumnType("TEXT"); + + b.Property("UsedSolarEnergy") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("HandledCharges"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChargingPower") + .HasColumnType("INTEGER"); + + b.Property("GridProportion") + .HasColumnType("REAL"); + + b.Property("HandledChargeId") + .HasColumnType("INTEGER"); + + b.Property("PowerFromGrid") + .HasColumnType("INTEGER"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.Property("UsedWattHours") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("HandledChargeId"); + + b.ToTable("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HttpMethod") + .HasColumnType("INTEGER"); + + b.Property("NodePatternType") + .HasColumnType("INTEGER"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RestValueConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId", "Key") + .IsUnique(); + + b.ToTable("RestValueConfigurationHeaders"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CorrectionFactor") + .HasColumnType("TEXT"); + + b.Property("NodePattern") + .HasColumnType("TEXT"); + + b.Property("Operator") + .HasColumnType("INTEGER"); + + b.Property("RestValueConfigurationId") + .HasColumnType("INTEGER"); + + b.Property("UsedFor") + .HasColumnType("INTEGER"); + + b.Property("XmlAttributeHeaderName") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeHeaderValue") + .HasColumnType("TEXT"); + + b.Property("XmlAttributeValueName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RestValueConfigurationId"); + + b.ToTable("RestValueResultConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.SpotPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("SpotPrices"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TeslaToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExpiresAtUtc") + .HasColumnType("TEXT"); + + b.Property("IdToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RefreshToken") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Region") + .HasColumnType("INTEGER"); + + b.Property("UnauthorizedCounter") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TeslaTokens"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.TscConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("TscConfigurations"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingDetail", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", "ChargingProcess") + .WithMany("ChargingDetails") + .HasForeignKey("ChargingProcessId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChargingProcess"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", "Car") + .WithMany("ChargingProcesses") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.PowerDistribution", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", "HandledCharge") + .WithMany("PowerDistributions") + .HasForeignKey("HandledChargeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HandledCharge"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfigurationHeader", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("Headers") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueResultConfiguration", b => + { + b.HasOne("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", "RestValueConfiguration") + .WithMany("RestValueResultConfigurations") + .HasForeignKey("RestValueConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RestValueConfiguration"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.Car", b => + { + b.Navigation("ChargingProcesses"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess", b => + { + b.Navigation("ChargingDetails"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.HandledCharge", b => + { + b.Navigation("PowerDistributions"); + }); + + modelBuilder.Entity("TeslaSolarCharger.Model.Entities.TeslaSolarCharger.RestValueConfiguration", b => + { + b.Navigation("Headers"); + + b.Navigation("RestValueResultConfigurations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/20240310135142_AddChargingProcessConvertedMarker.cs b/TeslaSolarCharger.Model/Migrations/20240310135142_AddChargingProcessConvertedMarker.cs new file mode 100644 index 000000000..4b82ae844 --- /dev/null +++ b/TeslaSolarCharger.Model/Migrations/20240310135142_AddChargingProcessConvertedMarker.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TeslaSolarCharger.Model.Migrations +{ + /// + public partial class AddChargingProcessConvertedMarker : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ConvertedFromOldStructure", + table: "ChargingProcesses", + type: "INTEGER", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ConvertedFromOldStructure", + table: "ChargingProcesses"); + } + } +} diff --git a/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs b/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs index 115e62a68..1f80f31ca 100644 --- a/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs +++ b/TeslaSolarCharger.Model/Migrations/TeslaSolarChargerContextModelSnapshot.cs @@ -204,6 +204,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CarId") .HasColumnType("INTEGER"); + b.Property("ConvertedFromOldStructure") + .HasColumnType("INTEGER"); + b.Property("Cost") .HasColumnType("TEXT"); diff --git a/TeslaSolarCharger/Server/Contracts/IChargingCostService.cs b/TeslaSolarCharger/Server/Contracts/IChargingCostService.cs index 4a96af88e..0200c939a 100644 --- a/TeslaSolarCharger/Server/Contracts/IChargingCostService.cs +++ b/TeslaSolarCharger/Server/Contracts/IChargingCostService.cs @@ -16,4 +16,5 @@ public interface IChargingCostService Task DeleteDuplicatedHandleCharges(); Task> GetSpotPrices(); Task> GetHandledCharges(int carId); + Task ConvertToNewChargingProcessStructure(); } diff --git a/TeslaSolarCharger/Server/Program.cs b/TeslaSolarCharger/Server/Program.cs index 5358e876a..4a3e6643a 100644 --- a/TeslaSolarCharger/Server/Program.cs +++ b/TeslaSolarCharger/Server/Program.cs @@ -90,8 +90,9 @@ var chargingCostService = app.Services.GetRequiredService(); await chargingCostService.DeleteDuplicatedHandleCharges().ConfigureAwait(false); - + + await configurationWrapper.TryAutoFillUrls().ConfigureAwait(false); var telegramService = app.Services.GetRequiredService(); @@ -100,12 +101,16 @@ var configJsonService = app.Services.GetRequiredService(); await configJsonService.ConvertOldCarsToNewCar().ConfigureAwait(false); await configJsonService.AddCarsToSettings().ConfigureAwait(false); + //This needs to be done after converting old cars to new cars as IDs might change + await chargingCostService.ConvertToNewChargingProcessStructure().ConfigureAwait(false); await configJsonService.UpdateAverageGridVoltage().ConfigureAwait(false); var pvValueService = app.Services.GetRequiredService(); await pvValueService.ConvertToNewConfiguration().ConfigureAwait(false); + + var teslaFleetApiService = app.Services.GetRequiredService(); var settings = app.Services.GetRequiredService(); if (await teslaFleetApiService.IsFleetApiProxyNeededInDatabase().ConfigureAwait(false)) @@ -114,7 +119,7 @@ } var jobManager = app.Services.GetRequiredService(); - //if (!Debugger.IsAttached) + if (!Debugger.IsAttached) { await jobManager.StartJobs().ConfigureAwait(false); } diff --git a/TeslaSolarCharger/Server/Services/ChargingCostService.cs b/TeslaSolarCharger/Server/Services/ChargingCostService.cs index 10e3d32b2..0e3da4e2f 100644 --- a/TeslaSolarCharger/Server/Services/ChargingCostService.cs +++ b/TeslaSolarCharger/Server/Services/ChargingCostService.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using TeslaSolarCharger.Model.Contracts; using TeslaSolarCharger.Model.Entities.TeslaSolarCharger; +using TeslaSolarCharger.Model.EntityFramework; using TeslaSolarCharger.Server.Contracts; using TeslaSolarCharger.Server.Services.GridPrice.Contracts; using TeslaSolarCharger.Server.Services.GridPrice.Dtos; @@ -9,6 +10,7 @@ using TeslaSolarCharger.Shared.Dtos.ChargingCost; using TeslaSolarCharger.Shared.Dtos.Contracts; using TeslaSolarCharger.Shared.Enums; +using TeslaSolarCharger.Shared.Resources.Contracts; using TeslaSolarCharger.SharedBackend.MappingExtensions; using ChargingProcess = TeslaSolarCharger.Model.Entities.TeslaMate.ChargingProcess; @@ -24,12 +26,14 @@ public class ChargingCostService : IChargingCostService private readonly IMapperConfigurationFactory _mapperConfigurationFactory; private readonly IConfigurationWrapper _configurationWrapper; private readonly IFixedPriceService _fixedPriceService; + private readonly IServiceProvider _serviceProvider; + private readonly IConstants _constants; public ChargingCostService(ILogger logger, ITeslaSolarChargerContext teslaSolarChargerContext, ITeslamateContext teslamateContext, IDateTimeProvider dateTimeProvider, ISettings settings, IMapperConfigurationFactory mapperConfigurationFactory, IConfigurationWrapper configurationWrapper, - IFixedPriceService fixedPriceService) + IFixedPriceService fixedPriceService, IServiceProvider serviceProvider, IConstants constants) { _logger = logger; _teslaSolarChargerContext = teslaSolarChargerContext; @@ -39,6 +43,98 @@ public ChargingCostService(ILogger logger, _mapperConfigurationFactory = mapperConfigurationFactory; _configurationWrapper = configurationWrapper; _fixedPriceService = fixedPriceService; + _serviceProvider = serviceProvider; + _constants = constants; + } + + public async Task ConvertToNewChargingProcessStructure() + { + var chargingProcessesConverted = + await _teslaSolarChargerContext.TscConfigurations.AnyAsync(c => c.Key == _constants.HandledChargesConverted).ConfigureAwait(false); + if (chargingProcessesConverted) + { + return; + } + var convertedChargingProcesses = await _teslaSolarChargerContext.ChargingProcesses + .Where(c => c.ConvertedFromOldStructure) + .ToListAsync(); + var gcCounter = 0; + foreach (var convertedChargingProcess in convertedChargingProcesses) + { + using var scope = _serviceProvider.CreateScope(); + var scopedTscContext = scope.ServiceProvider.GetRequiredService(); + var chargingDetails = await scopedTscContext.ChargingDetails + .Where(cd => cd.ChargingProcessId == convertedChargingProcess.Id) + .ToListAsync().ConfigureAwait(false); + scopedTscContext.ChargingDetails.RemoveRange(chargingDetails); + await scopedTscContext.SaveChangesAsync().ConfigureAwait(false); + _teslaSolarChargerContext.ChargingProcesses.Remove(convertedChargingProcess); + await _teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); + if (gcCounter++ % 20 == 0) + { + _logger.LogInformation("Deleted {counter} converted charging processes before restarting conversion", gcCounter); + GC.Collect(); + } + } + var handledCharges = await _teslaSolarChargerContext.HandledCharges + .Include(h => h.PowerDistributions) + .AsNoTracking() + .ToListAsync(); + gcCounter = 0; + foreach (var handledCharge in handledCharges) + { + var teslaMateChargingProcess = _teslamateContext.ChargingProcesses.FirstOrDefault(c => c.Id == handledCharge.ChargingProcessId); + if (teslaMateChargingProcess == default) + { + _logger.LogWarning("Could not find charging process in TeslaMate with ID {id} for handled charge with ID {handledChargeId}", handledCharge.ChargingProcessId, handledCharge.Id); + continue; + } + + var newChargingProcess = new TeslaSolarCharger.Model.Entities.TeslaSolarCharger.ChargingProcess() + { + CarId = handledCharge.CarId, + StartDate = teslaMateChargingProcess.StartDate, + EndDate = teslaMateChargingProcess.EndDate, + UsedGridEnergyKwh = handledCharge.UsedGridEnergy, + UsedSolarEnergyKwh = handledCharge.UsedSolarEnergy, + Cost = handledCharge.CalculatedPrice, + ConvertedFromOldStructure = true, + }; + var chargingDetails = handledCharge.PowerDistributions.Select(p => new ChargingDetail() + { + TimeStamp = p.TimeStamp, + SolarPower = p.ChargingPower - (p.PowerFromGrid < 0 ? 0 : p.PowerFromGrid), + GridPower = (p.PowerFromGrid < 0 ? 0 : p.PowerFromGrid), + }).ToList(); + newChargingProcess.ChargingDetails = chargingDetails; + try + { + await SaveNewChargingProcess(newChargingProcess); + if (gcCounter++ % 20 == 0) + { + _logger.LogInformation("Converted {counter} charging processes...", gcCounter); + GC.Collect(); + } + } + catch (Exception e) + { + _logger.LogError(e, "Error while converting handled charge with ID {handledChargeId} to new charging process structure", handledCharge.Id); + } + } + _teslaSolarChargerContext.TscConfigurations.Add(new TscConfiguration() + { + Key = _constants.HandledChargesConverted, + Value = "true", + }); + await _teslaSolarChargerContext.SaveChangesAsync().ConfigureAwait(false); + } + + private async Task SaveNewChargingProcess(Model.Entities.TeslaSolarCharger.ChargingProcess newChargingProcess) + { + using var scope = _serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + context.ChargingProcesses.Add(newChargingProcess); + await context.SaveChangesAsync().ConfigureAwait(false); } public async Task UpdateChargePrice(DtoChargePrice dtoChargePrice) @@ -368,6 +464,8 @@ public async Task> GetHandledCharges(int carId) return handledCharges.OrderByDescending(d => d.StartTime).ToList(); } + + public async Task FinalizeHandledCharges() { _logger.LogTrace("{method}()", nameof(FinalizeHandledCharges)); diff --git a/TeslaSolarCharger/Shared/Resources/Constants.cs b/TeslaSolarCharger/Shared/Resources/Constants.cs index e4425ae44..ec0625f0a 100644 --- a/TeslaSolarCharger/Shared/Resources/Constants.cs +++ b/TeslaSolarCharger/Shared/Resources/Constants.cs @@ -21,6 +21,7 @@ public class Constants : IConstants public string FleetApiProxyNeeded => "FleetApiProxyNeeded"; public string CarConfigurationsConverted => "CarConfigurationsConverted"; public string HandledChargesCarIdsConverted => "HandledChargesCarIdsConverted"; + public string HandledChargesConverted => "HandledChargesConverted"; public TimeSpan MaxTokenRequestWaitTime => TimeSpan.FromMinutes(5); public TimeSpan MinTokenRestLifetime => TimeSpan.FromMinutes(2); public int MaxTokenUnauthorizedCount => 5; diff --git a/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs b/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs index 0d4b9cb72..7a1887474 100644 --- a/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs +++ b/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs @@ -25,4 +25,5 @@ public interface IConstants string DefaultMargin { get; } Margin InputMargin { get; } string HandledChargesCarIdsConverted { get; } + string HandledChargesConverted { get; } }