From 85a4b7757ec5672977367113de3a8a2d04af199b Mon Sep 17 00:00:00 2001 From: Casper-NS Date: Tue, 5 Dec 2023 09:56:19 +0100 Subject: [PATCH 1/2] added an endpoint that provides the latest occupancy estimation --- .../GetLatestOccupancyResponse.cs | 27 +++++++++++++++++++ .../Controllers/MeasurementController.cs | 24 ++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 CentralHub.Api.Model/Responses/Measurements/GetLatestOccupancyResponse.cs diff --git a/CentralHub.Api.Model/Responses/Measurements/GetLatestOccupancyResponse.cs b/CentralHub.Api.Model/Responses/Measurements/GetLatestOccupancyResponse.cs new file mode 100644 index 0000000..2b6d487 --- /dev/null +++ b/CentralHub.Api.Model/Responses/Measurements/GetLatestOccupancyResponse.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace CentralHub.Api.Model.Responses.AggregatedMeasurements; + +public sealed class GetLatestOccupancyResponse +{ + [JsonConstructor] + public GetLatestOccupancyResponse(int? occupancy, bool success) + { + EstimatedOccupancy = occupancy; + Success = success; + } + + public bool Success { get; } + + public int? EstimatedOccupancy { get; } + + public static GetLatestOccupancyResponse CreateUnsuccessful() + { + return new GetLatestOccupancyResponse(null, false); + } + + public static GetLatestOccupancyResponse CreateSuccessful(int occupancy) + { + return new GetLatestOccupancyResponse(occupancy, true); + } +} \ No newline at end of file diff --git a/CentralHub.Api/Controllers/MeasurementController.cs b/CentralHub.Api/Controllers/MeasurementController.cs index 14e7323..ecee5ac 100644 --- a/CentralHub.Api/Controllers/MeasurementController.cs +++ b/CentralHub.Api/Controllers/MeasurementController.cs @@ -87,7 +87,29 @@ public async Task GetFirstAggreg return GetFirstAggregatedMeasurementsDateTimeResponse.CreateSuccessful(possibleFirstDateTime.Value); } - private static IReadOnlyList CreateMeasurements(IReadOnlyCollection aggregatedMeasurements) + [HttpGet("occupancy/latest")] + public async Task GetLatestEstimatedOccupancy( + int roomId, + float wifiDevicesPerPerson, + float bluetoothDevicesPerPerson, + CancellationToken cancellationToken) + { + var aggregatedMeasurements = (await measurementRepository.GetAggregatedMeasurementsAsync(roomId, cancellationToken)).Last(); + + if (aggregatedMeasurements == null) + { + return GetLatestOccupancyResponse.CreateUnsuccessful(); + } + + var occupancy = + (int)Math.Round( + bluetoothDevicesPerPerson == 0 ? 0 : aggregatedMeasurements.BluetoothCount / bluetoothDevicesPerPerson + + wifiDevicesPerPerson == 0 ? 0 : aggregatedMeasurements.BluetoothCount / wifiDevicesPerPerson); + + return GetLatestOccupancyResponse.CreateSuccessful(occupancy); + } + + private static IReadOnlyList CreateMeasurements(IEnumerable aggregatedMeasurements) { var recentAggregatedMeasurements = aggregatedMeasurements .Where(am => am.EndTime > (DateTime.UtcNow - TimeSpan.FromDays(1))) From b0bdb6a60234192b99a0bf0d0486cf4d528f8e20 Mon Sep 17 00:00:00 2001 From: Casper-NS Date: Tue, 5 Dec 2023 11:14:57 +0100 Subject: [PATCH 2/2] added a way to set the device settings used for calculating occupancy --- .../GetLatestOccupancyResponse.cs | 8 +-- .../SetDevicePerPersonResponse.cs | 29 ++++++++++ .../Controllers/MeasurementController.cs | 56 ++++++++++++++++--- 3 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 CentralHub.Api.Model/Responses/Measurements/SetDevicePerPersonResponse.cs diff --git a/CentralHub.Api.Model/Responses/Measurements/GetLatestOccupancyResponse.cs b/CentralHub.Api.Model/Responses/Measurements/GetLatestOccupancyResponse.cs index 2b6d487..a6f3e21 100644 --- a/CentralHub.Api.Model/Responses/Measurements/GetLatestOccupancyResponse.cs +++ b/CentralHub.Api.Model/Responses/Measurements/GetLatestOccupancyResponse.cs @@ -5,10 +5,10 @@ namespace CentralHub.Api.Model.Responses.AggregatedMeasurements; public sealed class GetLatestOccupancyResponse { [JsonConstructor] - public GetLatestOccupancyResponse(int? occupancy, bool success) + public GetLatestOccupancyResponse(bool success, int? occupancy) { - EstimatedOccupancy = occupancy; Success = success; + EstimatedOccupancy = occupancy; } public bool Success { get; } @@ -17,11 +17,11 @@ public GetLatestOccupancyResponse(int? occupancy, bool success) public static GetLatestOccupancyResponse CreateUnsuccessful() { - return new GetLatestOccupancyResponse(null, false); + return new GetLatestOccupancyResponse(false, null); } public static GetLatestOccupancyResponse CreateSuccessful(int occupancy) { - return new GetLatestOccupancyResponse(occupancy, true); + return new GetLatestOccupancyResponse(true, occupancy); } } \ No newline at end of file diff --git a/CentralHub.Api.Model/Responses/Measurements/SetDevicePerPersonResponse.cs b/CentralHub.Api.Model/Responses/Measurements/SetDevicePerPersonResponse.cs new file mode 100644 index 0000000..af467db --- /dev/null +++ b/CentralHub.Api.Model/Responses/Measurements/SetDevicePerPersonResponse.cs @@ -0,0 +1,29 @@ +using System.Reflection.Metadata; +using System.Text.Json.Serialization; + +namespace CentralHub.Api.Model.Responses.AggregatedMeasurements; + +public sealed class SetDevicesPerPersonResponse +{ + [JsonConstructor] + public SetDevicesPerPersonResponse(bool success, float? bluetoothDevicesPerPerson, float? wifiDevicesPerPerson) + { + Success = success; + BluetoothDevicesPerPerson = bluetoothDevicesPerPerson; + WifiDevicesPerPerson = wifiDevicesPerPerson; + } + + public bool Success { get; } + public float? BluetoothDevicesPerPerson { get; } + public float? WifiDevicesPerPerson { get; } + + public static SetDevicesPerPersonResponse CreateUnsuccessful() + { + return new SetDevicesPerPersonResponse(false, null, null); + } + + public static SetDevicesPerPersonResponse CreateSuccessful(float bluetoothDevicesPerPerson, float wifiDevicesPerPerson) + { + return new SetDevicesPerPersonResponse(true, bluetoothDevicesPerPerson, wifiDevicesPerPerson); + } +} \ No newline at end of file diff --git a/CentralHub.Api/Controllers/MeasurementController.cs b/CentralHub.Api/Controllers/MeasurementController.cs index ecee5ac..8810a37 100644 --- a/CentralHub.Api/Controllers/MeasurementController.cs +++ b/CentralHub.Api/Controllers/MeasurementController.cs @@ -4,10 +4,12 @@ using CentralHub.Api.Model.Responses.AggregatedMeasurements; using CentralHub.Api.Model.Responses.Measurements; using CentralHub.Api.Services; +using CentralHub.Api.Threading; using Microsoft.AspNetCore.Mvc; namespace CentralHub.Api.Controllers; + [ApiController] [Route("/measurements")] public sealed class MeasurementController( @@ -16,6 +18,10 @@ public sealed class MeasurementController( IMeasurementRepository measurementRepository) : ControllerBase { + private static CancellableMutex _occupancySettings = + new CancellableMutex( + new OccupancySettings(1.5f, 2.4f)); + [HttpPost("add")] public async Task AddMeasurements(AddMeasurementsRequest addMeasurementsRequest, CancellationToken token) { @@ -88,11 +94,7 @@ public async Task GetFirstAggreg } [HttpGet("occupancy/latest")] - public async Task GetLatestEstimatedOccupancy( - int roomId, - float wifiDevicesPerPerson, - float bluetoothDevicesPerPerson, - CancellationToken cancellationToken) + public async Task GetLatestEstimatedOccupancy(int roomId, CancellationToken cancellationToken) { var aggregatedMeasurements = (await measurementRepository.GetAggregatedMeasurementsAsync(roomId, cancellationToken)).Last(); @@ -101,14 +103,39 @@ public async Task GetLatestEstimatedOccupancy( return GetLatestOccupancyResponse.CreateUnsuccessful(); } - var occupancy = - (int)Math.Round( - bluetoothDevicesPerPerson == 0 ? 0 : aggregatedMeasurements.BluetoothCount / bluetoothDevicesPerPerson + - wifiDevicesPerPerson == 0 ? 0 : aggregatedMeasurements.BluetoothCount / wifiDevicesPerPerson); + var occupancy = await _occupancySettings.Lock(os => + { + return (int)Math.Round( + aggregatedMeasurements.BluetoothCount / os.BluetoothDevicesPerPerson + + aggregatedMeasurements.WifiCount / os.WifiDevicesPerPerson); + }, cancellationToken); + return GetLatestOccupancyResponse.CreateSuccessful(occupancy); } + [HttpPost("settings/set")] + public async Task SetDevicesPerPerson( + float bluetoothDevicesPerPerson, + float wifiDevicesPerPerson, + CancellationToken cancellationToken) + { + if (bluetoothDevicesPerPerson < 0 || wifiDevicesPerPerson < 0) + { + return SetDevicesPerPersonResponse.CreateUnsuccessful(); + } + + await _occupancySettings.Lock(os => + { + os.BluetoothDevicesPerPerson = bluetoothDevicesPerPerson; + os.WifiDevicesPerPerson = wifiDevicesPerPerson; + }, cancellationToken); + + return SetDevicesPerPersonResponse + .CreateSuccessful( + bluetoothDevicesPerPerson, wifiDevicesPerPerson); + } + private static IReadOnlyList CreateMeasurements(IEnumerable aggregatedMeasurements) { var recentAggregatedMeasurements = aggregatedMeasurements @@ -126,4 +153,15 @@ private static IReadOnlyList CreateMeasurements(IEnumera am.WifiCount - wifiCalibrationNumber) ).ToImmutableArray(); } + + private struct OccupancySettings + { + public OccupancySettings(float bluetoothDevicesPerPerson, float wifiDevicesPerPerson) + { + BluetoothDevicesPerPerson = bluetoothDevicesPerPerson; + WifiDevicesPerPerson = wifiDevicesPerPerson; + } + public float BluetoothDevicesPerPerson { get; set; } + public float WifiDevicesPerPerson { get; set; } + } }