From fa0ec5fa7c5dee6ef0407dc48f209e4637916439 Mon Sep 17 00:00:00 2001 From: dcancho Date: Thu, 14 Nov 2024 22:52:35 -0500 Subject: [PATCH 01/10] feat: add models and services for container status --- .../ContainerStatusReportCommand.cs | 28 +++++++++++++++---- .../Application/HealthMonitor.cs | 18 ++++++++++++ .../Models/Entities/ContainerHealthRecord.cs | 20 +++++++++++++ .../Models/Entities/ContainerStatusRecord.cs | 13 +++++++++ .../Domain/Models/ValueObjects/SensorType.cs | 7 +++++ .../Configuration/ApplicationDbContext.cs | 12 ++------ .../Interface/CloudServiceController.cs | 14 ++++++++-- 7 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 ContainerManagement/Application/HealthMonitor.cs create mode 100644 ContainerManagement/Domain/Models/Entities/ContainerHealthRecord.cs create mode 100644 ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs create mode 100644 ContainerManagement/Domain/Models/ValueObjects/SensorType.cs diff --git a/ContainerManagement/Application/ContainerStatusReportCommand.cs b/ContainerManagement/Application/ContainerStatusReportCommand.cs index 2c58624..4784e50 100644 --- a/ContainerManagement/Application/ContainerStatusReportCommand.cs +++ b/ContainerManagement/Application/ContainerStatusReportCommand.cs @@ -1,24 +1,40 @@ using System.ComponentModel.DataAnnotations; +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.ValueObjects; namespace DittoBox.EdgeServer.ContainerManagement.Application { public record ContainerStatusReportCommand { [Required] - public int ContainerId; + public string DeviceId { get; set; } [Required] public double? Temperature { get; set; } [Required] public double? Humidity { get; set; } [Required] - public double? Oxygen { get; set; } + public double? GasOxygen { get; set; } [Required] - public double? Dioxide { get; set; } + public double? GasCO2 { get; set; } [Required] - public double? Ethylene { get; set; } + public double? GasEthylene { get; set; } [Required] - public double? Ammonia { get; set; } + public double? GasAmmonia { get; set; } [Required] - public double? SulfurDioxide { get; set; } + public double? GasSO2 { get; set; } + [Required] + public HealthMonitor GasHealthMonitor { get; set; } + [Required] + public HealthMonitor TemperatureHealthMonitor { get; set; } + [Required] + public HealthMonitor HumidityHealthMonitor { get; set; } + + public ContainerStatusReportCommand() { + GasHealthMonitor.SensorType = SensorType.GAS_SENSOR; + GasHealthMonitor.SensorType = SensorType.GAS_SENSOR; + TemperatureHealthMonitor.SensorType = SensorType.TEMPERATURE_SENSOR; + TemperatureHealthMonitor.SensorType = SensorType.TEMPERATURE_SENSOR; + HumidityHealthMonitor.SensorType = SensorType.HUMIDITY_SENSOR; + HumidityHealthMonitor.SavedAt = DateTime.Now; + } } } diff --git a/ContainerManagement/Application/HealthMonitor.cs b/ContainerManagement/Application/HealthMonitor.cs new file mode 100644 index 0000000..ebf0ad6 --- /dev/null +++ b/ContainerManagement/Application/HealthMonitor.cs @@ -0,0 +1,18 @@ + +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.ValueObjects; + +namespace DittoBox.EdgeServer.ContainerManagement.Application +{ + public class HealthMonitor + { + public SensorType? SensorType { get; set; } + public int FailingRatePeriodCycles { get; set; } + public int FailuresSinceStartup { get; set; } + public int RemainingCycles { get; set; } + public int FailuresSinceLastCheck { get; set; } + public int RequestsSinceLastCheck { get; set; } + public int RequestsSinceStartup { get; set; } + public double FailingRate { get; set; } + public DateTime SavedAt { get; set; } + } +} \ No newline at end of file diff --git a/ContainerManagement/Domain/Models/Entities/ContainerHealthRecord.cs b/ContainerManagement/Domain/Models/Entities/ContainerHealthRecord.cs new file mode 100644 index 0000000..d5f69ec --- /dev/null +++ b/ContainerManagement/Domain/Models/Entities/ContainerHealthRecord.cs @@ -0,0 +1,20 @@ +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.ValueObjects; + +namespace DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; + +public class ContainerHealthRecord +{ + public int Id { get; set; } + public int ContainerId { get; set; } + public SensorType SensorType { get; set; } + public int FailingRatePeriodCycles { get; set; } + public int FailuresSinceStartup { get; set; } + public int RemainingCycles { get; set; } + public int FailuresSinceLastCheck { get; set; } + public int RequestsSinceLastCheck { get; set; } + public int RequestsSinceStartup { get; set; } + public double FailingRate { get; set; } + public DateTime SavedAt { get; set; } + + public ContainerHealthRecord() { } +} \ No newline at end of file diff --git a/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs b/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs new file mode 100644 index 0000000..66e222d --- /dev/null +++ b/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs @@ -0,0 +1,13 @@ +namespace DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; + +public class ContainerStatusRecord +{ + public int Id { get; set; } + public double Temperature { get; set; } + public double Humidity { get; set; } + public double GasOxygen { get; set; } + public double GasCO2 { get; set; } + public double GasEthylene { get; set; } + public double GasAmmonia { get; set; } + public double GasSO2 { get; set; } +} diff --git a/ContainerManagement/Domain/Models/ValueObjects/SensorType.cs b/ContainerManagement/Domain/Models/ValueObjects/SensorType.cs new file mode 100644 index 0000000..6c246ed --- /dev/null +++ b/ContainerManagement/Domain/Models/ValueObjects/SensorType.cs @@ -0,0 +1,7 @@ +namespace DittoBox.EdgeServer.ContainerManagement.Domain.Models.ValueObjects; + +public enum SensorType { + TEMPERATURE_SENSOR, + HUMIDITY_SENSOR, + GAS_SENSOR +} \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Configuration/ApplicationDbContext.cs b/ContainerManagement/Infrastructure/Configuration/ApplicationDbContext.cs index c53a415..c3c8943 100644 --- a/ContainerManagement/Infrastructure/Configuration/ApplicationDbContext.cs +++ b/ContainerManagement/Infrastructure/Configuration/ApplicationDbContext.cs @@ -2,15 +2,9 @@ namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; using Microsoft.EntityFrameworkCore; -public class ApplicationDbContext : DbContext +public class ApplicationDbContext(DbContextOptions options) : DbContext(options) { - public ApplicationDbContext(DbContextOptions options) : base(options) - { - } - public DbSet Containers { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - } + public DbSet ContainerStatusRecords { get; set; } + public DbSet ContainerHealthRecords { get; set; } } \ No newline at end of file diff --git a/ContainerManagement/Interface/CloudServiceController.cs b/ContainerManagement/Interface/CloudServiceController.cs index 83f5201..1cf0d71 100644 --- a/ContainerManagement/Interface/CloudServiceController.cs +++ b/ContainerManagement/Interface/CloudServiceController.cs @@ -1,13 +1,23 @@ -using Microsoft.AspNetCore.Mvc; +using DittoBox.EdgeServer.ContainerManagement.Application; +using Microsoft.AspNetCore.Mvc; // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 namespace DittoBox.EdgeServer.ContainerManagement.Interface { - [Route("api/[controller]")] + [Route("api/v1/cloud-service")] [ApiController] public class CloudServiceController : ControllerBase { // Containers send requests to this endpoint to forward them to the cloud service. The edge server will analyze and decide whether to forward the request to the cloud service or aggregate it with other requests. + + // POST api/v1/cloud-service/status + [HttpPost("status")] + public async Task PostStatusAsync([FromBody] ContainerStatusReportCommand command) { + + + return Ok(); + } + } } From 2ddb64a4d01b81075e2362993bee204494ccb03f Mon Sep 17 00:00:00 2001 From: dcancho Date: Fri, 15 Nov 2024 00:52:14 -0500 Subject: [PATCH 02/10] feat: add services and repositories --- .../IContainerStatusReportCommandHandler.cs | 7 ++++ .../ContainerStatusReportCommandHandler.cs | 15 +++++++++ .../Application/HealthMonitor.cs | 2 -- .../Application/Services/ContainerService.cs | 33 ++++++++++++++++--- .../Domain/Services/ICloudService.cs | 5 ++- .../Domain/Services/IContainerService.cs | 8 +++-- .../ContainerHealthRecordRepository.cs | 12 +++++++ .../Repositories/ContainerRepository.cs | 14 ++++++++ .../ContainerStatusRecordRepository.cs | 12 +++++++ .../IContainerHealthRecordRepository.cs | 10 ++++++ .../Repositories/IContainerRepository.cs | 10 ++++++ .../IContainerStatusRecordRepository.cs | 9 +++++ .../Interface/CloudServiceController.cs | 9 +++-- 13 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 ContainerManagement/Application/Handlers/Interfaces/IContainerStatusReportCommandHandler.cs create mode 100644 ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs create mode 100644 ContainerManagement/Infrastructure/Repositories/ContainerHealthRecordRepository.cs create mode 100644 ContainerManagement/Infrastructure/Repositories/ContainerRepository.cs create mode 100644 ContainerManagement/Infrastructure/Repositories/ContainerStatusRecordRepository.cs create mode 100644 ContainerManagement/Infrastructure/Repositories/IContainerHealthRecordRepository.cs create mode 100644 ContainerManagement/Infrastructure/Repositories/IContainerRepository.cs create mode 100644 ContainerManagement/Infrastructure/Repositories/IContainerStatusRecordRepository.cs diff --git a/ContainerManagement/Application/Handlers/Interfaces/IContainerStatusReportCommandHandler.cs b/ContainerManagement/Application/Handlers/Interfaces/IContainerStatusReportCommandHandler.cs new file mode 100644 index 0000000..c601845 --- /dev/null +++ b/ContainerManagement/Application/Handlers/Interfaces/IContainerStatusReportCommandHandler.cs @@ -0,0 +1,7 @@ +namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces +{ + public interface IContainerStatusReportCommandHandler + { + Task Handle(ContainerStatusReportCommand command); + } +} \ No newline at end of file diff --git a/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs new file mode 100644 index 0000000..b774c25 --- /dev/null +++ b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs @@ -0,0 +1,15 @@ +using DittoBox.EdgeServer.ContainerManagement.Application.Services; +using DittoBox.EdgeServer.ContainerManagement.Domain.Services; + +namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces +{ + public class ContainerStatusReportCommandHandler( + IContainerService containerService + ) : IContainerStatusReportCommandHandler + { + public Task Handle(ContainerStatusReportCommand command) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/ContainerManagement/Application/HealthMonitor.cs b/ContainerManagement/Application/HealthMonitor.cs index ebf0ad6..1b73532 100644 --- a/ContainerManagement/Application/HealthMonitor.cs +++ b/ContainerManagement/Application/HealthMonitor.cs @@ -6,9 +6,7 @@ namespace DittoBox.EdgeServer.ContainerManagement.Application public class HealthMonitor { public SensorType? SensorType { get; set; } - public int FailingRatePeriodCycles { get; set; } public int FailuresSinceStartup { get; set; } - public int RemainingCycles { get; set; } public int FailuresSinceLastCheck { get; set; } public int RequestsSinceLastCheck { get; set; } public int RequestsSinceStartup { get; set; } diff --git a/ContainerManagement/Application/Services/ContainerService.cs b/ContainerManagement/Application/Services/ContainerService.cs index ccd2868..6733681 100644 --- a/ContainerManagement/Application/Services/ContainerService.cs +++ b/ContainerManagement/Application/Services/ContainerService.cs @@ -1,8 +1,33 @@ -using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using System.ComponentModel; +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories; namespace DittoBox.EdgeServer.ContainerManagement.Application.Services { - public class ContainerService : BaseService, IContainerService - { - } + public class ContainerService( + IContainerHealthRecordRepository containerHealthRecordRepository, + IContainerStatusRecordRepository containerStatusRecordRepository + ) : BaseService, IContainerService + { + public Task ForwardNewTemplateSettings() + { + throw new NotImplementedException(); + } + + public Task IsReportToCloudRequired() + { + throw new NotImplementedException(); + } + + public Task SaveHealthReport(ContainerHealthRecord healthReport) + { + throw new NotImplementedException(); + } + + public Task SaveStatusReport(ContainerStatusRecord statusReport) + { + throw new NotImplementedException(); + } + } } diff --git a/ContainerManagement/Domain/Services/ICloudService.cs b/ContainerManagement/Domain/Services/ICloudService.cs index 8f31115..b2a0aa5 100644 --- a/ContainerManagement/Domain/Services/ICloudService.cs +++ b/ContainerManagement/Domain/Services/ICloudService.cs @@ -1,6 +1,9 @@ -namespace DittoBox.EdgeServer.ContainerManagement.Domain.Services +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; + +namespace DittoBox.EdgeServer.ContainerManagement.Domain.Services { public interface ICloudService { + public Task SendContainerStatusReport(ContainerStatusRecord record); } } diff --git a/ContainerManagement/Domain/Services/IContainerService.cs b/ContainerManagement/Domain/Services/IContainerService.cs index eecb4b5..c40e802 100644 --- a/ContainerManagement/Domain/Services/IContainerService.cs +++ b/ContainerManagement/Domain/Services/IContainerService.cs @@ -1,9 +1,13 @@ using DittoBox.EdgeServer.ContainerManagement.Application.Services; +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; namespace DittoBox.EdgeServer.ContainerManagement.Domain.Services { public interface IContainerService { - - } + public Task SaveHealthReport(ContainerHealthRecord healthReport); + public Task SaveStatusReport(ContainerStatusRecord statusReport); + public Task IsReportToCloudRequired(); + public Task ForwardNewTemplateSettings(); + } } diff --git a/ContainerManagement/Infrastructure/Repositories/ContainerHealthRecordRepository.cs b/ContainerManagement/Infrastructure/Repositories/ContainerHealthRecordRepository.cs new file mode 100644 index 0000000..ed4410a --- /dev/null +++ b/ContainerManagement/Infrastructure/Repositories/ContainerHealthRecordRepository.cs @@ -0,0 +1,12 @@ +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; + +namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories; + +public class ContainerHealthRecordRepository : BaseRepository, IContainerHealthRecordRepository +{ + public ContainerHealthRecordRepository(ApplicationDbContext dbContext) : base(dbContext) + { + } +} \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/ContainerRepository.cs b/ContainerManagement/Infrastructure/Repositories/ContainerRepository.cs new file mode 100644 index 0000000..2e05045 --- /dev/null +++ b/ContainerManagement/Infrastructure/Repositories/ContainerRepository.cs @@ -0,0 +1,14 @@ +using DittoBox.EdgeServer.ContainerManagement.Application.Services; +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; + +namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories +{ + public class ContainerRepository : BaseRepository, IContainerRepository + { + public ContainerRepository(ApplicationDbContext dbContext) : base(dbContext) + { + } + + } +} \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/ContainerStatusRecordRepository.cs b/ContainerManagement/Infrastructure/Repositories/ContainerStatusRecordRepository.cs new file mode 100644 index 0000000..746d2cf --- /dev/null +++ b/ContainerManagement/Infrastructure/Repositories/ContainerStatusRecordRepository.cs @@ -0,0 +1,12 @@ +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; + +namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories +{ + public class ContainerStatusRecordRepository : BaseRepository, IContainerStatusRecordRepository + { + public ContainerStatusRecordRepository(ApplicationDbContext dbContext) : base(dbContext) + { + } + } +} \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/IContainerHealthRecordRepository.cs b/ContainerManagement/Infrastructure/Repositories/IContainerHealthRecordRepository.cs new file mode 100644 index 0000000..7a5e1d1 --- /dev/null +++ b/ContainerManagement/Infrastructure/Repositories/IContainerHealthRecordRepository.cs @@ -0,0 +1,10 @@ +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories; + +namespace DittoBox.EdgeServer.ContainerManagement.Domain.Services +{ + public interface IContainerHealthRecordRepository : IBaseRepository + { + + } +} \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/IContainerRepository.cs b/ContainerManagement/Infrastructure/Repositories/IContainerRepository.cs new file mode 100644 index 0000000..3c0fd55 --- /dev/null +++ b/ContainerManagement/Infrastructure/Repositories/IContainerRepository.cs @@ -0,0 +1,10 @@ + +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories; + +namespace DittoBox.EdgeServer.ContainerManagement.Application.Services +{ + public interface IContainerRepository : IBaseRepository + { + } +} \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/IContainerStatusRecordRepository.cs b/ContainerManagement/Infrastructure/Repositories/IContainerStatusRecordRepository.cs new file mode 100644 index 0000000..ee2f1a6 --- /dev/null +++ b/ContainerManagement/Infrastructure/Repositories/IContainerStatusRecordRepository.cs @@ -0,0 +1,9 @@ +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; + +namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories +{ + public interface IContainerStatusRecordRepository : IBaseRepository + { + + } +} \ No newline at end of file diff --git a/ContainerManagement/Interface/CloudServiceController.cs b/ContainerManagement/Interface/CloudServiceController.cs index 1cf0d71..6c1b719 100644 --- a/ContainerManagement/Interface/CloudServiceController.cs +++ b/ContainerManagement/Interface/CloudServiceController.cs @@ -1,4 +1,5 @@ using DittoBox.EdgeServer.ContainerManagement.Application; +using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces; using Microsoft.AspNetCore.Mvc; // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 @@ -7,15 +8,17 @@ namespace DittoBox.EdgeServer.ContainerManagement.Interface { [Route("api/v1/cloud-service")] [ApiController] - public class CloudServiceController : ControllerBase + public class CloudServiceController( + IContainerStatusReportCommandHandler containerStatusReportCommandHandler, + ILogger logger + ) : ControllerBase { // Containers send requests to this endpoint to forward them to the cloud service. The edge server will analyze and decide whether to forward the request to the cloud service or aggregate it with other requests. // POST api/v1/cloud-service/status [HttpPost("status")] public async Task PostStatusAsync([FromBody] ContainerStatusReportCommand command) { - - + await containerStatusReportCommandHandler.Handle(command); return Ok(); } From 5ec0f96c9ea29afb0384057aeb125c3d526b5ae2 Mon Sep 17 00:00:00 2001 From: dcancho Date: Fri, 15 Nov 2024 11:20:33 -0500 Subject: [PATCH 03/10] feat: draft send container status --- .../ContainerStatusReportCommand.cs | 4 ++- .../ContainerStatusReportCommandHandler.cs | 25 ++++++++++++++++--- .../Application/Services/CloudService.cs | 13 +++++++--- .../Models/Entities/ContainerStatusRecord.cs | 14 +++++------ .../Domain/Services/IContainerService.cs | 1 + 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/ContainerManagement/Application/ContainerStatusReportCommand.cs b/ContainerManagement/Application/ContainerStatusReportCommand.cs index 4784e50..3f404e6 100644 --- a/ContainerManagement/Application/ContainerStatusReportCommand.cs +++ b/ContainerManagement/Application/ContainerStatusReportCommand.cs @@ -5,8 +5,10 @@ namespace DittoBox.EdgeServer.ContainerManagement.Application { public record ContainerStatusReportCommand { + [Required] + public int ContainerId { get; set; } [Required] - public string DeviceId { get; set; } + public string? DeviceId { get; set; } [Required] public double? Temperature { get; set; } [Required] diff --git a/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs index b774c25..858a5cd 100644 --- a/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs +++ b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs @@ -1,15 +1,34 @@ using DittoBox.EdgeServer.ContainerManagement.Application.Services; +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; using DittoBox.EdgeServer.ContainerManagement.Domain.Services; namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces { public class ContainerStatusReportCommandHandler( - IContainerService containerService + IContainerService containerService, + ICloudService cloudService ) : IContainerStatusReportCommandHandler { - public Task Handle(ContainerStatusReportCommand command) + public async Task Handle(ContainerStatusReportCommand command) { - throw new NotImplementedException(); + var container = await containerService.GetContainerById(command.ContainerId); + if (container == null) + { + throw new Exception("Container not found"); + } + + var record = new ContainerStatusRecord + { + Temperature = command.Temperature, + Humidity = command.Humidity, + GasOxygen = command.GasOxygen, + GasCO2 = command.GasCO2, + GasEthylene = command.GasEthylene, + GasAmmonia = command.GasAmmonia, + GasSO2 = command.GasSO2, + }; + + await cloudService.SendContainerStatusReport(record); } } } \ No newline at end of file diff --git a/ContainerManagement/Application/Services/CloudService.cs b/ContainerManagement/Application/Services/CloudService.cs index 89972a2..291eeb1 100644 --- a/ContainerManagement/Application/Services/CloudService.cs +++ b/ContainerManagement/Application/Services/CloudService.cs @@ -1,8 +1,13 @@ -using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Domain.Services; namespace DittoBox.EdgeServer.ContainerManagement.Application.Services { - public class CloudService : BaseService, ICloudService - { - } + public class CloudService : BaseService, ICloudService + { + public Task SendContainerStatusReport(ContainerStatusRecord record) + { + throw new NotImplementedException(); + } + } } diff --git a/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs b/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs index 66e222d..9358490 100644 --- a/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs +++ b/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs @@ -3,11 +3,11 @@ namespace DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; public class ContainerStatusRecord { public int Id { get; set; } - public double Temperature { get; set; } - public double Humidity { get; set; } - public double GasOxygen { get; set; } - public double GasCO2 { get; set; } - public double GasEthylene { get; set; } - public double GasAmmonia { get; set; } - public double GasSO2 { get; set; } + public double? Temperature { get; set; } + public double? Humidity { get; set; } + public double? GasOxygen { get; set; } + public double? GasCO2 { get; set; } + public double? GasEthylene { get; set; } + public double? GasAmmonia { get; set; } + public double? GasSO2 { get; set; } } diff --git a/ContainerManagement/Domain/Services/IContainerService.cs b/ContainerManagement/Domain/Services/IContainerService.cs index c40e802..fdf5a8f 100644 --- a/ContainerManagement/Domain/Services/IContainerService.cs +++ b/ContainerManagement/Domain/Services/IContainerService.cs @@ -9,5 +9,6 @@ public interface IContainerService public Task SaveStatusReport(ContainerStatusRecord statusReport); public Task IsReportToCloudRequired(); public Task ForwardNewTemplateSettings(); + public Task GetContainerById(int containerId); } } From 1a5553b4a9638609baed89ed5cb36dce65c00e3d Mon Sep 17 00:00:00 2001 From: Diego Cancho <102440160+dcancho@users.noreply.github.com> Date: Fri, 15 Nov 2024 21:11:18 -0500 Subject: [PATCH 04/10] Add or update the Azure App Service build and deployment workflow config --- .../develop_edge-dev-01-dittobox.yml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .github/workflows/develop_edge-dev-01-dittobox.yml diff --git a/.github/workflows/develop_edge-dev-01-dittobox.yml b/.github/workflows/develop_edge-dev-01-dittobox.yml new file mode 100644 index 0000000..b9a673b --- /dev/null +++ b/.github/workflows/develop_edge-dev-01-dittobox.yml @@ -0,0 +1,65 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy +# More GitHub Actions for Azure: https://github.com/Azure/actions + +name: Build and deploy ASP.Net Core app to Azure Web App - edge-dev-01-dittobox + +on: + push: + branches: + - develop + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up .NET Core + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.x' + + - name: Build with dotnet + run: dotnet build --configuration Release + + - name: dotnet publish + run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp + + - name: Upload artifact for deployment job + uses: actions/upload-artifact@v4 + with: + name: .net-app + path: ${{env.DOTNET_ROOT}}/myapp + + deploy: + runs-on: ubuntu-latest + needs: build + environment: + name: 'Production' + url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} + permissions: + id-token: write #This is required for requesting the JWT + + steps: + - name: Download artifact from build job + uses: actions/download-artifact@v4 + with: + name: .net-app + + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_233090D111F94CF38A1844536FC9A017 }} + tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_C1F177FC6AF84878AEF8459FD4E0E6C7 }} + subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_85141903D76C46FC8AE3CB521739A05E }} + + - name: Deploy to Azure Web App + id: deploy-to-webapp + uses: azure/webapps-deploy@v3 + with: + app-name: 'edge-dev-01-dittobox' + slot-name: 'Production' + package: . + \ No newline at end of file From 9f99dae841d25ca5aac98b4587bf1e303a62e11b Mon Sep 17 00:00:00 2001 From: dcancho Date: Fri, 15 Nov 2024 21:12:54 -0500 Subject: [PATCH 05/10] feat: add status report controller --- .../ContainerStatusReportCommand.cs | 15 +- .../ContainerStatusReportCommandHandler.cs | 128 ++++++++++++++---- .../Outbound/ContainerMetricsResource.cs | 29 ++++ .../Application/Services/BaseService.cs | 2 +- .../Application/Services/ContainerService.cs | 85 ++++++++++-- .../Domain/Models/Entities/Container.cs | 4 +- .../Models/Entities/ContainerHealthRecord.cs | 2 - .../Models/Entities/ContainerStatusRecord.cs | 6 +- .../Domain/Services/IContainerService.cs | 9 +- .../Configuration/IUnitOfWork.cs | 5 + .../Configuration/UnitOfWork.cs | 12 ++ .../Repositories/BaseRepository.cs | 74 +++++----- .../ContainerHealthRecordRepository.cs | 16 ++- .../Repositories/ContainerRepository.cs | 13 +- .../ContainerStatusRecordRepository.cs | 17 ++- .../Repositories/IBaseRepository.cs | 19 +-- .../IContainerHealthRecordRepository.cs | 4 +- .../Repositories/IContainerRepository.cs | 3 +- .../IContainerStatusRecordRepository.cs | 4 +- DittoBox.EdgeServer.csproj | 1 + Program.cs | 112 +++++++++------ appsettings.Development.json | 3 +- appsettings.json | 2 +- 23 files changed, 409 insertions(+), 156 deletions(-) create mode 100644 ContainerManagement/Application/Resources/Outbound/ContainerMetricsResource.cs create mode 100644 ContainerManagement/Infrastructure/Configuration/IUnitOfWork.cs create mode 100644 ContainerManagement/Infrastructure/Configuration/UnitOfWork.cs diff --git a/ContainerManagement/Application/ContainerStatusReportCommand.cs b/ContainerManagement/Application/ContainerStatusReportCommand.cs index 3f404e6..2e72d84 100644 --- a/ContainerManagement/Application/ContainerStatusReportCommand.cs +++ b/ContainerManagement/Application/ContainerStatusReportCommand.cs @@ -24,19 +24,10 @@ public record ContainerStatusReportCommand [Required] public double? GasSO2 { get; set; } [Required] - public HealthMonitor GasHealthMonitor { get; set; } + public HealthMonitor GasHealthMonitor { get; set; } = new HealthMonitor(); [Required] - public HealthMonitor TemperatureHealthMonitor { get; set; } + public HealthMonitor TemperatureHealthMonitor { get; set; } = new HealthMonitor(); [Required] - public HealthMonitor HumidityHealthMonitor { get; set; } - - public ContainerStatusReportCommand() { - GasHealthMonitor.SensorType = SensorType.GAS_SENSOR; - GasHealthMonitor.SensorType = SensorType.GAS_SENSOR; - TemperatureHealthMonitor.SensorType = SensorType.TEMPERATURE_SENSOR; - TemperatureHealthMonitor.SensorType = SensorType.TEMPERATURE_SENSOR; - HumidityHealthMonitor.SensorType = SensorType.HUMIDITY_SENSOR; - HumidityHealthMonitor.SavedAt = DateTime.Now; - } + public HealthMonitor HumidityHealthMonitor { get; set; } = new HealthMonitor(); } } diff --git a/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs index 858a5cd..fd638d0 100644 --- a/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs +++ b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs @@ -1,34 +1,114 @@ -using DittoBox.EdgeServer.ContainerManagement.Application.Services; using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.ValueObjects; using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces { public class ContainerStatusReportCommandHandler( IContainerService containerService, - ICloudService cloudService + ICloudService cloudService, + IUnitOfWork unitOfWork, + IConfiguration configuration ) : IContainerStatusReportCommandHandler { - public async Task Handle(ContainerStatusReportCommand command) - { - var container = await containerService.GetContainerById(command.ContainerId); - if (container == null) - { - throw new Exception("Container not found"); - } - - var record = new ContainerStatusRecord - { - Temperature = command.Temperature, - Humidity = command.Humidity, - GasOxygen = command.GasOxygen, - GasCO2 = command.GasCO2, - GasEthylene = command.GasEthylene, - GasAmmonia = command.GasAmmonia, - GasSO2 = command.GasSO2, - }; - - await cloudService.SendContainerStatusReport(record); - } - } + private readonly int _maxMillisecondsBetweenReports = Convert.ToInt32(Environment.GetEnvironmentVariable("MAX_MILLISECONDS_BETWEEN_REPORTS") ?? "60000"); + + public async Task Handle(ContainerStatusReportCommand command) + { + Container? container = await containerService.GetContainerById(command.ContainerId); + + // If not found by Id, try to find by DeviceId + if (container == null && !string.IsNullOrEmpty(command.DeviceId)) + { + container = await containerService.GetContainerByUIID(command.DeviceId); + } + + + // If still not found, create a new container + if (container == null && !string.IsNullOrEmpty(command.DeviceId)) + { + container = await containerService.CreateContainer(command.DeviceId); + await unitOfWork.CommitAsync(); + } + + // If still not found, throw an exception + if (container == null) + { + throw new Exception("Container not found. Couldn't create a new container."); + } + + // Generate the status report + var statusRecord = new ContainerStatusRecord + { + ContainerId = container.Id, + Temperature = command.Temperature, + Humidity = command.Humidity, + GasOxygen = command.GasOxygen, + GasCO2 = command.GasCO2, + GasEthylene = command.GasEthylene, + GasAmmonia = command.GasAmmonia, + GasSO2 = command.GasSO2, + SavedAt = DateTime.Now + }; + + // Generate the health reports + var healthRecords = new List + { + new() { + ContainerId = container.Id, + SensorType = SensorType.GAS_SENSOR, + FailuresSinceStartup = command.GasHealthMonitor.FailuresSinceStartup, + FailuresSinceLastCheck = command.GasHealthMonitor.FailuresSinceLastCheck, + RequestsSinceLastCheck = command.GasHealthMonitor.RequestsSinceLastCheck, + RequestsSinceStartup = command.GasHealthMonitor.RequestsSinceStartup, + FailingRate = command.GasHealthMonitor.FailingRate, + SavedAt = DateTime.Now + }, + new() { + ContainerId = container.Id, + SensorType = SensorType.TEMPERATURE_SENSOR, + FailuresSinceStartup = command.TemperatureHealthMonitor.FailuresSinceStartup, + FailuresSinceLastCheck = command.TemperatureHealthMonitor.FailuresSinceLastCheck, + RequestsSinceLastCheck = command.TemperatureHealthMonitor.RequestsSinceLastCheck, + RequestsSinceStartup = command.TemperatureHealthMonitor.RequestsSinceStartup, + FailingRate = command.TemperatureHealthMonitor.FailingRate, + SavedAt = DateTime.Now + }, + new() { + ContainerId = container.Id, + SensorType = SensorType.HUMIDITY_SENSOR, + FailuresSinceStartup = command.HumidityHealthMonitor.FailuresSinceStartup, + FailuresSinceLastCheck = command.HumidityHealthMonitor.FailuresSinceLastCheck, + RequestsSinceLastCheck = command.HumidityHealthMonitor.RequestsSinceLastCheck, + RequestsSinceStartup = command.HumidityHealthMonitor.RequestsSinceStartup, + FailingRate = command.HumidityHealthMonitor.FailingRate, + SavedAt = DateTime.Now + } + }; + + // Save the health reports + foreach (var healthRecord in healthRecords) + { + await containerService.SaveHealthReport(healthRecord); + } + + // Save the status report + await containerService.SaveStatusReport(statusRecord); + + // Commit the changes + await unitOfWork.CommitAsync(); + + // ---- + + // Send the report to the cloud if necessary + if (container.LastSentStatusReport == null || container.LastSentStatusReport < DateTime.Now.AddMinutes(-1)) + { + await containerService.SendReportToCloud(container.Id, _maxMillisecondsBetweenReports); + + } + + await unitOfWork.CommitAsync(); + } + } } \ No newline at end of file diff --git a/ContainerManagement/Application/Resources/Outbound/ContainerMetricsResource.cs b/ContainerManagement/Application/Resources/Outbound/ContainerMetricsResource.cs new file mode 100644 index 0000000..ce3fbd7 --- /dev/null +++ b/ContainerManagement/Application/Resources/Outbound/ContainerMetricsResource.cs @@ -0,0 +1,29 @@ +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; + +namespace DittoBox.EdgeServer.ContainerManagement.Application.Resources.Outbound +{ + public record ContainerMetricsResource + { + public double? Temperature { get; init; } + public double? Humidity { get; init; } + public double? Oxygen { get; init; } + public double? Dioxide { get; init; } + public double? Ethylene { get; init; } + public double? Ammonia { get; init; } + public double? SulfurDioxide { get; init; } + + public static ContainerMetricsResource FromContainerStatusRecord(ContainerStatusRecord containerStatusReport) + { + return new ContainerMetricsResource() + { + Temperature = containerStatusReport.Temperature, + Humidity = containerStatusReport.Humidity, + Oxygen = containerStatusReport.GasOxygen, + Dioxide = containerStatusReport.GasCO2, + Ethylene = containerStatusReport.GasEthylene, + Ammonia = containerStatusReport.GasAmmonia, + SulfurDioxide = containerStatusReport.GasSO2 + }; + } + } +} diff --git a/ContainerManagement/Application/Services/BaseService.cs b/ContainerManagement/Application/Services/BaseService.cs index d93cccc..cb054f8 100644 --- a/ContainerManagement/Application/Services/BaseService.cs +++ b/ContainerManagement/Application/Services/BaseService.cs @@ -2,6 +2,6 @@ { public abstract class BaseService { - protected string BaseUrl { get; set; } = "https://app-dev-01-dittobox-a8bpd5bkh4dnh3g7.eastus-01.azurewebsites.net/"; + protected string BaseUrl { get; set; } = "https://app-dev-01-dittobox-a8bpd5bkh4dnh3g7.eastus-01.azurewebsites.net/api/v1/"; } } diff --git a/ContainerManagement/Application/Services/ContainerService.cs b/ContainerManagement/Application/Services/ContainerService.cs index 6733681..ebc669d 100644 --- a/ContainerManagement/Application/Services/ContainerService.cs +++ b/ContainerManagement/Application/Services/ContainerService.cs @@ -1,33 +1,92 @@ -using System.ComponentModel; +using DittoBox.EdgeServer.ContainerManagement.Application.Resources.Outbound; using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories; namespace DittoBox.EdgeServer.ContainerManagement.Application.Services { public class ContainerService( - IContainerHealthRecordRepository containerHealthRecordRepository, - IContainerStatusRecordRepository containerStatusRecordRepository - ) : BaseService, IContainerService + IContainerRepository containerRepository, + IContainerHealthRecordRepository containerHealthRecordRepository, + IContainerStatusRecordRepository containerStatusRecordRepository, + ILogger logger + ) : BaseService, IContainerService { - public Task ForwardNewTemplateSettings() + public async Task CreateContainer(string uiid, string? macAddress = null) + { + var container = new Container() { UIID = uiid, MACAddress = macAddress }; + await containerRepository.Add(container); + return container; + + } + + public Task ForwardNewTemplateSettings() { throw new NotImplementedException(); } - public Task IsReportToCloudRequired() + public async Task GetContainerById(int containerId) + { + return await containerRepository.GetById(containerId); + } + + public Task GetContainerByUIID(string uiid) + { + return containerRepository.GetContainerByUIID(uiid); + } + + public Task IsReportToCloudRequired(int containerId) { throw new NotImplementedException(); } - public Task SaveHealthReport(ContainerHealthRecord healthReport) + public async Task SaveHealthReport(ContainerHealthRecord healthReport) { - throw new NotImplementedException(); - } + await containerHealthRecordRepository.Add(healthReport); + } - public Task SaveStatusReport(ContainerStatusRecord statusReport) + public async Task SaveStatusReport(ContainerStatusRecord statusReport) { - throw new NotImplementedException(); - } - } + await containerStatusRecordRepository.Add(statusReport); + } + + public async Task SendReportToCloud(int containerId, int timeframe) + { + // Send a report with a POST request to the cloud + var container = await containerRepository.GetById(containerId); + // Get the health report from the last 1 minute + var from = DateTime.Now.AddMinutes(-timeframe); + var statusReports = await containerStatusRecordRepository.GetLatestReportsByTime(containerId, from); + + // Send the report to the cloud + var client = new HttpClient(); + // Make a ContainerMetricsResource by the average of all the health reports + var statusResource = new ContainerMetricsResource() + { + Temperature = statusReports.Average(r => r.Temperature), + Humidity = statusReports.Average(r => r.Humidity), + Oxygen = statusReports.Average(r => r.GasOxygen), + Dioxide = statusReports.Average(r => r.GasCO2), + Ethylene = statusReports.Average(r => r.GasEthylene), + Ammonia = statusReports.Average(r => r.GasAmmonia), + SulfurDioxide = statusReports.Average(r => r.GasSO2) + }; + + var response = await client.PutAsJsonAsync(Path.Combine(BaseUrl, $"container/{container!.Id}/metrics"),statusResource); + if (response.IsSuccessStatusCode) + { + + container.LastSentStatusReport = DateTime.Now; + await containerRepository.Update(container); + logger.LogInformation($"Status report sent to cloud for container {container.Id} at {container.LastSentStatusReport}"); + return; + } + else + { + logger.LogError($"Failed to send report to cloud. Status code: {response.StatusCode}.\n{response.Content}"); + throw new Exception($"Failed to send report to cloud."); + } + } + } } diff --git a/ContainerManagement/Domain/Models/Entities/Container.cs b/ContainerManagement/Domain/Models/Entities/Container.cs index 8d32cf3..7a579f9 100644 --- a/ContainerManagement/Domain/Models/Entities/Container.cs +++ b/ContainerManagement/Domain/Models/Entities/Container.cs @@ -5,6 +5,6 @@ public class Container public int Id { get; set; } public string UIID { get; set; } // Unique internal identifier. Hardcoded value on each container microcontroller. public string? MACAddress { get; set; } // MAC address of the container microcontroller. - public DateTime LastReport { get; set; } // Last time the container microcontroller reported to the cloud. - public string? Name { get; set; } // Name of the container. + public DateTime? LastSentStatusReport { get; set; } // Last time a the container's status report was sent to the cloud. + public DateTime? LastSentHealthReport { get; set; } // Last time the container's health report was sent to the cloud. } \ No newline at end of file diff --git a/ContainerManagement/Domain/Models/Entities/ContainerHealthRecord.cs b/ContainerManagement/Domain/Models/Entities/ContainerHealthRecord.cs index d5f69ec..e3121b3 100644 --- a/ContainerManagement/Domain/Models/Entities/ContainerHealthRecord.cs +++ b/ContainerManagement/Domain/Models/Entities/ContainerHealthRecord.cs @@ -7,9 +7,7 @@ public class ContainerHealthRecord public int Id { get; set; } public int ContainerId { get; set; } public SensorType SensorType { get; set; } - public int FailingRatePeriodCycles { get; set; } public int FailuresSinceStartup { get; set; } - public int RemainingCycles { get; set; } public int FailuresSinceLastCheck { get; set; } public int RequestsSinceLastCheck { get; set; } public int RequestsSinceStartup { get; set; } diff --git a/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs b/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs index 9358490..231da96 100644 --- a/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs +++ b/ContainerManagement/Domain/Models/Entities/ContainerStatusRecord.cs @@ -3,11 +3,15 @@ namespace DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; public class ContainerStatusRecord { public int Id { get; set; } - public double? Temperature { get; set; } + public int ContainerId { get; set; } + public double? Temperature { get; set; } public double? Humidity { get; set; } public double? GasOxygen { get; set; } public double? GasCO2 { get; set; } public double? GasEthylene { get; set; } public double? GasAmmonia { get; set; } public double? GasSO2 { get; set; } + public DateTime SavedAt { get; set; } + + public ContainerStatusRecord() { } } diff --git a/ContainerManagement/Domain/Services/IContainerService.cs b/ContainerManagement/Domain/Services/IContainerService.cs index fdf5a8f..7fa636b 100644 --- a/ContainerManagement/Domain/Services/IContainerService.cs +++ b/ContainerManagement/Domain/Services/IContainerService.cs @@ -7,8 +7,11 @@ public interface IContainerService { public Task SaveHealthReport(ContainerHealthRecord healthReport); public Task SaveStatusReport(ContainerStatusRecord statusReport); - public Task IsReportToCloudRequired(); + public Task IsReportToCloudRequired(int containerId); public Task ForwardNewTemplateSettings(); - public Task GetContainerById(int containerId); - } + public Task GetContainerById(int containerId); + public Task GetContainerByUIID(string uiid); + public Task CreateContainer(string uiid, string? macAddress = null); + public Task SendReportToCloud(int containerId, int timeframe); + } } diff --git a/ContainerManagement/Infrastructure/Configuration/IUnitOfWork.cs b/ContainerManagement/Infrastructure/Configuration/IUnitOfWork.cs new file mode 100644 index 0000000..fa1bb0b --- /dev/null +++ b/ContainerManagement/Infrastructure/Configuration/IUnitOfWork.cs @@ -0,0 +1,5 @@ +namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; + +public interface IUnitOfWork { + Task CommitAsync(); +} \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Configuration/UnitOfWork.cs b/ContainerManagement/Infrastructure/Configuration/UnitOfWork.cs new file mode 100644 index 0000000..64e7928 --- /dev/null +++ b/ContainerManagement/Infrastructure/Configuration/UnitOfWork.cs @@ -0,0 +1,12 @@ + +namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; + +public class UnitOfWork(ApplicationDbContext context) : IUnitOfWork +{ + private readonly ApplicationDbContext context = context; + + public async Task CommitAsync() + { + await context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/BaseRepository.cs b/ContainerManagement/Infrastructure/Repositories/BaseRepository.cs index ea4c33f..7bd0f9e 100644 --- a/ContainerManagement/Infrastructure/Repositories/BaseRepository.cs +++ b/ContainerManagement/Infrastructure/Repositories/BaseRepository.cs @@ -2,40 +2,42 @@ using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories; using Microsoft.EntityFrameworkCore; -public abstract class BaseRepository : IBaseRepository where T : class + +public abstract class BaseRepository( + ApplicationDbContext context + ) : IBaseRepository where T : class { - protected readonly ApplicationDbContext _dbContext; - - public BaseRepository(ApplicationDbContext dbContext) - { - _dbContext = dbContext; - } - - public virtual async Task GetByIdAsync(Guid id) - { - return await _dbContext.Set().FindAsync(id); - } - - public virtual async Task> GetAllAsync() - { - return await _dbContext.Set().ToListAsync(); - } - - public virtual async Task AddAsync(T entity) - { - await _dbContext.Set().AddAsync(entity); - await _dbContext.SaveChangesAsync(); - } - - public virtual async Task UpdateAsync(T entity) - { - _dbContext.Set().Update(entity); - await _dbContext.SaveChangesAsync(); - } - - public virtual async Task DeleteAsync(T entity) - { - _dbContext.Set().Remove(entity); - await _dbContext.SaveChangesAsync(); - } -} \ No newline at end of file + protected ApplicationDbContext context = context; + + public async Task Add(T entity) + { + await context.Set().AddAsync(entity); + } + + public async Task Delete(T entity) + { + context.Set().Remove(entity); + await Task.CompletedTask; + } + + public async Task> GetAll() + { + return await context.Set().ToListAsync(); + } + + public async Task GetById(int id) + { + return await context.Set().FindAsync(id); + } + + public Task Update(T entity) + { + context.Set().Update(entity); + return Task.CompletedTask; + } + + public Task> GetAllSync() + { + return Task.FromResult((IQueryable)context.Set()); + } +} diff --git a/ContainerManagement/Infrastructure/Repositories/ContainerHealthRecordRepository.cs b/ContainerManagement/Infrastructure/Repositories/ContainerHealthRecordRepository.cs index ed4410a..feaebb2 100644 --- a/ContainerManagement/Infrastructure/Repositories/ContainerHealthRecordRepository.cs +++ b/ContainerManagement/Infrastructure/Repositories/ContainerHealthRecordRepository.cs @@ -1,12 +1,26 @@ using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; using DittoBox.EdgeServer.ContainerManagement.Domain.Services; using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; +using Microsoft.EntityFrameworkCore; namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories; public class ContainerHealthRecordRepository : BaseRepository, IContainerHealthRecordRepository { - public ContainerHealthRecordRepository(ApplicationDbContext dbContext) : base(dbContext) + public ContainerHealthRecordRepository(ApplicationDbContext dbContext) : base(dbContext) { } + + public async Task> GetLatestReportsByTime(int containerId, DateTime from, DateTime? to = null) + { + if (to == null) + { + to = DateTime.Now; + } + + return await context.ContainerHealthRecords + .Where(x => x.ContainerId == containerId && x.SavedAt >= from && x.SavedAt <= to) + .OrderByDescending(x => x.SavedAt) + .ToListAsync(); + } } \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/ContainerRepository.cs b/ContainerManagement/Infrastructure/Repositories/ContainerRepository.cs index 2e05045..566e218 100644 --- a/ContainerManagement/Infrastructure/Repositories/ContainerRepository.cs +++ b/ContainerManagement/Infrastructure/Repositories/ContainerRepository.cs @@ -1,14 +1,15 @@ using DittoBox.EdgeServer.ContainerManagement.Application.Services; using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; +using Microsoft.EntityFrameworkCore; namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories { - public class ContainerRepository : BaseRepository, IContainerRepository + public class ContainerRepository(ApplicationDbContext dbContext) : BaseRepository(dbContext), IContainerRepository { - public ContainerRepository(ApplicationDbContext dbContext) : base(dbContext) - { - } - - } + public Task GetContainerByUIID(string uiid) + { + return context.Containers.FirstOrDefaultAsync(c => c.UIID == uiid); + } + } } \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/ContainerStatusRecordRepository.cs b/ContainerManagement/Infrastructure/Repositories/ContainerStatusRecordRepository.cs index 746d2cf..5ee13bc 100644 --- a/ContainerManagement/Infrastructure/Repositories/ContainerStatusRecordRepository.cs +++ b/ContainerManagement/Infrastructure/Repositories/ContainerStatusRecordRepository.cs @@ -1,5 +1,6 @@ using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; +using Microsoft.EntityFrameworkCore; namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories { @@ -8,5 +9,19 @@ public class ContainerStatusRecordRepository : BaseRepository> GetLatestReportsByTime(int containerId, DateTime from, DateTime? to) + { + if (to == null) + { + to = DateTime.Now; + } + + return await context.ContainerStatusRecords + .Where(x => x.ContainerId == containerId && x.SavedAt >= from && x.SavedAt <= to) + .OrderByDescending(x => x.SavedAt).ToListAsync(); + } + + + } } \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/IBaseRepository.cs b/ContainerManagement/Infrastructure/Repositories/IBaseRepository.cs index 00cc00c..e1c07c6 100644 --- a/ContainerManagement/Infrastructure/Repositories/IBaseRepository.cs +++ b/ContainerManagement/Infrastructure/Repositories/IBaseRepository.cs @@ -1,11 +1,12 @@ -namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories +namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories; +public interface IBaseRepository where T : class { - public interface IBaseRepository where T : class - { - Task GetByIdAsync(Guid id); - Task> GetAllAsync(); - Task AddAsync(T entity); - Task UpdateAsync(T entity); - Task DeleteAsync(T entity); - } + Task GetById(int id); + Task> GetAll(); + Task Add(T entity); + Task Update(T entity); + Task Delete(T entity); + + Task> GetAllSync(); + } \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/IContainerHealthRecordRepository.cs b/ContainerManagement/Infrastructure/Repositories/IContainerHealthRecordRepository.cs index 7a5e1d1..28dcb17 100644 --- a/ContainerManagement/Infrastructure/Repositories/IContainerHealthRecordRepository.cs +++ b/ContainerManagement/Infrastructure/Repositories/IContainerHealthRecordRepository.cs @@ -5,6 +5,6 @@ namespace DittoBox.EdgeServer.ContainerManagement.Domain.Services { public interface IContainerHealthRecordRepository : IBaseRepository { - - } + public Task> GetLatestReportsByTime(int containerId, DateTime from, DateTime? to = null); + } } \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/IContainerRepository.cs b/ContainerManagement/Infrastructure/Repositories/IContainerRepository.cs index 3c0fd55..f1d7213 100644 --- a/ContainerManagement/Infrastructure/Repositories/IContainerRepository.cs +++ b/ContainerManagement/Infrastructure/Repositories/IContainerRepository.cs @@ -6,5 +6,6 @@ namespace DittoBox.EdgeServer.ContainerManagement.Application.Services { public interface IContainerRepository : IBaseRepository { - } + public Task GetContainerByUIID(string uiid); + } } \ No newline at end of file diff --git a/ContainerManagement/Infrastructure/Repositories/IContainerStatusRecordRepository.cs b/ContainerManagement/Infrastructure/Repositories/IContainerStatusRecordRepository.cs index ee2f1a6..2b4754e 100644 --- a/ContainerManagement/Infrastructure/Repositories/IContainerStatusRecordRepository.cs +++ b/ContainerManagement/Infrastructure/Repositories/IContainerStatusRecordRepository.cs @@ -4,6 +4,6 @@ namespace DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories { public interface IContainerStatusRecordRepository : IBaseRepository { - - } + public Task> GetLatestReportsByTime(int containerId, DateTime from, DateTime? to = null); + } } \ No newline at end of file diff --git a/DittoBox.EdgeServer.csproj b/DittoBox.EdgeServer.csproj index 9d436fa..ca9f73c 100644 --- a/DittoBox.EdgeServer.csproj +++ b/DittoBox.EdgeServer.csproj @@ -9,6 +9,7 @@ + diff --git a/Program.cs b/Program.cs index 00ff539..1356e99 100644 --- a/Program.cs +++ b/Program.cs @@ -1,44 +1,80 @@ -var builder = WebApplication.CreateBuilder(args); +using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces; +using DittoBox.EdgeServer.ContainerManagement.Application.Services; +using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Repositories; +using Microsoft.EntityFrameworkCore; -// Add services to the container. -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +namespace DittoBox.EdgeServer; -var app = builder.Build(); - -// Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) +public class Program { - app.UseSwagger(); - app.UseSwaggerUI(); -} + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); -app.UseHttpsRedirection(); + builder.Services.AddControllers(); + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + builder.Configuration.AddUserSecrets(); -var summaries = new[] -{ - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" -}; + var folder = Environment.SpecialFolder.LocalApplicationData; + var path = Environment.GetFolderPath(folder); + var sqliteConnectionString = $"Data Source={Path.Join(path, "edge-server.db")}"; -app.MapGet("/weatherforecast", () => -{ - var forecast = Enumerable.Range(1, 5).Select(index => - new WeatherForecast - ( - DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - Random.Shared.Next(-20, 55), - summaries[Random.Shared.Next(summaries.Length)] - )) - .ToArray(); - return forecast; -}) -.WithName("GetWeatherForecast") -.WithOpenApi(); - -app.Run(); - -record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) -{ - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); -} + if (string.IsNullOrEmpty(sqliteConnectionString)) + { + throw new Exception("SQLITE_CONNECTION_STRING environment variable not set"); + } + + builder.Services.AddDbContext(options => + { + options.UseSqlite(sqliteConnectionString); + }); + + builder.Services.AddScoped(); + + builder.Services.AddCors(options => + { + options.AddPolicy("AllowAll", corsPolicyBuilder => + { + corsPolicyBuilder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + }); + }); + + ConfigureServices(builder); + + var app = builder.Build(); + app.UseSwagger(); + app.UseSwaggerUI(); + + using (var scope = app.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); + } + + app.UseHttpsRedirection(); + + app.UseCors("AllowAll"); + + app.MapControllers(); + + app.Run(); + + } + + private static void ConfigureServices(WebApplicationBuilder builder) + { + builder.Services.AddScoped(); + + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + + builder.Services.AddScoped(); + } +} \ No newline at end of file diff --git a/appsettings.Development.json b/appsettings.Development.json index 0c208ae..54151b6 100644 --- a/appsettings.Development.json +++ b/appsettings.Development.json @@ -2,7 +2,8 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Microsoft.AspNetCore": "None", + "Microsoft.EntityFrameworkCore": "Warning" } } } diff --git a/appsettings.json b/appsettings.json index 10f68b8..51c11b2 100644 --- a/appsettings.json +++ b/appsettings.json @@ -1,7 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", + "Default": "Warning", "Microsoft.AspNetCore": "Warning" } }, From 0983e32a31761de750704c55edb13d3ebb15b480 Mon Sep 17 00:00:00 2001 From: dcancho Date: Sat, 16 Nov 2024 02:53:51 -0500 Subject: [PATCH 06/10] feat: review self register endpoint --- .../ContainerSelfRegisterCommand.cs | 10 ++++++++ .../Application/GetContainersQuery.cs | 8 +++++++ .../IContainerSelfRegisterCommandHandler.cs | 7 ++++++ .../Interfaces/IGetContainersQueryHandler.cs | 9 ++++++++ .../ContainerSelfRegisterCommandHandler.cs | 18 +++++++++++++++ .../ContainerStatusReportCommandHandler.cs | 14 +++-------- .../Internal/GetContainersQueryHandler.cs | 17 ++++++++++++++ .../ContainerRegistrationResource.cs | 8 +++++++ .../Application/Services/CloudService.cs | 23 ++++++++++++++++++- .../Application/Services/ContainerService.cs | 11 ++++++--- .../Domain/Models/Entities/Container.cs | 1 + .../Domain/Services/ICloudService.cs | 11 +++++---- .../Domain/Services/IContainerService.cs | 3 ++- .../Interface/CloudServiceController.cs | 8 ++++++- .../Interface/ContainerController.cs | 13 +++++++++-- Program.cs | 4 ++++ 16 files changed, 142 insertions(+), 23 deletions(-) create mode 100644 ContainerManagement/Application/ContainerSelfRegisterCommand.cs create mode 100644 ContainerManagement/Application/GetContainersQuery.cs create mode 100644 ContainerManagement/Application/Handlers/Interfaces/IContainerSelfRegisterCommandHandler.cs create mode 100644 ContainerManagement/Application/Handlers/Interfaces/IGetContainersQueryHandler.cs create mode 100644 ContainerManagement/Application/Handlers/Internal/ContainerSelfRegisterCommandHandler.cs create mode 100644 ContainerManagement/Application/Handlers/Internal/GetContainersQueryHandler.cs create mode 100644 ContainerManagement/Application/Resources/ContainerRegistrationResource.cs diff --git a/ContainerManagement/Application/ContainerSelfRegisterCommand.cs b/ContainerManagement/Application/ContainerSelfRegisterCommand.cs new file mode 100644 index 0000000..3ba8821 --- /dev/null +++ b/ContainerManagement/Application/ContainerSelfRegisterCommand.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace DittoBox.EdgeServer.ContainerManagement.Application +{ + public record ContainerSelfRegisterCommand + { + [Required] + public string Uiid { get; set; } + } +} diff --git a/ContainerManagement/Application/GetContainersQuery.cs b/ContainerManagement/Application/GetContainersQuery.cs new file mode 100644 index 0000000..5e2caa2 --- /dev/null +++ b/ContainerManagement/Application/GetContainersQuery.cs @@ -0,0 +1,8 @@ +using System.ComponentModel.DataAnnotations; + +namespace DittoBox.EdgeServer.ContainerManagement.Application +{ + public record GetContainersQuery + { + } +} diff --git a/ContainerManagement/Application/Handlers/Interfaces/IContainerSelfRegisterCommandHandler.cs b/ContainerManagement/Application/Handlers/Interfaces/IContainerSelfRegisterCommandHandler.cs new file mode 100644 index 0000000..f5740f2 --- /dev/null +++ b/ContainerManagement/Application/Handlers/Interfaces/IContainerSelfRegisterCommandHandler.cs @@ -0,0 +1,7 @@ +namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces +{ + public interface IContainerSelfRegisterCommandHandler + { + Task Handle(ContainerSelfRegisterCommand command); + } +} \ No newline at end of file diff --git a/ContainerManagement/Application/Handlers/Interfaces/IGetContainersQueryHandler.cs b/ContainerManagement/Application/Handlers/Interfaces/IGetContainersQueryHandler.cs new file mode 100644 index 0000000..6eed271 --- /dev/null +++ b/ContainerManagement/Application/Handlers/Interfaces/IGetContainersQueryHandler.cs @@ -0,0 +1,9 @@ +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; + +namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces +{ + public interface IGetContainersQueryHandler + { + Task> Handle(); + } +} \ No newline at end of file diff --git a/ContainerManagement/Application/Handlers/Internal/ContainerSelfRegisterCommandHandler.cs b/ContainerManagement/Application/Handlers/Internal/ContainerSelfRegisterCommandHandler.cs new file mode 100644 index 0000000..f8dbf29 --- /dev/null +++ b/ContainerManagement/Application/Handlers/Internal/ContainerSelfRegisterCommandHandler.cs @@ -0,0 +1,18 @@ +using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces; +using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; + +namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Internal +{ + public class ContainerSelfRegisterCommandHandler( + ICloudService cloudService, + IUnitOfWork unitOfWork + ) : IContainerSelfRegisterCommandHandler + { + public async Task Handle(ContainerSelfRegisterCommand command) + { + await cloudService.RegisterContainer(command.Uiid); + await unitOfWork.CommitAsync(); + } + } +} \ No newline at end of file diff --git a/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs index fd638d0..131f296 100644 --- a/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs +++ b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs @@ -1,15 +1,15 @@ +using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces; using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; using DittoBox.EdgeServer.ContainerManagement.Domain.Models.ValueObjects; using DittoBox.EdgeServer.ContainerManagement.Domain.Services; using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; -namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces +namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Internal { public class ContainerStatusReportCommandHandler( IContainerService containerService, ICloudService cloudService, - IUnitOfWork unitOfWork, - IConfiguration configuration + IUnitOfWork unitOfWork ) : IContainerStatusReportCommandHandler { private readonly int _maxMillisecondsBetweenReports = Convert.ToInt32(Environment.GetEnvironmentVariable("MAX_MILLISECONDS_BETWEEN_REPORTS") ?? "60000"); @@ -24,14 +24,6 @@ public async Task Handle(ContainerStatusReportCommand command) container = await containerService.GetContainerByUIID(command.DeviceId); } - - // If still not found, create a new container - if (container == null && !string.IsNullOrEmpty(command.DeviceId)) - { - container = await containerService.CreateContainer(command.DeviceId); - await unitOfWork.CommitAsync(); - } - // If still not found, throw an exception if (container == null) { diff --git a/ContainerManagement/Application/Handlers/Internal/GetContainersQueryHandler.cs b/ContainerManagement/Application/Handlers/Internal/GetContainersQueryHandler.cs new file mode 100644 index 0000000..752193e --- /dev/null +++ b/ContainerManagement/Application/Handlers/Internal/GetContainersQueryHandler.cs @@ -0,0 +1,17 @@ +using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces; +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; + +namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Internal +{ + public class GetContainersQueryHandler( + IContainerService containerService + ) : IGetContainersQueryHandler + { + public async Task> Handle() + { + return await containerService.GetContainers(); + } + } +} \ No newline at end of file diff --git a/ContainerManagement/Application/Resources/ContainerRegistrationResource.cs b/ContainerManagement/Application/Resources/ContainerRegistrationResource.cs new file mode 100644 index 0000000..9236432 --- /dev/null +++ b/ContainerManagement/Application/Resources/ContainerRegistrationResource.cs @@ -0,0 +1,8 @@ +namespace DittoBox.EdgeServer.ContainerManagement.Application.Resources +{ + public record ContainerRegistrationResource + { + public int Id { get; init; } + public string Uiid { get; init; } + } +} diff --git a/ContainerManagement/Application/Services/CloudService.cs b/ContainerManagement/Application/Services/CloudService.cs index 291eeb1..2a24e6e 100644 --- a/ContainerManagement/Application/Services/CloudService.cs +++ b/ContainerManagement/Application/Services/CloudService.cs @@ -1,13 +1,34 @@ using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; using DittoBox.EdgeServer.ContainerManagement.Domain.Services; +using DittoBox.EdgeServer.ContainerManagement.Application.Resources; namespace DittoBox.EdgeServer.ContainerManagement.Application.Services { - public class CloudService : BaseService, ICloudService + public class CloudService( + IContainerService containerService + ) : BaseService, ICloudService { public Task SendContainerStatusReport(ContainerStatusRecord record) { throw new NotImplementedException(); } + + public async Task RegisterContainer(string uiid) + { + var resource = new { uiid = uiid }; + var client = new HttpClient(); + var response = await client.PostAsJsonAsync(Path.Combine(BaseUrl, "group/register-container"), resource); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync(); + await containerService.CreateContainer(result.Uiid, result.Id); + return result; + } + else + { + Console.WriteLine($"Failed to register container: {response.StatusCode}. {response.Content}. \n\n {resource} "); + throw new Exception("Failed to register container"); + } + } } } diff --git a/ContainerManagement/Application/Services/ContainerService.cs b/ContainerManagement/Application/Services/ContainerService.cs index ebc669d..eaf05bd 100644 --- a/ContainerManagement/Application/Services/ContainerService.cs +++ b/ContainerManagement/Application/Services/ContainerService.cs @@ -13,9 +13,9 @@ public class ContainerService( ILogger logger ) : BaseService, IContainerService { - public async Task CreateContainer(string uiid, string? macAddress = null) + public async Task CreateContainer(string uiid, int idInCloud) { - var container = new Container() { UIID = uiid, MACAddress = macAddress }; + var container = new Container() { UIID = uiid, IdInCloudService = idInCloud }; await containerRepository.Add(container); return container; @@ -36,7 +36,12 @@ public Task ForwardNewTemplateSettings() return containerRepository.GetContainerByUIID(uiid); } - public Task IsReportToCloudRequired(int containerId) + public async Task> GetContainers() + { + return await containerRepository.GetAll(); + } + + public Task IsReportToCloudRequired(int containerId) { throw new NotImplementedException(); } diff --git a/ContainerManagement/Domain/Models/Entities/Container.cs b/ContainerManagement/Domain/Models/Entities/Container.cs index 7a579f9..5d5146e 100644 --- a/ContainerManagement/Domain/Models/Entities/Container.cs +++ b/ContainerManagement/Domain/Models/Entities/Container.cs @@ -3,6 +3,7 @@ namespace DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; public class Container { public int Id { get; set; } + public int IdInCloudService { get; set; } // Id of the container in the cloud service. public string UIID { get; set; } // Unique internal identifier. Hardcoded value on each container microcontroller. public string? MACAddress { get; set; } // MAC address of the container microcontroller. public DateTime? LastSentStatusReport { get; set; } // Last time a the container's status report was sent to the cloud. diff --git a/ContainerManagement/Domain/Services/ICloudService.cs b/ContainerManagement/Domain/Services/ICloudService.cs index b2a0aa5..56f2986 100644 --- a/ContainerManagement/Domain/Services/ICloudService.cs +++ b/ContainerManagement/Domain/Services/ICloudService.cs @@ -1,9 +1,12 @@ -using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; +using DittoBox.EdgeServer.ContainerManagement.Application.Resources; +using DittoBox.EdgeServer.ContainerManagement.Domain.Models.Entities; namespace DittoBox.EdgeServer.ContainerManagement.Domain.Services { - public interface ICloudService - { + public interface ICloudService + { public Task SendContainerStatusReport(ContainerStatusRecord record); - } + public Task RegisterContainer(string uiid); + + } } diff --git a/ContainerManagement/Domain/Services/IContainerService.cs b/ContainerManagement/Domain/Services/IContainerService.cs index 7fa636b..419ffd8 100644 --- a/ContainerManagement/Domain/Services/IContainerService.cs +++ b/ContainerManagement/Domain/Services/IContainerService.cs @@ -10,8 +10,9 @@ public interface IContainerService public Task IsReportToCloudRequired(int containerId); public Task ForwardNewTemplateSettings(); public Task GetContainerById(int containerId); + public Task> GetContainers(); public Task GetContainerByUIID(string uiid); - public Task CreateContainer(string uiid, string? macAddress = null); + public Task CreateContainer(string uiid, int idInCloud); public Task SendReportToCloud(int containerId, int timeframe); } } diff --git a/ContainerManagement/Interface/CloudServiceController.cs b/ContainerManagement/Interface/CloudServiceController.cs index 6c1b719..8ccee49 100644 --- a/ContainerManagement/Interface/CloudServiceController.cs +++ b/ContainerManagement/Interface/CloudServiceController.cs @@ -10,17 +10,23 @@ namespace DittoBox.EdgeServer.ContainerManagement.Interface [ApiController] public class CloudServiceController( IContainerStatusReportCommandHandler containerStatusReportCommandHandler, + IContainerSelfRegisterCommandHandler containerSelfRegisterCommandHandler, ILogger logger ) : ControllerBase { // Containers send requests to this endpoint to forward them to the cloud service. The edge server will analyze and decide whether to forward the request to the cloud service or aggregate it with other requests. // POST api/v1/cloud-service/status - [HttpPost("status")] + [HttpPost("send-container-status")] public async Task PostStatusAsync([FromBody] ContainerStatusReportCommand command) { await containerStatusReportCommandHandler.Handle(command); return Ok(); } + [HttpPost("self-register-container")] + public async Task PostSelfRegisterContainerAsync([FromBody] ContainerSelfRegisterCommand command) { + await containerSelfRegisterCommandHandler.Handle(command); + return Ok(); + } } } diff --git a/ContainerManagement/Interface/ContainerController.cs b/ContainerManagement/Interface/ContainerController.cs index 9eb1136..14685ae 100644 --- a/ContainerManagement/Interface/ContainerController.cs +++ b/ContainerManagement/Interface/ContainerController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces; +using Microsoft.AspNetCore.Mvc; // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 @@ -6,8 +7,16 @@ namespace DittoBox.EdgeServer.ContainerManagement.Interface { [Route("api/[controller]")] [ApiController] - public class ContainerController : ControllerBase + public class ContainerController( + IGetContainersQueryHandler getContainersQueryHandler + ) : ControllerBase { // Cloud Service sends requests to this endpoint to forward them to the container service. The edge server will analyze and decide whether to forward the request to the container service or aggregate it with other requests. Most likely it will just forward the request to the container. + + [HttpGet] + public async Task GetRegisteredContainers() { + var response = await getContainersQueryHandler.Handle(); + return Ok(response); + } } } diff --git a/Program.cs b/Program.cs index 1356e99..006d597 100644 --- a/Program.cs +++ b/Program.cs @@ -1,4 +1,5 @@ using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces; +using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Internal; using DittoBox.EdgeServer.ContainerManagement.Application.Services; using DittoBox.EdgeServer.ContainerManagement.Domain.Services; using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; @@ -53,6 +54,7 @@ public static void Main(string[] args) using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); + // db.Database.EnsureDeleted(); db.Database.EnsureCreated(); } @@ -69,6 +71,8 @@ public static void Main(string[] args) private static void ConfigureServices(WebApplicationBuilder builder) { builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); From aac3863236d4063f4a5b0050c03882b012fc5efb Mon Sep 17 00:00:00 2001 From: Diego Cancho <102440160+dcancho@users.noreply.github.com> Date: Sat, 16 Nov 2024 10:33:45 -0500 Subject: [PATCH 07/10] Update Program.cs --- Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Program.cs b/Program.cs index 006d597..f89dcb3 100644 --- a/Program.cs +++ b/Program.cs @@ -54,7 +54,7 @@ public static void Main(string[] args) using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); - // db.Database.EnsureDeleted(); + db.Database.EnsureDeleted(); db.Database.EnsureCreated(); } @@ -81,4 +81,4 @@ private static void ConfigureServices(WebApplicationBuilder builder) builder.Services.AddScoped(); } -} \ No newline at end of file +} From 9657dce8ba6fcb0634a3763bcdd18f81d27f339d Mon Sep 17 00:00:00 2001 From: dcancho Date: Sun, 17 Nov 2024 10:19:25 -0500 Subject: [PATCH 08/10] fix: change posting path --- .../Interfaces/IContainerSelfRegisterCommandHandler.cs | 4 +++- .../Internal/ContainerSelfRegisterCommandHandler.cs | 6 ++++-- ContainerManagement/Application/Services/BaseService.cs | 3 ++- .../Application/Services/ContainerService.cs | 3 +-- ContainerManagement/Interface/CloudServiceController.cs | 7 ++++--- Program.cs | 4 +++- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/ContainerManagement/Application/Handlers/Interfaces/IContainerSelfRegisterCommandHandler.cs b/ContainerManagement/Application/Handlers/Interfaces/IContainerSelfRegisterCommandHandler.cs index f5740f2..895c12b 100644 --- a/ContainerManagement/Application/Handlers/Interfaces/IContainerSelfRegisterCommandHandler.cs +++ b/ContainerManagement/Application/Handlers/Interfaces/IContainerSelfRegisterCommandHandler.cs @@ -1,7 +1,9 @@ +using DittoBox.EdgeServer.ContainerManagement.Application.Resources; + namespace DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces { public interface IContainerSelfRegisterCommandHandler { - Task Handle(ContainerSelfRegisterCommand command); + Task Handle(ContainerSelfRegisterCommand command); } } \ No newline at end of file diff --git a/ContainerManagement/Application/Handlers/Internal/ContainerSelfRegisterCommandHandler.cs b/ContainerManagement/Application/Handlers/Internal/ContainerSelfRegisterCommandHandler.cs index f8dbf29..375bb1c 100644 --- a/ContainerManagement/Application/Handlers/Internal/ContainerSelfRegisterCommandHandler.cs +++ b/ContainerManagement/Application/Handlers/Internal/ContainerSelfRegisterCommandHandler.cs @@ -1,4 +1,5 @@ using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces; +using DittoBox.EdgeServer.ContainerManagement.Application.Resources; using DittoBox.EdgeServer.ContainerManagement.Domain.Services; using DittoBox.EdgeServer.ContainerManagement.Infrastructure.Configuration; @@ -9,10 +10,11 @@ public class ContainerSelfRegisterCommandHandler( IUnitOfWork unitOfWork ) : IContainerSelfRegisterCommandHandler { - public async Task Handle(ContainerSelfRegisterCommand command) + public async Task Handle(ContainerSelfRegisterCommand command) { - await cloudService.RegisterContainer(command.Uiid); + var result = await cloudService.RegisterContainer(command.Uiid); await unitOfWork.CommitAsync(); + return result; } } } \ No newline at end of file diff --git a/ContainerManagement/Application/Services/BaseService.cs b/ContainerManagement/Application/Services/BaseService.cs index cb054f8..86a9b50 100644 --- a/ContainerManagement/Application/Services/BaseService.cs +++ b/ContainerManagement/Application/Services/BaseService.cs @@ -3,5 +3,6 @@ public abstract class BaseService { protected string BaseUrl { get; set; } = "https://app-dev-01-dittobox-a8bpd5bkh4dnh3g7.eastus-01.azurewebsites.net/api/v1/"; - } + + } } diff --git a/ContainerManagement/Application/Services/ContainerService.cs b/ContainerManagement/Application/Services/ContainerService.cs index eaf05bd..520d649 100644 --- a/ContainerManagement/Application/Services/ContainerService.cs +++ b/ContainerManagement/Application/Services/ContainerService.cs @@ -18,7 +18,6 @@ public async Task CreateContainer(string uiid, int idInCloud) var container = new Container() { UIID = uiid, IdInCloudService = idInCloud }; await containerRepository.Add(container); return container; - } public Task ForwardNewTemplateSettings() @@ -78,7 +77,7 @@ public async Task SendReportToCloud(int containerId, int timeframe) SulfurDioxide = statusReports.Average(r => r.GasSO2) }; - var response = await client.PutAsJsonAsync(Path.Combine(BaseUrl, $"container/{container!.Id}/metrics"),statusResource); + var response = await client.PutAsJsonAsync(Path.Combine(BaseUrl, $"container/{container!.IdInCloudService}/metrics"),statusResource); if (response.IsSuccessStatusCode) { diff --git a/ContainerManagement/Interface/CloudServiceController.cs b/ContainerManagement/Interface/CloudServiceController.cs index 8ccee49..a6ee9e0 100644 --- a/ContainerManagement/Interface/CloudServiceController.cs +++ b/ContainerManagement/Interface/CloudServiceController.cs @@ -1,5 +1,6 @@ using DittoBox.EdgeServer.ContainerManagement.Application; using DittoBox.EdgeServer.ContainerManagement.Application.Handlers.Interfaces; +using DittoBox.EdgeServer.ContainerManagement.Application.Resources; using Microsoft.AspNetCore.Mvc; // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 @@ -24,9 +25,9 @@ public async Task PostStatusAsync([FromBody] ContainerStatusRepor } [HttpPost("self-register-container")] - public async Task PostSelfRegisterContainerAsync([FromBody] ContainerSelfRegisterCommand command) { - await containerSelfRegisterCommandHandler.Handle(command); - return Ok(); + public async Task> PostSelfRegisterContainerAsync([FromBody] ContainerSelfRegisterCommand command) { + var result = await containerSelfRegisterCommandHandler.Handle(command); + return Ok(result); } } } diff --git a/Program.cs b/Program.cs index 006d597..c038bff 100644 --- a/Program.cs +++ b/Program.cs @@ -54,7 +54,9 @@ public static void Main(string[] args) using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); - // db.Database.EnsureDeleted(); + if (Environment.GetEnvironmentVariable("RESET_DATABASE") == "true") { + db.Database.EnsureDeleted(); + } db.Database.EnsureCreated(); } From c68223ff22ffe120c79d29ca414d185eb98aec1c Mon Sep 17 00:00:00 2001 From: dcancho Date: Sun, 17 Nov 2024 19:09:06 -0500 Subject: [PATCH 09/10] feat: update container report timeframe --- .../Handlers/Internal/ContainerStatusReportCommandHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs index 131f296..f23f93e 100644 --- a/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs +++ b/ContainerManagement/Application/Handlers/Internal/ContainerStatusReportCommandHandler.cs @@ -94,7 +94,7 @@ public async Task Handle(ContainerStatusReportCommand command) // ---- // Send the report to the cloud if necessary - if (container.LastSentStatusReport == null || container.LastSentStatusReport < DateTime.Now.AddMinutes(-1)) + if (container.LastSentStatusReport == null || container.LastSentStatusReport < DateTime.Now.AddMilliseconds(-_maxMillisecondsBetweenReports)) { await containerService.SendReportToCloud(container.Id, _maxMillisecondsBetweenReports); From d8fd66d4fc7e983516041befe4fd939cb1291601 Mon Sep 17 00:00:00 2001 From: dcancho Date: Sun, 17 Nov 2024 23:00:11 -0500 Subject: [PATCH 10/10] fix: change endpoint url --- ContainerManagement/Application/Services/BaseService.cs | 2 +- ContainerManagement/Interface/ContainerController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ContainerManagement/Application/Services/BaseService.cs b/ContainerManagement/Application/Services/BaseService.cs index 86a9b50..a101737 100644 --- a/ContainerManagement/Application/Services/BaseService.cs +++ b/ContainerManagement/Application/Services/BaseService.cs @@ -2,7 +2,7 @@ { public abstract class BaseService { - protected string BaseUrl { get; set; } = "https://app-dev-01-dittobox-a8bpd5bkh4dnh3g7.eastus-01.azurewebsites.net/api/v1/"; + protected string BaseUrl { get; set; } = "https://app-prod-01-dittobox-argeesg8era0c7ex.eastus-01.azurewebsites.net/api/v1/"; } } diff --git a/ContainerManagement/Interface/ContainerController.cs b/ContainerManagement/Interface/ContainerController.cs index 14685ae..7c3d2d0 100644 --- a/ContainerManagement/Interface/ContainerController.cs +++ b/ContainerManagement/Interface/ContainerController.cs @@ -5,7 +5,7 @@ namespace DittoBox.EdgeServer.ContainerManagement.Interface { - [Route("api/[controller]")] + [Route("api/v1/container")] [ApiController] public class ContainerController( IGetContainersQueryHandler getContainersQueryHandler