From 101b78f34e1c960a4e460f8c901be94af1003642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 16 Feb 2025 15:50:36 +0100 Subject: [PATCH 1/5] feat(FleetTelemetryReconfigurationJob): add job to auto reconfigure fleet telemetry --- TeslaSolarCharger/Server/Scheduling/JobManager.cs | 5 +++++ .../Jobs/FleetTelemetryReconfigurationJob.cs | 15 +++++++++++++++ .../Jobs/FleetTelemetryReconnectionJob.cs | 4 +--- .../Server/ServiceCollectionExtensions.cs | 1 + .../Server/Services/ConfigJsonService.cs | 5 ++--- 5 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconfigurationJob.cs diff --git a/TeslaSolarCharger/Server/Scheduling/JobManager.cs b/TeslaSolarCharger/Server/Scheduling/JobManager.cs index aaccf8976..9a2d947ab 100644 --- a/TeslaSolarCharger/Server/Scheduling/JobManager.cs +++ b/TeslaSolarCharger/Server/Scheduling/JobManager.cs @@ -52,6 +52,7 @@ public async Task StartJobs() var errorDetectionJob = JobBuilder.Create().WithIdentity(nameof(ErrorDetectionJob)).Build(); var bleApiVersionDetectionJob = JobBuilder.Create().WithIdentity(nameof(BleApiVersionDetectionJob)).Build(); var fleetTelemetryReconnectionJob = JobBuilder.Create().WithIdentity(nameof(FleetTelemetryReconnectionJob)).Build(); + var fleetTelemetryReconfigurationJob = JobBuilder.Create().WithIdentity(nameof(FleetTelemetryReconfigurationJob)).Build(); var currentDate = dateTimeProvider.DateTimeOffSetNow(); var chargingTriggerStartTime = currentDate.AddSeconds(5); @@ -128,6 +129,9 @@ public async Task StartJobs() var fleetTelemetryReconnectionTrigger = TriggerBuilder.Create().WithIdentity("fleetTelemetryReconnectionTrigger") .StartAt(currentDate.AddSeconds(10)) .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(61)).Build(); + var fleetTelemetryReconfigurationTrigger = TriggerBuilder.Create().WithIdentity("fleetTelemetryReconfigurationTrigger") + .StartAt(currentDate.AddSeconds(13)) + .WithSchedule(SimpleScheduleBuilder.RepeatHourlyForever(3)).Build(); var random = new Random(); var hour = random.Next(0, 5); @@ -153,6 +157,7 @@ public async Task StartJobs() {errorDetectionJob, new HashSet {errorDetectionTrigger}}, {bleApiVersionDetectionJob, new HashSet {bleApiVersionDetectionTrigger}}, {fleetTelemetryReconnectionJob, new HashSet {fleetTelemetryReconnectionTrigger}}, + {fleetTelemetryReconfigurationJob, new HashSet {fleetTelemetryReconfigurationTrigger}}, }; if (!configurationWrapper.ShouldUseFakeSolarValues()) diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconfigurationJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconfigurationJob.cs new file mode 100644 index 000000000..93652ac1e --- /dev/null +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconfigurationJob.cs @@ -0,0 +1,15 @@ +using Quartz; +using TeslaSolarCharger.Server.Services.Contracts; + +namespace TeslaSolarCharger.Server.Scheduling.Jobs; + +public class FleetTelemetryReconfigurationJob( + ILogger logger, + IFleetTelemetryConfigurationService service) : IJob +{ + public async Task Execute(IJobExecutionContext context) + { + logger.LogTrace("{method}({context})", nameof(Execute), context); + await service.ReconfigureAllCarsIfRequired(); + } +} diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs index 950701a88..46a899251 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs @@ -5,13 +5,11 @@ namespace TeslaSolarCharger.Server.Scheduling.Jobs; public class FleetTelemetryReconnectionJob( ILogger logger, - IFleetTelemetryWebSocketService service, - IFleetTelemetryConfigurationService fleetTelemetryConfigurationService) : IJob + IFleetTelemetryWebSocketService service) : IJob { public async Task Execute(IJobExecutionContext context) { logger.LogTrace("{method}({context})", nameof(Execute), context); - await fleetTelemetryConfigurationService.ReconfigureAllCarsIfRequired(); await service.ReconnectWebSocketsForEnabledCars(); } } diff --git a/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs b/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs index af20d6326..3a0d423ad 100644 --- a/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs +++ b/TeslaSolarCharger/Server/ServiceCollectionExtensions.cs @@ -57,6 +57,7 @@ public static IServiceCollection AddMyDependencies(this IServiceCollection servi .AddTransient() .AddTransient() .AddTransient() + .AddTransient() .AddTransient() .AddTransient() .AddTransient() diff --git a/TeslaSolarCharger/Server/Services/ConfigJsonService.cs b/TeslaSolarCharger/Server/Services/ConfigJsonService.cs index 876751c27..55fc71776 100644 --- a/TeslaSolarCharger/Server/Services/ConfigJsonService.cs +++ b/TeslaSolarCharger/Server/Services/ConfigJsonService.cs @@ -28,10 +28,8 @@ public class ConfigJsonService( IConfigurationWrapper configurationWrapper, ITeslaSolarChargerContext teslaSolarChargerContext, IConstants constants, - IDateTimeProvider dateTimeProvider, - JobManager jobManager, ITeslaMateDbContextWrapper teslaMateDbContextWrapper, - IFleetTelemetryWebSocketService fleetTelemetryWebSocketService) + IFleetTelemetryConfigurationService fleetTelemetryConfigurationService) : IConfigJsonService { private bool CarConfigurationFileExists() @@ -266,6 +264,7 @@ public async Task UpdateCarBasicConfiguration(int carId, CarBasicConfiguration c settingsCar.ShouldBeManaged = carBasicConfiguration.ShouldBeManaged; settingsCar.UseBle = carBasicConfiguration.UseBle; settingsCar.BleApiBaseUrl = carBasicConfiguration.BleApiBaseUrl; + await fleetTelemetryConfigurationService.SetFleetTelemetryConfiguration(settingsCar.Vin, false); } public async Task SaveOrUpdateCar(DtoCar car) From 1b4571cc185c7c4128e15f126ba0b5c394c17175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 16 Feb 2025 16:12:26 +0100 Subject: [PATCH 2/5] fix(jobs): update logger types --- .../Server/Scheduling/Jobs/ErrorDetectionJob.cs | 2 +- .../Jobs/FleetTelemetryReconfigurationJob.cs | 2 +- .../Jobs/FleetTelemetryReconnectionJob.cs | 2 +- .../Scheduling/Jobs/MqttReconnectionJob.cs | 14 +++----------- .../Server/Scheduling/Jobs/PvValueJob.cs | 14 +++----------- .../Server/Scheduling/Jobs/SpotPriceJob.cs | 16 +++------------- 6 files changed, 12 insertions(+), 38 deletions(-) diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/ErrorDetectionJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/ErrorDetectionJob.cs index f0aaac093..67a3a98f6 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/ErrorDetectionJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/ErrorDetectionJob.cs @@ -5,7 +5,7 @@ namespace TeslaSolarCharger.Server.Scheduling.Jobs; [DisallowConcurrentExecution] -public class ErrorDetectionJob(ILogger logger, IErrorDetectionService service) : IJob +public class ErrorDetectionJob(ILogger logger, IErrorDetectionService service) : IJob { public async Task Execute(IJobExecutionContext context) { diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconfigurationJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconfigurationJob.cs index 93652ac1e..49c12bdd1 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconfigurationJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconfigurationJob.cs @@ -4,7 +4,7 @@ namespace TeslaSolarCharger.Server.Scheduling.Jobs; public class FleetTelemetryReconfigurationJob( - ILogger logger, + ILogger logger, IFleetTelemetryConfigurationService service) : IJob { public async Task Execute(IJobExecutionContext context) diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs index 46a899251..e0a8481c4 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/FleetTelemetryReconnectionJob.cs @@ -4,7 +4,7 @@ namespace TeslaSolarCharger.Server.Scheduling.Jobs; public class FleetTelemetryReconnectionJob( - ILogger logger, + ILogger logger, IFleetTelemetryWebSocketService service) : IJob { public async Task Execute(IJobExecutionContext context) diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/MqttReconnectionJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/MqttReconnectionJob.cs index b1e2dcd9f..d43935259 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/MqttReconnectionJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/MqttReconnectionJob.cs @@ -4,19 +4,11 @@ namespace TeslaSolarCharger.Server.Scheduling.Jobs; [DisallowConcurrentExecution] -public class MqttReconnectionJob : IJob +public class MqttReconnectionJob(ILogger logger, IMqttConnectionService service) : IJob { - private readonly ILogger _logger; - private readonly IMqttConnectionService _service; - - public MqttReconnectionJob(ILogger logger, IMqttConnectionService service) - { - _logger = logger; - _service = service; - } public async Task Execute(IJobExecutionContext context) { - _logger.LogTrace("{method}({context})", nameof(Execute), context); - await _service.ReconnectMqttServices().ConfigureAwait(false); + logger.LogTrace("{method}({context})", nameof(Execute), context); + await service.ReconnectMqttServices().ConfigureAwait(false); } } diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/PvValueJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/PvValueJob.cs index e1b39b932..a07278ab5 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/PvValueJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/PvValueJob.cs @@ -4,19 +4,11 @@ namespace TeslaSolarCharger.Server.Scheduling.Jobs; [DisallowConcurrentExecution] -public class PvValueJob : IJob +public class PvValueJob(ILogger logger, IPvValueService service) : IJob { - private readonly ILogger _logger; - private readonly IPvValueService _service; - - public PvValueJob(ILogger logger, IPvValueService service) - { - _logger = logger; - _service = service; - } public async Task Execute(IJobExecutionContext context) { - _logger.LogTrace("{method}({context})", nameof(Execute), context); - await _service.UpdatePvValues().ConfigureAwait(false); + logger.LogTrace("{method}({context})", nameof(Execute), context); + await service.UpdatePvValues().ConfigureAwait(false); } } diff --git a/TeslaSolarCharger/Server/Scheduling/Jobs/SpotPriceJob.cs b/TeslaSolarCharger/Server/Scheduling/Jobs/SpotPriceJob.cs index 2140c5b4d..9321c1641 100644 --- a/TeslaSolarCharger/Server/Scheduling/Jobs/SpotPriceJob.cs +++ b/TeslaSolarCharger/Server/Scheduling/Jobs/SpotPriceJob.cs @@ -4,21 +4,11 @@ namespace TeslaSolarCharger.Server.Scheduling.Jobs; [DisallowConcurrentExecution] -public class SpotPriceJob : IJob +public class SpotPriceJob(ILogger logger, ISpotPriceService service) : IJob { - private readonly ILogger _logger; - private readonly ISpotPriceService _service; - - public SpotPriceJob(ILogger logger, ISpotPriceService service) - { - _logger = logger; - _service = service; - } - - public async Task Execute(IJobExecutionContext context) { - _logger.LogTrace("{method}({context})", nameof(Execute), context); - await _service.UpdateSpotPrices().ConfigureAwait(false); + logger.LogTrace("{method}({context})", nameof(Execute), context); + await service.UpdateSpotPrices().ConfigureAwait(false); } } From 7104f0f9b10b3dab033cab56bfac9fe73dd91f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 16 Feb 2025 16:27:43 +0100 Subject: [PATCH 3/5] feat(Constants): add FleetTelemetryConfigurationBufferHours --- TeslaSolarCharger/Server/Scheduling/JobManager.cs | 4 +++- .../Server/Services/FleetTelemetryConfigurationService.cs | 2 +- TeslaSolarCharger/Shared/Resources/Constants.cs | 2 ++ TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/TeslaSolarCharger/Server/Scheduling/JobManager.cs b/TeslaSolarCharger/Server/Scheduling/JobManager.cs index 9a2d947ab..3d6069951 100644 --- a/TeslaSolarCharger/Server/Scheduling/JobManager.cs +++ b/TeslaSolarCharger/Server/Scheduling/JobManager.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Options; using Quartz; using Quartz.Spi; using TeslaSolarCharger.Server.Scheduling.Jobs; @@ -129,9 +130,10 @@ public async Task StartJobs() var fleetTelemetryReconnectionTrigger = TriggerBuilder.Create().WithIdentity("fleetTelemetryReconnectionTrigger") .StartAt(currentDate.AddSeconds(10)) .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForever(61)).Build(); + var fleetTelemetryReconfigurationTrigger = TriggerBuilder.Create().WithIdentity("fleetTelemetryReconfigurationTrigger") .StartAt(currentDate.AddSeconds(13)) - .WithSchedule(SimpleScheduleBuilder.RepeatHourlyForever(3)).Build(); + .WithSchedule(SimpleScheduleBuilder.RepeatHourlyForever(constants.FleetTelemetryReconfigurationBufferHours)).Build(); var random = new Random(); var hour = random.Next(0, 5); diff --git a/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs index 9257abe3d..b46b9e666 100644 --- a/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs +++ b/TeslaSolarCharger/Server/Services/FleetTelemetryConfigurationService.cs @@ -180,7 +180,7 @@ public async Task ReconfigureAllCarsIfRequired() !memoryCache.TryGetValue(constants.FleetTelemetryConfigurationExpiryKey + car.Vin, out DateTimeOffset expiryTime); if(!reconfigurationRequired) { - reconfigurationRequired = expiryTime < currentDate; + reconfigurationRequired = expiryTime < currentDate.AddHours(constants.FleetTelemetryReconfigurationBufferHours); } if (!reconfigurationRequired) { diff --git a/TeslaSolarCharger/Shared/Resources/Constants.cs b/TeslaSolarCharger/Shared/Resources/Constants.cs index 6b8087587..8b60d7373 100644 --- a/TeslaSolarCharger/Shared/Resources/Constants.cs +++ b/TeslaSolarCharger/Shared/Resources/Constants.cs @@ -45,6 +45,8 @@ public class Constants : IConstants public string IsBaseAppLicensedKey => "IsBaseAppLicensed"; public string IsFleetApiLicensedKey => "IsFleetApiLicensed_"; public string FleetTelemetryConfigurationExpiryKey => "FleetTelemetryConfigurationExpiry_"; + //Also on Cloud Server in Solar4Car.Backend.Helper.Constants + public int FleetTelemetryReconfigurationBufferHours => 3; public string GridPoleIcon => "power-pole"; } diff --git a/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs b/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs index 7878ed5d9..4d941fff3 100644 --- a/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs +++ b/TeslaSolarCharger/Shared/Resources/Contracts/IConstants.cs @@ -45,4 +45,5 @@ public interface IConstants string IsBaseAppLicensedKey { get; } string IsFleetApiLicensedKey { get; } string FleetTelemetryConfigurationExpiryKey { get; } + int FleetTelemetryReconfigurationBufferHours { get; } } From 7bb1bc6ea4ed2860536c8e6e3cf0aed63f80e2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Wed, 19 Feb 2025 09:27:21 +0100 Subject: [PATCH 4/5] feat(BackendApiService): improve send request logging --- TeslaSolarCharger/Server/Services/BackendApiService.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/TeslaSolarCharger/Server/Services/BackendApiService.cs b/TeslaSolarCharger/Server/Services/BackendApiService.cs index 4c714644f..9f24b4d96 100644 --- a/TeslaSolarCharger/Server/Services/BackendApiService.cs +++ b/TeslaSolarCharger/Server/Services/BackendApiService.cs @@ -306,6 +306,7 @@ public async Task IsFleetApiLicensed(string vin, bool useCache) /// public async Task> SendRequestToBackend(HttpMethod httpMethod, string? accessToken, string requestUrlPart, object? content) { + logger.LogTrace("{method}({httpMethod}, {accessToken}, {requestUrlPart}, {content}, {@serializedContent})", nameof(SendRequestToBackend), httpMethod, accessToken, requestUrlPart, content, content); var request = new HttpRequestMessage(); var finalUrl = configurationWrapper.BackendApiBaseUrl() + requestUrlPart; request.RequestUri = new Uri(finalUrl); @@ -330,6 +331,7 @@ public async Task IsFleetApiLicensed(string vin, bool useCache) JsonConvert.SerializeObject(content), System.Text.Encoding.UTF8, "application/json"); + logger.LogTrace("Sending content: {content}", await jsonContent.ReadAsStringAsync()); request.Content = jsonContent; } } @@ -342,12 +344,13 @@ public async Task IsFleetApiLicensed(string vin, bool useCache) ); } var response = await httpClient.SendAsync(request).ConfigureAwait(false); + var responseContentString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + logger.LogTrace("Response: {responseContent}", responseContentString); if (response.IsSuccessStatusCode) { - var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (typeof(T) != typeof(object)) { - var deserializedObject = JsonConvert.DeserializeObject(responseContent); + var deserializedObject = JsonConvert.DeserializeObject(responseContentString); if (deserializedObject == null) { From daf4ff5f1ae903f0babb275b1e34d65f41559208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Wed, 19 Feb 2025 09:48:11 +0100 Subject: [PATCH 5/5] refactor(BackendApiService): do not double serialize backend api json content --- TeslaSolarCharger/Server/Services/BackendApiService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TeslaSolarCharger/Server/Services/BackendApiService.cs b/TeslaSolarCharger/Server/Services/BackendApiService.cs index 9f24b4d96..f8e5819ec 100644 --- a/TeslaSolarCharger/Server/Services/BackendApiService.cs +++ b/TeslaSolarCharger/Server/Services/BackendApiService.cs @@ -327,11 +327,12 @@ public async Task IsFleetApiLicensed(string vin, bool useCache) request.Method = HttpMethod.Post; if (content != default) { + var serializedContent = JsonConvert.SerializeObject(content); var jsonContent = new StringContent( - JsonConvert.SerializeObject(content), + serializedContent, System.Text.Encoding.UTF8, "application/json"); - logger.LogTrace("Sending content: {content}", await jsonContent.ReadAsStringAsync()); + logger.LogTrace("Sending content: {serializedContent}", serializedContent); request.Content = jsonContent; } }