From 2bcd319359568e2ef8036e7485852861de631372 Mon Sep 17 00:00:00 2001 From: Mads Mogensen Date: Mon, 4 Dec 2023 10:25:01 +0100 Subject: [PATCH] DateTime Ranges on Charts Also fix a lot of stuff. Add an endpoint that gets the Min StartDatetime for all aggregated measurements in a room. --- .../GetAggregatedMeasurementsResponse.cs | 8 +- ...tAggregatedMeasurementsDateTimeResponse.cs | 20 ++++ .../MockAggregatedMeasurementRepository.cs | 109 ++++++++++-------- .../Controllers/MeasurementController.cs | 26 ++++- CentralHub.Api/Program.cs | 1 - CentralHub.Api/ScopedBackgroundService.cs | 42 ------- .../Services/IMeasurementRepository.cs | 4 +- .../IScopedProcessingService.cs | 2 +- .../Services/LocalizationService.cs | 1 - .../Services/MeasurementRepository.cs | 48 ++++---- .../Services/ScopedBackgroundService.cs | 36 ++++++ CentralHub.WebUI/Data/MeasurementService.cs | 28 +++++ CentralHub.WebUI/Pages/Charts.razor | 74 +++++++++++- 13 files changed, 274 insertions(+), 125 deletions(-) create mode 100644 CentralHub.Api.Model/Responses/Measurements/GetFirstAggregatedMeasurementsDateTimeResponse.cs delete mode 100644 CentralHub.Api/ScopedBackgroundService.cs rename CentralHub.Api/{ => Services}/IScopedProcessingService.cs (74%) create mode 100644 CentralHub.Api/Services/ScopedBackgroundService.cs diff --git a/CentralHub.Api.Model/Responses/Measurements/GetAggregatedMeasurementsResponse.cs b/CentralHub.Api.Model/Responses/Measurements/GetAggregatedMeasurementsResponse.cs index cea3e83..942f126 100644 --- a/CentralHub.Api.Model/Responses/Measurements/GetAggregatedMeasurementsResponse.cs +++ b/CentralHub.Api.Model/Responses/Measurements/GetAggregatedMeasurementsResponse.cs @@ -1,11 +1,11 @@ using System.Text.Json.Serialization; -namespace CentralHub.Api.Model.Responses.AggregatedMeasurements; +namespace CentralHub.Api.Model.Responses.Measurements; public sealed class GetAggregatedMeasurementsResponse { [JsonConstructor] - public GetAggregatedMeasurementsResponse(bool success, IReadOnlyCollection? aggregatedMeasurements) + public GetAggregatedMeasurementsResponse(bool success, IReadOnlyCollection? aggregatedMeasurements) { Success = success; AggregatedMeasurements = aggregatedMeasurements; @@ -14,14 +14,14 @@ public GetAggregatedMeasurementsResponse(bool success, IReadOnlyCollection? AggregatedMeasurements { get; } + public IReadOnlyCollection? AggregatedMeasurements { get; } public static GetAggregatedMeasurementsResponse CreateUnsuccessful() { return new GetAggregatedMeasurementsResponse(false, null); } - public static GetAggregatedMeasurementsResponse CreateSuccessful(IReadOnlyCollection aggregatedMeasurements) + public static GetAggregatedMeasurementsResponse CreateSuccessful(IReadOnlyCollection aggregatedMeasurements) { return new GetAggregatedMeasurementsResponse(true, aggregatedMeasurements); } diff --git a/CentralHub.Api.Model/Responses/Measurements/GetFirstAggregatedMeasurementsDateTimeResponse.cs b/CentralHub.Api.Model/Responses/Measurements/GetFirstAggregatedMeasurementsDateTimeResponse.cs new file mode 100644 index 0000000..103f081 --- /dev/null +++ b/CentralHub.Api.Model/Responses/Measurements/GetFirstAggregatedMeasurementsDateTimeResponse.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; + +namespace CentralHub.Api.Model.Responses.Measurements; + +[method: JsonConstructor] +public sealed class GetFirstAggregatedMeasurementsDateTimeResponse(bool success, DateTime? firstDateTime) +{ + public bool Success { get; } = success; + public DateTime? FirstDateTime { get; } = firstDateTime; + + public static GetFirstAggregatedMeasurementsDateTimeResponse CreateUnsuccessful() + { + return new GetFirstAggregatedMeasurementsDateTimeResponse(false, null); + } + + public static GetFirstAggregatedMeasurementsDateTimeResponse CreateSuccessful(DateTime firstDateTime) + { + return new GetFirstAggregatedMeasurementsDateTimeResponse(true, firstDateTime); + } +} \ No newline at end of file diff --git a/CentralHub.Api.Tests/MockAggregatedMeasurementRepository.cs b/CentralHub.Api.Tests/MockAggregatedMeasurementRepository.cs index 4719418..04e348a 100644 --- a/CentralHub.Api.Tests/MockAggregatedMeasurementRepository.cs +++ b/CentralHub.Api.Tests/MockAggregatedMeasurementRepository.cs @@ -2,87 +2,106 @@ using CentralHub.Api.Dtos; using CentralHub.Api.Model; using CentralHub.Api.Services; +using CentralHub.Api.Threading; namespace CentralHub.Api.Tests; internal sealed class MockAggregatedMeasurementRepository : IMeasurementRepository { - private static readonly Dictionary> _recentMeasurementGroups = new Dictionary>(); + private static readonly CancellableMutex>> RecentMeasurementGroupsMutex = + new CancellableMutex>>(new Dictionary>()); - private static readonly List _AggregatedMeasurements = new List(); - private int _nextId = 0; - public Task AddAggregatedMeasurementAsync(AggregatedMeasurementDto aggregatedDto, CancellationToken cancellationToken) + private static readonly CancellableMutex AggregatedMeasurementsMutex = + new CancellableMutex(new AggregatedMeasurementsStuff()); + + private sealed class AggregatedMeasurementsStuff + { + public List AggregatedMeasurements { get; } = new List(); + + public int NextId { get; set; } = 0; + } + + public async Task AddAggregatedMeasurementAsync(AggregatedMeasurementDto aggregatedDto, CancellationToken cancellationToken) { - lock (_AggregatedMeasurements) + return await AggregatedMeasurementsMutex.Lock(stuff => { - _AggregatedMeasurements.Add(aggregatedDto); - aggregatedDto.AggregatedMeasurementDtoId = _nextId; - _nextId++; - } - return new ValueTask(aggregatedDto.RoomDtoId).AsTask(); + stuff.AggregatedMeasurements.Add(aggregatedDto); + aggregatedDto.AggregatedMeasurementDtoId = stuff.NextId; + stuff.NextId++; + + return aggregatedDto.AggregatedMeasurementDtoId; + }, cancellationToken); } - public Task RemoveAggregatedMeasurementAsync(AggregatedMeasurementDto aggregatedDto, CancellationToken cancellationToken) + public async Task RemoveAggregatedMeasurementAsync(AggregatedMeasurementDto aggregatedDto, CancellationToken cancellationToken) { - lock (_AggregatedMeasurements) + await AggregatedMeasurementsMutex.Lock(stuff => { - _AggregatedMeasurements.Remove(aggregatedDto); - } - return Task.CompletedTask; + stuff.AggregatedMeasurements.Remove(aggregatedDto); + }, cancellationToken); } - public Task> GetAggregatedMeasurementsAsync(int roomId, CancellationToken cancellationToken) + public async Task> GetAggregatedMeasurementsAsync(int roomId, CancellationToken cancellationToken) { - lock (_AggregatedMeasurements) + return await AggregatedMeasurementsMutex.Lock(stuff => { - return new ValueTask>( - _AggregatedMeasurements + return stuff.AggregatedMeasurements .Where(m => m.RoomDtoId == roomId) - .ToImmutableArray()).AsTask(); - } + .ToImmutableArray(); + }, cancellationToken); } - public Task> GetAggregatedMeasurementsAsync(int roomId, DateTime timeStart, DateTime timeEnd, CancellationToken cancellationToken) + public async Task> GetAggregatedMeasurementsAsync(int roomId, DateTime startTime, DateTime endTime, CancellationToken cancellationToken) { - lock (_AggregatedMeasurements) + return await AggregatedMeasurementsMutex.Lock(stuff => { - return new ValueTask>( - _AggregatedMeasurements - .Where(m => m.RoomDtoId == roomId && m.StartTime >= timeStart && m.EndTime <= timeEnd) - .ToImmutableArray()).AsTask(); - } + return stuff.AggregatedMeasurements + .Where(m => m.RoomDtoId == roomId && m.StartTime >= startTime && m.EndTime <= endTime) + .ToImmutableArray(); + }, cancellationToken); } public async Task>> GetRoomMeasurementGroupsAsync(CancellationToken cancellationToken) { - return await Task.Run(() => + return await RecentMeasurementGroupsMutex.Lock(recentMeasurementGroups => { - lock (_recentMeasurementGroups) + var recentTrackerMeasurementGroups = recentMeasurementGroups + .ToDictionary(k => k.Key, v => (IReadOnlyList)v.Value.ToImmutableArray()); + recentMeasurementGroups.Clear(); + return recentTrackerMeasurementGroups; + }, cancellationToken); + } + + public async Task AddMeasurementsAsync(int id, IReadOnlyCollection measurements, CancellationToken cancellationToken) + { + await RecentMeasurementGroupsMutex.Lock(recentMeasurementGroups => + { + if (recentMeasurementGroups.TryGetValue(id, out var value)) { - var recentTrackerMeasurementGroups = _recentMeasurementGroups - .ToDictionary(k => k.Key, v => (IReadOnlyList)v.Value.ToImmutableArray()); - _recentMeasurementGroups.Clear(); - return recentTrackerMeasurementGroups; + value.Add(new MeasurementGroup(measurements.ToImmutableArray())); } + else + { + recentMeasurementGroups.Add(id, + new List() { new MeasurementGroup(measurements.ToImmutableArray()) }); + } + }, cancellationToken); } - public Task AddMeasurementsAsync(int id, IReadOnlyCollection measurements, CancellationToken cancellationToken) + public async Task GetFirstAggregatedMeasurementsDateTimeAsync(int roomId, CancellationToken cancellationToken) { - return Task.Run(() => + return await AggregatedMeasurementsMutex.Lock(stuff => { - lock (_recentMeasurementGroups) + var aggregatedMeasurements = stuff.AggregatedMeasurements.Where(m => m.RoomDtoId == roomId) + .ToImmutableArray(); + if (!aggregatedMeasurements.Any()) { - if (_recentMeasurementGroups.TryGetValue(id, out var value)) - { - value.Add(new MeasurementGroup(measurements.ToList())); - } - else - { - _recentMeasurementGroups.Add(id, new List() { new MeasurementGroup(measurements.ToList()) }); - } + return (DateTime?)null; } + + return aggregatedMeasurements.Min(m => m.StartTime); }, cancellationToken); } } \ No newline at end of file diff --git a/CentralHub.Api/Controllers/MeasurementController.cs b/CentralHub.Api/Controllers/MeasurementController.cs index eb38b2c..0c90818 100644 --- a/CentralHub.Api/Controllers/MeasurementController.cs +++ b/CentralHub.Api/Controllers/MeasurementController.cs @@ -2,6 +2,7 @@ using CentralHub.Api.Model; using CentralHub.Api.Model.Requests.Localization; using CentralHub.Api.Model.Responses.AggregatedMeasurements; +using CentralHub.Api.Model.Responses.Measurements; using CentralHub.Api.Services; using Microsoft.AspNetCore.Mvc; @@ -13,17 +14,17 @@ public sealed class MeasurementController : ControllerBase { private readonly ILogger _logger; - private readonly IMeasurementRepository _aggregatorRepository; + private readonly IMeasurementRepository _measurementRepository; private readonly ITrackerRepository _trackerRepository; public MeasurementController( ILogger logger, - IMeasurementRepository aggregatorRepository, + IMeasurementRepository measurementRepository, ITrackerRepository trackerRepository) { _logger = logger; - _aggregatorRepository = aggregatorRepository; + _measurementRepository = measurementRepository; _trackerRepository = trackerRepository; } @@ -40,7 +41,7 @@ public async Task AddMeasurements(AddMeasurementsReques return AddMeasurementsResponse.CreateUnsuccessful(); } - await _aggregatorRepository.AddMeasurementsAsync( + await _measurementRepository.AddMeasurementsAsync( tracker.RoomDtoId, addMeasurementsRequest.Measurements .Where(m => !registeredTrackers.Any(t => t.WifiMacAddress == m.MacAddress || t.BluetoothMacAddress == m.MacAddress)) @@ -53,7 +54,7 @@ await _aggregatorRepository.AddMeasurementsAsync( [HttpGet("all")] public async Task GetAggregateMeasurements(int roomId, CancellationToken token) { - var aggregatedMeasurements = await _aggregatorRepository.GetAggregatedMeasurementsAsync(roomId, token); + var aggregatedMeasurements = await _measurementRepository.GetAggregatedMeasurementsAsync(roomId, token); if (aggregatedMeasurements == null) { @@ -93,4 +94,19 @@ public async Task GetAggregateMeasurements(in .Where(am => am.StartTime >= timeStart && am.EndTime <= timeEnd) .ToImmutableArray()); } + + [HttpGet("first")] + public async Task GetFirstAggregatedMeasurementDateTime(int roomId, + CancellationToken cancellationToken) + { + var possibleFirstDateTime = + await _measurementRepository.GetFirstAggregatedMeasurementsDateTimeAsync(roomId, cancellationToken); + + if (possibleFirstDateTime == null) + { + return GetFirstAggregatedMeasurementsDateTimeResponse.CreateUnsuccessful(); + } + + return GetFirstAggregatedMeasurementsDateTimeResponse.CreateSuccessful(possibleFirstDateTime.Value); + } } diff --git a/CentralHub.Api/Program.cs b/CentralHub.Api/Program.cs index 00142a8..5caca14 100644 --- a/CentralHub.Api/Program.cs +++ b/CentralHub.Api/Program.cs @@ -1,5 +1,4 @@ using System.ComponentModel; -using App.ScopedService; using CentralHub.Api.DbContexts; using CentralHub.Api.Services; using Microsoft.EntityFrameworkCore; diff --git a/CentralHub.Api/ScopedBackgroundService.cs b/CentralHub.Api/ScopedBackgroundService.cs deleted file mode 100644 index 30f273a..0000000 --- a/CentralHub.Api/ScopedBackgroundService.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace App.ScopedService; - -public sealed class ScopedBackgroundService : BackgroundService -{ - private readonly IServiceProvider _serviceProvider; - private readonly ILogger _logger; - - public ScopedBackgroundService( - IServiceProvider serviceProvider, - ILogger logger) => - (_serviceProvider, _logger) = (serviceProvider, logger); - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - _logger.LogInformation( - $"{nameof(ScopedBackgroundService)} is running."); - - await DoWorkAsync(stoppingToken); - } - - private async Task DoWorkAsync(CancellationToken stoppingToken) - { - _logger.LogInformation( - $"{nameof(ScopedBackgroundService)} is working."); - - using (IServiceScope scope = _serviceProvider.CreateScope()) - { - IScopedProcessingService scopedProcessingService = - scope.ServiceProvider.GetRequiredService(); - - await scopedProcessingService.DoWorkAsync(stoppingToken); - } - } - - public override async Task StopAsync(CancellationToken stoppingToken) - { - _logger.LogInformation( - $"{nameof(ScopedBackgroundService)} is stopping."); - - await base.StopAsync(stoppingToken); - } -} \ No newline at end of file diff --git a/CentralHub.Api/Services/IMeasurementRepository.cs b/CentralHub.Api/Services/IMeasurementRepository.cs index 2d6bcb3..d34f113 100644 --- a/CentralHub.Api/Services/IMeasurementRepository.cs +++ b/CentralHub.Api/Services/IMeasurementRepository.cs @@ -11,9 +11,11 @@ public interface IMeasurementRepository Task> GetAggregatedMeasurementsAsync(int roomId, CancellationToken cancellationToken); - Task> GetAggregatedMeasurementsAsync(int roomId, DateTime timeStart, DateTime timeEnd, CancellationToken cancellationToken); + Task> GetAggregatedMeasurementsAsync(int roomId, DateTime startTime, DateTime endTime, CancellationToken cancellationToken); Task>> GetRoomMeasurementGroupsAsync(CancellationToken cancellationToken); Task AddMeasurementsAsync(int roomId, IReadOnlyCollection measurements, CancellationToken cancellationToken); + + Task GetFirstAggregatedMeasurementsDateTimeAsync(int roomId, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/CentralHub.Api/IScopedProcessingService.cs b/CentralHub.Api/Services/IScopedProcessingService.cs similarity index 74% rename from CentralHub.Api/IScopedProcessingService.cs rename to CentralHub.Api/Services/IScopedProcessingService.cs index 881a536..68a16eb 100644 --- a/CentralHub.Api/IScopedProcessingService.cs +++ b/CentralHub.Api/Services/IScopedProcessingService.cs @@ -1,4 +1,4 @@ -namespace App.ScopedService; +namespace CentralHub.Api.Services; public interface IScopedProcessingService { diff --git a/CentralHub.Api/Services/LocalizationService.cs b/CentralHub.Api/Services/LocalizationService.cs index 9fffa91..f930db9 100644 --- a/CentralHub.Api/Services/LocalizationService.cs +++ b/CentralHub.Api/Services/LocalizationService.cs @@ -1,6 +1,5 @@ using System.Collections.Immutable; using System.Linq; -using App.ScopedService; using CentralHub.Api.Dtos; using CentralHub.Api.Model; using CentralHub.Api.Model.Responses.Room; diff --git a/CentralHub.Api/Services/MeasurementRepository.cs b/CentralHub.Api/Services/MeasurementRepository.cs index d06e3c8..d7934f3 100644 --- a/CentralHub.Api/Services/MeasurementRepository.cs +++ b/CentralHub.Api/Services/MeasurementRepository.cs @@ -2,13 +2,15 @@ using CentralHub.Api.DbContexts; using CentralHub.Api.Dtos; using CentralHub.Api.Model; +using CentralHub.Api.Threading; using Microsoft.EntityFrameworkCore; namespace CentralHub.Api.Services; internal sealed class MeasurementRepository : IMeasurementRepository { - private readonly static Dictionary> _recentRoomMeasurementGroups = new Dictionary>(); + private static readonly CancellableMutex>> RecentRoomMeasurementGroupsMutex = + new CancellableMutex>>(new Dictionary>()); private readonly ApplicationDbContext _applicationDbContext; @@ -55,44 +57,48 @@ public async Task> GetAggregatedMeasuremen return await _applicationDbContext.AggregatedMeasurements.Where(am => roomId == am.RoomDtoId).ToArrayAsync(cancellationToken); } - public async Task> GetAggregatedMeasurementsAsync(int roomId, DateTime timeStart, DateTime timeEnd, CancellationToken cancellationToken) + public async Task> GetAggregatedMeasurementsAsync(int roomId, DateTime startTime, DateTime endTime, CancellationToken cancellationToken) { return await _applicationDbContext.AggregatedMeasurements .Where(am => roomId == am.RoomDtoId) - .Where(am => am.StartTime >= timeStart && am.EndTime <= timeEnd) + .Where(am => am.StartTime >= startTime && am.EndTime <= endTime) .ToArrayAsync(cancellationToken); } public async Task AddMeasurementsAsync(int roomId, IReadOnlyCollection measurements, CancellationToken cancellationToken) { - await Task.Run(() => + await RecentRoomMeasurementGroupsMutex.Lock(recentRoomMeasurementGroups => { - lock (_recentRoomMeasurementGroups) + if (recentRoomMeasurementGroups.TryGetValue(roomId, out var value)) { - if (_recentRoomMeasurementGroups.TryGetValue(roomId, out var value)) - { - value.Add(new MeasurementGroup(measurements.ToImmutableArray())); - } - else - { - _recentRoomMeasurementGroups.Add(roomId, new List() { new MeasurementGroup(measurements.ToImmutableArray()) }); - } + value.Add(new MeasurementGroup(measurements.ToImmutableArray())); + } + else + { + recentRoomMeasurementGroups.Add(roomId, new List() { new MeasurementGroup(measurements.ToImmutableArray()) }); } }, cancellationToken); } + public async Task GetFirstAggregatedMeasurementsDateTimeAsync(int roomId, CancellationToken cancellationToken) + { + var aggregatedMeasurements = _applicationDbContext.AggregatedMeasurements.Where(m => m.RoomDtoId == roomId); + if (!aggregatedMeasurements.Any()) + { + return null; + } + + return await aggregatedMeasurements.MinAsync(m => m.StartTime, cancellationToken); + } + public async Task>> GetRoomMeasurementGroupsAsync(CancellationToken cancellationToken) { - return await Task.Run(() => + return await RecentRoomMeasurementGroupsMutex.Lock(recentRoomMeasurementGroups => { - lock (_recentRoomMeasurementGroups) - { - var recentRoomMeasurementGroups = _recentRoomMeasurementGroups - .ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList)kvp.Value.ToImmutableArray()); + var copy = recentRoomMeasurementGroups.ToDictionary(kv => kv.Key, kv => (IReadOnlyList)kv.Value.ToImmutableArray()); + recentRoomMeasurementGroups.Clear(); - _recentRoomMeasurementGroups.Clear(); - return recentRoomMeasurementGroups; - } + return copy; }, cancellationToken); } } \ No newline at end of file diff --git a/CentralHub.Api/Services/ScopedBackgroundService.cs b/CentralHub.Api/Services/ScopedBackgroundService.cs new file mode 100644 index 0000000..cb9b304 --- /dev/null +++ b/CentralHub.Api/Services/ScopedBackgroundService.cs @@ -0,0 +1,36 @@ +namespace CentralHub.Api.Services; + +public sealed class ScopedBackgroundService(IServiceProvider serviceProvider, + ILogger logger) + : BackgroundService +{ + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + logger.LogInformation( + $"{nameof(ScopedBackgroundService)} is running."); + + await DoWorkAsync(stoppingToken); + } + + private async Task DoWorkAsync(CancellationToken cancellationToken) + { + logger.LogInformation( + $"{nameof(ScopedBackgroundService)} is working."); + + using (IServiceScope scope = serviceProvider.CreateScope()) + { + IScopedProcessingService scopedProcessingService = + scope.ServiceProvider.GetRequiredService(); + + await scopedProcessingService.DoWorkAsync(cancellationToken); + } + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + logger.LogInformation( + $"{nameof(ScopedBackgroundService)} is stopping."); + + await base.StopAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/CentralHub.WebUI/Data/MeasurementService.cs b/CentralHub.WebUI/Data/MeasurementService.cs index 9ac43d4..4d2511a 100644 --- a/CentralHub.WebUI/Data/MeasurementService.cs +++ b/CentralHub.WebUI/Data/MeasurementService.cs @@ -3,6 +3,7 @@ using System.Text.Encodings.Web; using System.Text.Json; using CentralHub.Api.Model.Responses.AggregatedMeasurements; +using CentralHub.Api.Model.Responses.Measurements; namespace CentralHub.WebUI.Data; @@ -45,4 +46,31 @@ public async Task> GetAggregatedMeas return getAggregatedMeasurementsResponse.AggregatedMeasurements!; } + + public async Task GetFirstAggregatedMeasurementsDateTime(int roomId, CancellationToken cancellationToken) + { + var request = new HttpRequestMessage( + HttpMethod.Get, + $"http://localhost:8081/measurements/first?roomId={roomId}"); + request.Headers.Add("Accept", "application/json"); + request.Headers.Add("User-Agent", "CentralHub.WebUI"); + + var client = _clientFactory.CreateClient(); + + var response = await client.SendAsync(request, cancellationToken); + + if (response.StatusCode != HttpStatusCode.OK) + { + throw new InvalidOperationException("Somethings fucky"); + } + + var getFirstAggregatedMeasurementsDateTimeResponse = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken); + + if (getFirstAggregatedMeasurementsDateTimeResponse == null || !getFirstAggregatedMeasurementsDateTimeResponse.Success) + { + throw new InvalidOperationException("Unsuccessful!"); + } + + return getFirstAggregatedMeasurementsDateTimeResponse.FirstDateTime.Value; + } } \ No newline at end of file diff --git a/CentralHub.WebUI/Pages/Charts.razor b/CentralHub.WebUI/Pages/Charts.razor index fcad1f1..6026012 100644 --- a/CentralHub.WebUI/Pages/Charts.razor +++ b/CentralHub.WebUI/Pages/Charts.razor @@ -11,9 +11,25 @@

Charts

+
+
+ +
+
+ +
+
- +
- + _options1 = new(); private readonly ApexChartOptions _options2 = new(); - protected override async Task OnInitializedAsync() + + private DateTime _firstDateTime; + private DateTime _minDateTime = DateTime.Today - TimeSpan.FromDays(1); + private DateTime _maxDateTime = DateTime.Now; + + public DateTime MinDateTime + { + get => _minDateTime; + set + { + _minDateTime = value; + InvokeAsync(async () => + { + await UpdateAggregatedMeasurements(); + StateHasChanged(); + await _wifiChart!.RenderAsync(); + await _bluetoothChart!.RenderAsync(); + }); + } + } + + public DateTime MaxDateTime + { + get => _maxDateTime; + set { _maxDateTime = value; + InvokeAsync(async () => + { + await UpdateAggregatedMeasurements(); + StateHasChanged(); + await _wifiChart!.RenderAsync(); + await _bluetoothChart!.RenderAsync(); + }); + } + } + + private ApexChart? _wifiChart; + private ApexChart? _bluetoothChart; + + private async Task UpdateAggregatedMeasurements() { - _data1 = await MeasurementService.GetAggregatedMeasurements(RoomId, DateTime.MinValue, DateTime.MaxValue, _cancellationTokenSource.Token); + _data1 = await MeasurementService.GetAggregatedMeasurements(RoomId, MinDateTime.ToUniversalTime(), MaxDateTime.ToUniversalTime(), _cancellationTokenSource.Token); + + // HACK!: Add dummy data because the charts crash if there is no data and a page reload is necessary to get it back + if (!_data1.Any()) + { + _data1 = new[] {new AggregatedMeasurements(0, MinDateTime.ToUniversalTime(), MaxDateTime.ToUniversalTime(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)}; + } _data2 = _data1.ToImmutableArray(); + } + + protected override async Task OnInitializedAsync() + { + _firstDateTime = await MeasurementService.GetFirstAggregatedMeasurementsDateTime(RoomId, _cancellationTokenSource.Token); + await UpdateAggregatedMeasurements(); _options1.Chart = new Chart() { Type = ChartType.RangeArea