Informatique

Comment calculer sa facture d'électricité

Voici les éléments à considérer :

1.  Consommation mensuelle (en kWh) : Notée C.

2.  Prix du kWh Hors Taxes (HT) : Noté PkWh​.

3.  Coût de l'abonnement mensuel HT : Noté A.

4.  Taxes et contributions :

o   Accise sur l'électricité (ex-CSPE) : Depuis le 1ᵉʳ février 2025, le taux est de 26,23 €/MWh, soit 0,02623 €/kWh .Primeo Énergie+1Selectra+1

o   Contribution Tarifaire d’Acheminement (CTA) : Cette contribution est calculée à partir de la part fixe hors taxes du Tarif d’Utilisation des Réseaux Publics d’Électricité (TURPE). Pour les consommateurs raccordés au réseau public de distribution (comme c'est le cas pour les particuliers), le taux est de 21,93% de cette part fixe .Fournisseurs d'électricité+5Selectra+5Hellowatt+5

o   Taxe sur la Consommation Finale d'Électricité (TCFE) : Cette taxe est spécifique à chaque commune. Pour connaître le taux exact applicable à Saint-Jean-de-Braye, il est recommandé de consulter votre facture actuelle ou de contacter votre fournisseur d'électricité.

o   Taxe sur la Valeur Ajoutée (TVA) :

§  5,5% sur le montant de l'abonnement HT et sur la CTA.

§  20% sur le montant de la consommation HT, l'accise sur l'électricité et la TCFE.

Formules de calcul :

1.       Montant de la consommation HT : MC = C × PkWh

2.       Montant de l'accise sur l'électricité : MAccise = C × 0,02623

3.       Montant de la TCFE : MTCFE = C × Taux TCFE (le taux spécifique doit être obtenu auprès de votre fournisseur)

4.       Montant de la CTA : MCTA = A × 21,93 %

5.       TVA :

o   TVA à 5,5% : MTVA 5,5% = (A + MCTA ) × 5,5%

o   TVA à 20% : MTVA 20% = ( MC + MAccise + MTCFE) × 20%

6.       Montant total de la facture TTC : MTotal = A + MC + MAccise + MTCFE + MCTA + MTVA 5,5% + MTVA 20%

7.       Exemple de calcul :

Supposons les données suivantes :

·         Consommation mensuelle : 300 kWh​

·         Prix du kWh HT : 0,15 €​

·         Abonnement mensuel HT : 10 €​

·         TCFE : supposons 0,009 € par kWh (à vérifier pour Saint-Jean-de-Braye)​

Calculs :

1.          MC = 300 × 0,15 = 45 €

2.          M Accise = 300 × 0,02623 = 7,869 €

3.          MTCFE = 300 × 0,009 = 2,7 €

4.          MCTA = 10 × 21,93% = 2,193 €

5.          MTVA 5,5% = ( 10 + 2,193 ) × 5,5% = 0,671 €

6.          MTVA 20% = ( 45 + 7,869 + 2, 7) × 20% = 11,114 €

7.          M Total = 10 + 45 + 7,869 +2,7 + 2,193 + 0,671 + 11,114 = 79,547 €

Ainsi, la facture mensuelle TTC serait d'environ 79,55 €.

Clean Architecture in ASP .NET Core Web API

 

What is Clean Architecture?

Clean Architecture is a design pattern that separates an application into different layers based on their responsibility. It’s a way of organizing your code into independent, testable, and reusable components. This architecture pattern is a software design methodology that emphasizes the separation of concerns and separates the application into distinct modules.

The primary objective of Clean Architecture is to create a structure that makes it easy to manage and maintain an application as it grows and changes over time. It also makes it easy to add new features, fix bugs, and make changes to existing functionality without affecting the rest of the application.

Implementing Clean Architecture in ASP .NET Core Web API

To apply Clean Architecture, we can divide the application into four primary layers:

  1. Domain Layer → the project that contains the domain layer, including the entities, value objects, and domain services
  2. Application Layer → the project that contains the application layer and implements the application services, DTOs (data transfer objects), and mappers. It should reference the Domain project.
  3. Infrastructure Layer → The project contains the infrastructure layer, including the implementation of data access, logging, email, and other communication mechanisms. It should reference the Application project.
  4. Presentation Layer → The main project contains the presentation layer and implements the ASP.NET Core web API. It should reference the Application and Infrastructure projects.

Project Structure

The project structure for a Clean Architecture in ASP.NET Core Web API:

Domain Layer

The domain layer represents the application’s core, encapsulating business rules, entities, and domain-specific logic. It should be technology-agnostic and contain no dependencies on external frameworks or libraries.

There are two folders inside the Domain project: Common and Entities.

■ Common
The Common folder is used to store BaseEntity class and other aggregates:

public class BaseEntity
 {
     [Key]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public Guid ID { get; set; }
     public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.Now;
     public DateTimeOffset? DateUpdated { get; set; }
     public DateTimeOffset? DateDeleted { get; set; }
     public Guid UserID { get; set; }
     public bool IsDeleted { get; set; } = false;
 }

Domain
All domain entities are stored inside the Entities folder:

public class TestTakers : BaseEntity
 {
     public string Email { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
     public string FormNumber { get; set; }
     public string BannerID { get; set; }
 }

Application Layer

The application layer contains the business logic and use cases of the application. It acts as an intermediary between the presentation layer and the domain layer. This layer is independent of any specific UI or infrastructure concerns.

■ Features
The Features folder inside the application layer is a standard convention in Clean Architecture that provides a way to organize the application code by functional feature. This makes it easier to understand the application’s overall structure and maintain the codebase over time.

Infrastructure Layer

The infrastructure layer deals with external concerns such as databases, external services, and frameworks. It contains implementations of interfaces defined in the application layer and interacts with external resources.

Presentation Layer

This layer is responsible for handling user interactions and delivering data to the user interface. In a .NET Core Web API, this layer comprises the controllers and other components that handle HTTP requests and responses.

Blazor Clean Architecture | .NET 8, C# 12 | CQRS + MediatR

 

In the evolving landscape of web development, Blazor stands out as a revolutionary framework that empowers developers to build rich, interactive web applications with the elegance of C# and the power of .NET 8. By harnessing the capabilities of Single-Page Applications (SPAs), Blazor provides a robust environment that caters to both client-side and server-side execution, ensuring fast, real-time updates and seamless user experiences. This article delves into the intricacies of Blazor, exploring its architecture, including Clean Architecture, the CQRS pattern with MediatR, and the innovative Vertical Slice Architecture that streamlines the development process.

Repo: https://github.com/RobHutton/Blazor-Clean-Architecture

Ask me why I love Blazor.

I love Blazor development because it is a complete Single-Page Application (SPA) UI framework for building rich, interactive web applications using .NET 8 framework and C# 12.

Blazor offers flexibility with options like WebAssembly for full client-side execution and Blazor Server for fast, real-time updates. With Server-Side Rendering (SSR), it improves performance and SEO, and Blazor Auto dynamically selects the best rendering mode. It enables C# coding everywhere, is blazing fast, and eliminates the need for JavaScript for most tasks.

Blazor Hosting Models

Blazor Server Side Rendered (SSR) Mode generates and sends static HTML from the server to the client, and the rendering happens entirely on the server. After the initial render, the client can interact with the server for more updates.

  • Rendering: Server-side.
  • Interactivity: Typically involves round-trips to the server for user interactions (like clicking buttons), unless JavaScript is used.
  • Performance: Faster initial load because only HTML is sent. However, user interactions may feel slower because every action requires a server round-trip.
  • Use Case: Suitable for pages where SEO and quick first-page load are important.

Stream Rendering allows parts of a Blazor application to be progressively rendered and sent to the client as they are completed, rather than waiting for the entire page to be processed.

  • Rendering: Server-side, but streamed in chunks.
  • Interactivity: User can start interacting with visible parts of the UI while the rest of the page is still being rendered.
  • Performance: Improves perceived load times by sending the initial parts of the page early. User interactivity is enabled as sections become available.
  • Use Case: Ideal for pages with heavy content or complex UI where rendering different parts at different times provides a smoother experience.

Blazor Server Components run on the server but update the UI in the browser via a real-time SignalR connection. These components render HTML on the server and send a “render tree” (UI diff) to the client for updates.

  • Rendering: Server-side, with real-time updates via SignalR.
  • Interactivity: Highly interactive, as the user interacts with the UI, the changes are processed on the server, and only the UI diffs are sent back to the client.
  • Performance: Faster interactivity compared to SSR, but requires a constant server connection and low latency for the best performance.
  • Use Case: Ideal for internal business applications where maintaining a connection and real-time updates are crucial, and you want to avoid downloading the full app to the client.

Blazor Web Assembly Components (WASM) run entirely in the client’s browser using WebAssembly. The components are downloaded as part of the initial application, and the rendering logic happens client-side.

  • Rendering: Client-side.
  • Interactivity: All interactions are handled client-side, which results in faster interactions because there’s no need to contact the server for each action.
  • Performance: Slower initial load (due to downloading the application), but very fast once the app is fully loaded in the browser. Can run offline.
  • Use Case: Great for applications that require rich client-side interactivity, offline capabilities, or where a persistent server connection isn’t feasible.

Auto Mode (Server to WebAssembly Transition) Blazor can start rendering components on the server (like Blazor Server) and then transition to WebAssembly on the client when the WebAssembly runtime becomes available.

  • Rendering: Begins with server-side rendering (SSR), then switches to client-side WebAssembly rendering as the WASM runtime is downloaded.
  • Interactivity: Initially, interactions are processed server-side, but after transitioning to WebAssembly, all interactivity happens on the client.
  • Performance: Faster initial load because the application starts as an SSR app, and then transitions to a client-side WebAssembly app to provide a more interactive experience without server round-trips.
  • Use Case: Suitable for apps that want the best of both worlds — fast initial loads with SSR and fast interactivity with WASM after the transition. This mode is beneficial when you want SEO support and fast interactions after the initial page load.

What is Clean Architecture?

Presentation/UI => Infrastructure => Application => Domain

Clean Architecture organizes a project in such a way that ensures separation of concerns by decoupling different aspects of the application. Clean Architecture typically includes the following Layers, which are included in the Solution as separate projects:

Domain Layer (Entities)

Inner-most layer represents the core business objects of the application. These entities capture business concepts and are independent of other layers.
References:

  • Project References: None
  • Example: In a Blazor app managing orders, an Order class would be an entity.

Application Layer (Use Cases)

Contains the business rules and defines what operations can be performed using the entities.

  • Project References: Domain Layer
  • Example: A use case for placing an order might include order validation, updating inventory, and sending notifications.

Infrastructure Layer

Implements the interfaces to interact with external systems like databases, APIs, or file systems.

  • Project References: Application Layer
  • Example: An implementation of IOrderRepository using Entity Framework or Dapper to access a SQL database.

Presentation/UI Layer

Handles user interaction and displays data using Razor Components in Blazor. This layer communicates with use cases to retrieve or modify data.

  • Project References: Infrastructure Layer
  • Example: Blazor components such as OrderList.razor or OrderDetails.razor are part of this layer, fetching and displaying order data.

CQRS + MediatR Pattern

CQRS is a software pattern that separates reading (query) and writing (command) logic, which further promotes separation of concerns and decoupling. In addition, the CQRS pattern allows us to optimize database reads and writes separately for scalability, caching and data consistency.

The Mediator Pattern is a behavioral design pattern that helps in reducinthe coupling between objects by ensuring that objects communicate with each other through a mediator instead of directly.

  • Command: The component sends a Command (e.g., CreateOrderCommand) to the Mediator. The Mediator routes this command to the correct Handler in the Application Layer. The Handler performs the business logic, interacting with the Domain Layer or Infrastructure Layer to persist data.
  • Query: If a query is made (e.g., fetching orders), a Query (e.g., GetOrdersQuery) is sent to the Mediator, which routes it to the corresponding QueryHandler that fetches data and returns it.

Example: This example creates an ICommand and ICommandHandler using MediatR and wrapping the results with OK or Fail.

public interface ICommand : IRequest<Result>
{
}
public interface ICommand<TResponse> : IRequest<Result<TResponse>>
{
}

// CQRS Pattern
// Command Handler for commands that do not return any specific result other than success or failure
public interface ICommandHandler<TCommand> : IRequestHandler<TCommand, Result>
    where TCommand : ICommand
{
}
// CQRS Pattern
// Command Handler for commands that do return a specific response type
public interface ICommandHandler<TCommand, TResponse> : IRequestHandler<TCommand, Result<TResponse>>
    where TCommand : ICommand<TResponse>
{
}

Example: Executing a Command using ICommandHandler. This example uses Microsoft.Identity for authorization and Mapster Auto-Mapper to convert result to Data Transformation Object, which is a simple and lightweight object used to communicate between Layers.

// Article Data Transformation Object
// Used to Communicate with the Client
public record struct ArticleDto(
    int Id,
    string Title,
    string? Content,
    DateTime DatePublished,
    bool IsPublished,
    string UserName,
    string UserId,
    bool CanEdit
)
{ }

// Command Parameters
public class CreateArticleCommand : ICommand<ArticleDto>
{
    public required string Title { get; set; }
    public required string Content { get; set; }
    public DateTime DatePublished { get; set; } = DateTime.Now;
    public bool IsPublished { get; set; } = false;
}

public class CreateArticleCommandHandler : ICommandHandler<CreateArticleCommand, ArticleDto>
{
    private readonly IArticleRepository _articleRepository;
    private readonly IUserService _userService;
    public CreateArticleCommandHandler(IArticleRepository articleRepository, IUserService userService)
    {
        _articleRepository = articleRepository;
        _userService = userService;
    }
    public async Task<Result<ArticleDto>> Handle(CreateArticleCommand request, CancellationToken cancellationToken)
    {
        try
        {
            var newArticle = request.Adapt<Article>();
            newArticle.UserId = await _userService.GetCurrentUserIdAsync();
            if (!await _userService.CurrentUserCanCreateArticleAsync())
            {
                return FailingResult("You are not authorized to create an article.");
            }
            var article = await _articleRepository.CreateArticleAsync(newArticle);
            return article.Adapt<ArticleDto>();
        }
        catch (UserNotAuthorizedException)
        {
            return FailingResult("An error occurred creating the article.");
        }

    }
    private Result<ArticleDto> FailingResult(string msg)
    {
        return Result.Fail<ArticleDto>(msg ?? "Failed to create article.");
    }
}

Vertical Slice Architecture

With Blazor development, I typically prefer a Vertical Slice Architecture, which organizes source code by feature rather than by technology. This approach promotes better separation of concerns, enhances maintainability, and allows teams to work more autonomously.

 
  • Feature-Focused: Each vertical slice contains everything needed to implement a specific feature or functionality, including UI, business logic, and data access code.
  • Independently Deployable: Since each slice is a self-contained unit, it can be developed, tested, and deployed independently, making continuous integration and deployment easier.
  • Enhanced Collaboration: Teams can work on different slices concurrently, reducing dependencies and enabling faster delivery of features.
  • Improved Maintainability: By grouping related code together, it becomes easier to understand and maintain the functionality associated with a specific feature.
  • Easier Testing: Testing can be more straightforward since each slice can be tested in isolation, ensuring that all components related to a feature are working as intended.

Example: In the Application Layer, there is a folder called Articles that handles all of the article-related functionality.

In conclusion, Blazor represents a significant leap forward in web application development, marrying the benefits of modern frameworks with the reliability of C#. Its flexible architecture, combined with patterns like Clean Architecture and CQRS, enhances maintainability and scalability, allowing developers to create high-quality applications with ease. As Blazor continues to evolve, its potential to transform the development landscape is undeniable, making it an essential tool for any developer looking to build the next generation of web applications. Embracing Blazor not only facilitates faster development cycles but also fosters a deeper connection with the underlying business logic, enabling the creation of truly impactful digital experiences.

AutoMapper in .NET

In .NET 6, you can use the built-in dependency injection (DI) container along with AutoMapper to set up mapping profiles. Here’s a step-by-step guide on how to configure dependency injection and AutoMapper in a .NET 6 program:

1. Create a new .NET 6 console application or web application (MVC, API, etc.) or use an existing one.

2. Install the required NuGet packages if you haven’t already:

dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection

3. Define your data models and DTOs. For example:

// Data model
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

// Data Transfer Object (DTO)
public class ProductDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

4. Create AutoMapper profiles to define how your data models should be mapped to DTOs. Create a class that extends Profile and configure your mappings. For example:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Product, ProductDto>();
    }
}

Alternatively your file might look like this

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<ProductDocument, ProductDocument_DTO>();
        CreateMap<ProductModelDocument, ProductModelDocument_DTO>()
            .ForMember(dest => dest.Variants, opt => opt.Ignore());
   }
}

Or even simpler like that

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<CollectionHeader_DTO,  CollectionHeader>().ReverseMap();
        CreateMap<ProductEntry_Header_DTO, ProductEntry_Header>().ReverseMap();
   }
}

5. Configure AutoMapper and Dependency Injection in your program’s Program.cs file. Here's a sample Program.cs: 

using AutoMapper;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;

// Add AutoMapper with a custom mapping profile
services.AddAutoMapper(typeof(MappingProfile));

// Configure other services
// ...

var app = builder.Build();

// Configure the app
// ...

app.Run();

6. With this setup, you can now use dependency injection to inject IMapper into your services or controllers:

public class ProductService
{
    private readonly IMapper _mapper;

    public ProductService(IMapper mapper)
    {
        _mapper = mapper;
    }

    public ProductDto MapProductToDto(Product product)
    {
        return _mapper.Map<ProductDto>(product);
    }
}

That’s it! You’ve set up AutoMapper and Dependency Injection in your .NET 6 program. You can now use ProductService (or any other service) to map your data models to DTOs.

DTO et AutoMapper

En C# (.NET), DTO signifie Data Transfer Object.

Un DTO est un objet utilisé pour transférer des données entre différentes couches d'une application, par exemple entre la couche de service et la couche de présentation. Les DTO sont généralement des objets simples qui contiennent uniquement des propriétés et ne possèdent pas de logique métier.

Pourquoi utiliser des DTO ?

  1. Performance : Réduire la quantité de données transférées en sélectionnant uniquement les champs nécessaires.
  2. Sécurité : Éviter d'exposer directement les entités de la base de données.
  3. Simplicité : Faciliter la sérialisation et le transfert de données via des API ou des services.
  4. Découplage : Isoler les modèles de domaine des modèles d'affichage ou d'API.

Exemple de DTO en C#

public class UserDto
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string Email { get; set; }
}

Exemple d'utilisation

Supposons que vous ayez une entité utilisateur dans votre base de données, mais que vous ne souhaitiez exposer que certains champs :

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public DateTime CreatedAt { get; set; }
}

Pour transférer les données à travers une API sans exposer les informations sensibles comme le mot de passe, vous pouvez mapper l'entité vers un DTO :

UserDto dto = new UserDto
{
    Id = user.Id,
    Username = user.Username,
    Email = user.Email
};

En résumé, les DTO sont très utiles pour transférer des données de manière sécurisée et efficace, en isolant les modèles de domaine des modèles d'affichage ou d'API.

 

AutoMapper est une bibliothèque .NET qui permet de mapper automatiquement des objets entre eux, généralement entre des objets de domaine (modèles d'entités) et des objets de transfert de données (DTO).
Elle est particulièrement utile pour éviter d'écrire du code répétitif lors de la conversion entre différents objets.


🔧 Installation

Pour installer AutoMapper via NuGet, utilisez cette commande :

dotnet add package AutoMapper
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection

💡 Comment ça marche ?

AutoMapper utilise des profils pour configurer les mappages entre différents objets.


🔥 Exemple simple

1. Modèles (Domain Model et DTO)

public class User
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public string Email { get; set; }
}

public class UserDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

2. Configuration de AutoMapper (Profile)

AutoMapper utilise les Profiles pour définir les mappages.

using AutoMapper;

public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<User, UserDto>()
            .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FullName));
    }
}

3. Configuration dans un projet .NET (Program.cs ou Startup.cs)

using AutoMapper;

var builder = WebApplication.CreateBuilder(args);

// Ajouter AutoMapper au conteneur de services
builder.Services.AddAutoMapper(typeof(UserProfile));

var app = builder.Build();
app.Run();

4. Utilisation dans un service ou un contrôleur

using AutoMapper;

public class UserService
{
    private readonly IMapper _mapper;

    public UserService(IMapper mapper)
    {
        _mapper = mapper;
    }

    public UserDto GetUserDto(User user)
    {
        return _mapper.Map<UserDto>(user);
    }
}

5. Appel du service

var user = new User { Id = 1, FullName = "John Doe", Email = "Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser." };
var service = new UserService(new MapperConfiguration(cfg => cfg.AddProfile<UserProfile>()).CreateMapper());

UserDto dto = service.GetUserDto(user);
Console.WriteLine($"DTO: {dto.Id}, {dto.Name}, {dto.Email}");

🌟 Avantages d'AutoMapper

  1. Productivité : Réduit la quantité de code boilerplate.
  2. Maintenance : Les mappings sont centralisés, ce qui simplifie la maintenance.
  3. Performances : Les mappages sont précompilés, ce qui améliore les performances.
  4. Flexibilité : Permet de configurer des règles de mapping complexes.

🔄 Mapping bidirectionnel

AutoMapper permet aussi de faire des mappings dans les deux sens :

CreateMap<User, UserDto>().ReverseMap();

🪄 Mapping avec transformation

Vous pouvez également transformer les données pendant le mapping :

CreateMap<User, UserDto>()
    .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FullName.ToUpper()));

AutoMapper est donc un excellent outil pour simplifier la conversion entre objets, notamment dans les applications qui utilisent beaucoup de DTOs. 

 


🌟Mapping avec transformation

 

Détaillons  la syntaxe d'AutoMapper étape par étape, en simplifiant les concepts !


💡 Les bases d'AutoMapper

AutoMapper utilise un concept de profil pour définir les règles de transformation entre deux objets.
Un profil est comme un plan qui dit à AutoMapper comment copier les données d'un objet source vers un objet de destination.


📌 Étape 1 : Créer les classes (Model et DTO)

Imaginons que nous ayons un modèle d'utilisateur et un DTO (Data Transfer Object).

Classe User (Modèle de domaine)

public class User
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public string Email { get; set; }
}

Classe UserDto (DTO)

public class UserDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

📌 Étape 2 : Créer un profil de mappage

On utilise une classe qui hérite de Profile pour configurer les mappages.

Classe UserProfile

using AutoMapper;

public class UserProfile : Profile
{
    public UserProfile()
    {
        // Créer un mappage de User vers UserDto
        CreateMap<User, UserDto>()
            .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FullName));
    }
}

🔍 Explications sur la syntaxe

  • CreateMap<User, UserDto>()
    → Crée un lien de mappage entre la classe User et la classe UserDto.

  • .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FullName))
    → Indique que la propriété Name de l'objet UserDto doit être remplie avec la valeur de la propriété FullName de l'objet User.


📌 Étape 3 : Configurer AutoMapper (dans Program.cs ou Startup.cs)

Pour que le mappage fonctionne dans toute l'application, il faut enregistrer AutoMapper dans le conteneur de services.

Program.cs

using AutoMapper;

var builder = WebApplication.CreateBuilder(args);

// Ajouter AutoMapper au conteneur de services
builder.Services.AddAutoMapper(typeof(UserProfile));

var app = builder.Build();
app.Run();

📌 Étape 4 : Utiliser AutoMapper

Dans un service ou un contrôleur, vous allez injecter IMapper pour effectuer la conversion.

UserService.cs

using AutoMapper;

public class UserService
{
    private readonly IMapper _mapper;

    public UserService(IMapper mapper)
    {
        _mapper = mapper;
    }

    public UserDto GetUserDto(User user)
    {
        // Utiliser AutoMapper pour convertir un User en UserDto
        return _mapper.Map<UserDto>(user);
    }
}

📌 Étape 5 : Appeler le service

var user = new User { Id = 1, FullName = "John Doe", Email = "Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser." };
var mapper = new MapperConfiguration(cfg => cfg.AddProfile<UserProfile>()).CreateMapper();
var service = new UserService(mapper);

UserDto dto = service.GetUserDto(user);
Console.WriteLine($"DTO: {dto.Id}, {dto.Name}, {dto.Email}");

📝 Résultat

Le programme va afficher :

DTO: 1, John Doe, Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser.

🚀 Pourquoi utiliser AutoMapper ?

  1. Simplicité : Moins de code répétitif.
  2. Performance : Le mappage est précompilé, donc plus rapide que les mappings manuels.
  3. Centralisation : Tous les mappages sont définis en un seul endroit.

 

Mutualiser des fonctions entre plusieurs écrans LGVL

Pour éviter les doublons de code et mutualiser les fonctions textarea et keyboard, il est préférable d'adopter une approche générique avec des paramètres dynamiques pour les événements. Voici les étapes pour mutualiser ou différencier proprement les fonctions entre connexionScreen et optionsScreen.


1. Mutualiser les événements communs

Les fonctions textarea_event_cb et keyboard_event_cb peuvent être généralisées pour accepter un contexte spécifique (par exemple, les boutons associés ou des actions spécifiques).

Code générique pour les événements

Ajoutons une structure pour stocker les objets spécifiques à chaque écran :

typedef struct {
    lv_obj_t* keyboard;       // Clavier virtuel
    lv_obj_t* btnValidate;    // Bouton "Valider"
} ui_screen_context_t;

2. Mutualiser textarea_event_cb

Voici une version générique de textarea_event_cb :

void generic_textarea_event_cb(lv_event_t* e) {
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t* textarea = lv_event_get_target(e);

    // Récupérer le contexte passé comme user_data
    ui_screen_context_t* context = (ui_screen_context_t*)lv_event_get_user_data(e);

3. Mutualiser keyboard_event_cb

Voici une version générique de keyboard_event_cb :

void generic_keyboard_event_cb(lv_event_t* e) {
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t* keyboard = lv_event_get_target(e);

    // Récupérer le contexte passé comme user_data
    ui_screen_context_t* context = (ui_screen_context_t*)lv_event_get_user_data(e);

 

4. Exemple d'intégration dans connexionScreen

Dans ui_connexionScreen_screen_init, on utilise ces fonctions génériques avec un contexte propre à l'écran.

 

ESP32 : Light Sleep et Deep Sleep

Oui, il est possible de mettre l’ESP32 en mode sleep tout en utilisant SPIFFS et un serveur web, mais il y a certaines limitations et des considérations importantes à garder à l’esprit.

Explication et Considérations pour le Mode Sleep avec un Serveur Web et SPIFFS

L'ESP32 dispose de plusieurs modes de veille (sleep modes), mais dès qu'il est en veille, la plupart des fonctionnalités (y compris le Wi-Fi) sont désactivées. Cela signifie que si l'ESP32 est mis en veille, il ne pourra pas servir de page web tant qu’il ne se réveillera pas.

Voici les modes de veille principaux et comment ils affectent le fonctionnement de votre serveur web et SPIFFS :

  1. Mode Light Sleep :

    • Dans ce mode, le processeur peut être mis en veille pour économiser de l’énergie, mais le Wi-Fi peut rester connecté (bien que sa consommation soit réduite).
    • Avantages : Le périphérique reste connecté au réseau, et le réveil est rapide.
    • Inconvénients : Ce mode économise moins d’énergie comparé aux autres modes de veille.
    • Utilisation : Vous pourriez utiliser le mode Light Sleep entre les requêtes, en sortant de la veille uniquement quand il y a une nouvelle requête.
  2. Mode Deep Sleep :

    • Dans ce mode, l’ESP32 désactive pratiquement tout, y compris le Wi-Fi et le processeur, ce qui réduit énormément la consommation d’énergie.
    • Avantages : Consommation minimale, idéal pour les projets à très faible énergie.
    • Inconvénients : Le Wi-Fi doit se reconnecter chaque fois que le périphérique sort de la veille, ce qui prend du temps et consomme un peu plus d’énergie pour chaque réveil.
    • Utilisation : Si vous voulez utiliser le Deep Sleep tout en ayant un serveur web, l’ESP32 doit se réveiller périodiquement ou en réponse à un événement (comme un timer ou un capteur) pour se reconnecter au réseau et répondre aux requêtes. Cela convient mieux aux systèmes où les requêtes sont peu fréquentes.

Exemple de Configuration avec Light Sleep

Pour mettre l’ESP32 en mode Light Sleep entre les requêtes, vous pouvez configurer un intervalle d’inactivité où, si aucune requête n’est reçue, l’ESP32 passe en Light Sleep jusqu'à ce qu’une interruption (comme une nouvelle requête ou un timer) le réveille.

Voici un exemple simple de Light Sleep avec un délai d’inactivité :

#include <SPIFFS.h>
#include <ESPAsyncWebServer.h>
#include <WiFi.h>

AsyncWebServer server(80);
unsigned long lastRequestTime = 0;
const unsigned long sleepDelay = 10000; // Temps avant Light Sleep (10 secondes d'inactivité)

void setup() {
    Serial.begin(115200);

    // Initialiser SPIFFS
    if (!SPIFFS.begin(true)) {
        Serial.println("Erreur lors du montage SPIFFS");
        return;
    }

    // Connecter au Wi-Fi
    WiFi.begin("SSID", "password");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("Connecté au Wi-Fi");

    // Configurer le serveur pour servir un fichier depuis SPIFFS
    server.on("/alarme.log", HTTP_GET, [](AsyncWebServerRequest *request) {
        lastRequestTime = millis();  // Réinitialiser le délai d'inactivité
        request->send(SPIFFS, "/alarme.log", "text/plain");
    });

    server.begin();
}

void loop() {
    // Si le délai d'inactivité est dépassé, entrer en Light Sleep
    if (millis() - lastRequestTime > sleepDelay) {
        Serial.println("Inactivité détectée, passage en Light Sleep.");
        esp_light_sleep_start();  // Mettre l'ESP32 en Light Sleep
        Serial.println("Réveil du Light Sleep.");  // Message imprimé après réveil
        lastRequestTime = millis();  // Réinitialiser le compteur
    }
}

Utilisation du Deep Sleep

Si vous souhaitez utiliser le Deep Sleep, l'ESP32 doit être réveillé périodiquement ou par un événement externe. Par exemple, vous pouvez configurer un timer pour réveiller l’ESP32 toutes les minutes pour vérifier les requêtes, ou un capteur pour déclencher le réveil.

Voici un exemple d'utilisation du Deep Sleep avec un réveil par timer :

#include <SPIFFS.h>
#include <ESPAsyncWebServer.h>
#include <WiFi.h>

AsyncWebServer server(80);

void setup() {
    Serial.begin(115200);

    // Initialiser SPIFFS
    if (!SPIFFS.begin(true)) {
        Serial.println("Erreur lors du montage SPIFFS");
        return;
    }

    // Connecter au Wi-Fi
    WiFi.begin("SSID", "password");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("Connecté au Wi-Fi");

    // Configurer le serveur pour servir un fichier depuis SPIFFS
    server.on("/alarme.log", HTTP_GET, [](AsyncWebServerRequest *request) {
        request->send(SPIFFS, "/alarme.log", "text/plain");
    });

    server.begin();

    // Configurer le timer pour le Deep Sleep (réveil toutes les 60 secondes)
    esp_sleep_enable_timer_wakeup(60 * 1000000);  // 60 secondes en microsecondes
    Serial.println("Passage en Deep Sleep pour 60 secondes");
    esp_deep_sleep_start();  // Mettre l'ESP32 en Deep Sleep
}

void loop() {
    // Le code de loop() ne sera pas exécuté car l'ESP32 redémarre après Deep Sleep
}

Points Importants à Prendre en Compte

  • Wi-Fi Reconnexion : Dans le mode Deep Sleep, chaque réveil nécessite une reconnexion au Wi-Fi, ce qui peut entraîner un délai de réponse plus long pour la première requête après chaque réveil.
  • Économie d’Énergie : Le Deep Sleep est plus économe en énergie, mais nécessite une logique de réveil par timer ou événement pour répondre aux requêtes. Le Light Sleep permet de conserver la connexion Wi-Fi mais avec une économie d’énergie moindre.
  • Gestion des Requêtes : Si les requêtes sont fréquentes, le Light Sleep peut être plus approprié, mais si elles sont rares, le Deep Sleep avec un réveil périodique pourrait être préférable.

Le choix entre ces options dépend de votre application et de vos besoins en énergie.

En mode Light Sleep, l'ESP32 peut en effet perdre temporairement la connexion MQTT. Bien que ce mode soit conçu pour réduire la consommation d’énergie tout en permettant de conserver une connexion Wi-Fi active, il n’est pas toujours fiable pour maintenir les connexions réseau stables, notamment celles basées sur des protocoles nécessitant une connexion continue, comme MQTT.

Explication

Le mode Light Sleep met le processeur principal en veille, mais il garde le module Wi-Fi actif. Cependant :

  • Pauses de communication : Le Light Sleep entraîne de petites pauses dans les communications réseau lorsque le processeur est en veille. Cela peut poser des problèmes avec les protocoles qui nécessitent une communication continue (comme MQTT), surtout si le serveur MQTT s'attend à des signaux réguliers de la part de l’ESP32 pour maintenir la connexion active.
  • Timeout et déconnexions : Certaines configurations de serveurs MQTT déconnectent les clients après un certain temps d'inactivité. Avec le Light Sleep, il est probable que l’ESP32 ne réponde pas assez fréquemment, entraînant une déconnexion de la part du serveur.

Solution pour le Light Sleep avec MQTT

  1. Maintien de la Connexion MQTT : Vous pouvez tenter de configurer des keep-alive (paquets envoyés périodiquement pour maintenir la connexion) très courts (par exemple, 5 à 10 secondes) pour essayer de conserver la connexion. Cela pourrait aider dans certains cas, mais ce n’est pas garanti, car le Light Sleep peut tout de même introduire des interruptions.
  2. Reconnecter après le réveil : Une autre solution consiste à détecter la perte de connexion MQTT dans le code, puis à essayer de reconnecter le client MQTT dès que l’ESP32 sort de Light Sleep.

Alternatives

Pour des connexions stables avec MQTT, il est souvent recommandé :

  • Soit de désactiver le Light Sleep pendant que la connexion MQTT est nécessaire, puis de le réactiver après la déconnexion.
  • Soit d'utiliser Deep Sleep avec des réveils périodiques. Dans ce cas, l’ESP32 se réveille, se reconnecte au serveur MQTT, envoie les données nécessaires (ou écoute les messages), puis retourne en Deep Sleep.

Exemple de Code : Reconnexion MQTT après le Light Sleep

Un exemple simplifié pour reconnecter MQTT après un Light Sleep, en détectant les déconnexions et en essayant de rétablir la connexion :

#include <WiFi.h>
#include <PubSubClient.h>

WiFiClient espClient;
PubSubClient client(espClient);

const char* ssid = "SSID";
const char* password = "PASSWORD";
const char* mqtt_server = "broker.hivemq.com";

void setup() {
    Serial.begin(115200);
    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    client.setServer(mqtt_server, 1883);

    // Connecter au serveur MQTT
    reconnectMQTT();
}

void loop() {
    // Si déconnecté, essayer de se reconnecter
    if (!client.connected()) {
        reconnectMQTT();
    }

    // Garder la connexion MQTT active
    client.loop();

    // Exemple de passage en Light Sleep si inactif pendant 10 secondes
    delay(10000);
    esp_light_sleep_start();  // L'ESP32 sort de Light Sleep sur une interruption
}

void reconnectMQTT() {
    while (!client.connected()) {
        Serial.print("Tentative de connexion MQTT...");
        if (client.connect("ESP32Client")) {
            Serial.println("connecté");
            client.subscribe("topic/test");
        } else {
            Serial.print("échec, code erreur= ");
            Serial.print(client.state());
            delay(5000);
        }
    }
}

Dans cet exemple, le programme essaie de reconnecter le client MQTT chaque fois qu'il détecte une déconnexion. Cela permet de restaurer la connexion après chaque Light Sleep.

Vidage alarme.log avec alerte mqtt

Je voudrais publier sur mqtt via le topic alarme/memory, quand il reste moins de 20% de mémoire disponible pour le fichier alarme.log, 2 informations : la mémoire restante et un flag d'alerte. Ce pourait être : freeMemory = xxxxx et flagMemory = 1

Pour publier une alerte sur MQTT lorsque l’espace restant pour le fichier alarme.log est inférieur à 20 %, tu peux mettre en place un code pour surveiller régulièrement l’espace disponible sur le système de fichiers SPIFFS et envoyer un message MQTT avec la mémoire restante et un flag d'alerte si la condition est remplie.

Voici un exemple de code pour accomplir cela :

Code Arduino pour ESP32 avec MQTT

Assurez-vous que les bibliothèques PubSubClient (pour MQTT) et SPIFFS sont installées et configurées.

#include <WiFi.h>
#include <PubSubClient.h>
#include <SPIFFS.h>

const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const char* mqttServer = "YOUR_MQTT_SERVER";
const int mqttPort = 1883;

WiFiClient espClient;
PubSubClient client(espClient);

const char* topic = "alarme/memory";  // Topic MQTT pour les alertes mémoire
const int memoryThreshold = 20;       // Seuil de 20% pour déclencher l'alerte

void setup() {
    Serial.begin(115200);

    // Connexion Wi-Fi
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("\nWiFi connecté");

    // Configuration MQTT
    client.setServer(mqttServer, mqttPort);

    // Initialisation de SPIFFS
    if (!SPIFFS.begin(true)) {
        Serial.println("Erreur de montage de SPIFFS");
        return;
    }
}

void loop() {
    if (!client.connected()) {
        reconnectMQTT();
    }
    client.loop();

    // Vérification de la mémoire disponible pour alarme.log
    checkMemoryAndPublish();

    delay(60000);  // Vérifie la mémoire toutes les 60 secondes
}

void checkMemoryAndPublish() {
    // Taille totale et espace disponible
    size_t totalBytes = SPIFFS.totalBytes();
    size_t usedBytes = SPIFFS.usedBytes();
    size_t freeBytes = totalBytes - usedBytes;

    // Calcul du pourcentage de mémoire libre
    int freeMemoryPercentage = (freeBytes * 100) / totalBytes;

    // Vérifie si le seuil de 20% est atteint
    if (freeMemoryPercentage < memoryThreshold) {
        String payload = "freeMemory=" + String(freeBytes) + " ; flagMemory=1";
        client.publish(topic, payload.c_str());
        Serial.println("Alerte mémoire publiée sur MQTT: " + payload);
    } else {
        Serial.println("Mémoire suffisante: " + String(freeMemoryPercentage) + "% restant.");
    }
}

void reconnectMQTT() {
    // Connexion au serveur MQTT
    while (!client.connected()) {
        Serial.print("Connexion à MQTT...");
        if (client.connect("ESP32Client")) {
            Serial.println("connecté");
        } else {
            Serial.print("Erreur de connexion, rc=");
            Serial.print(client.state());
            Serial.println(" nouvelle tentative dans 5 secondes");
            delay(5000);
        }
    }
}

Explication du Code

  1. Connexion Wi-Fi et MQTT : Configure le Wi-Fi et se connecte au serveur MQTT.
  2. Contrôle de la Mémoire : La fonction checkMemoryAndPublish() utilise SPIFFS.totalBytes() et SPIFFS.usedBytes() pour calculer l’espace libre en pourcentage. Si le pourcentage est inférieur au seuil (20 %), elle publie un message MQTT sur le topic alarme/memory avec les informations de mémoire libre et un flag d'alerte.
  3. Reconnexion MQTT : La fonction reconnectMQTT() s’assure que la connexion MQTT reste active.

Points à Vérifier

  • Configuration du Serveur MQTT : Assurez-vous que les informations (mqttServer, mqttPort, etc.) correspondent à celles de votre serveur MQTT.
  • Fréquence de Vérification : La vérification est configurée toutes les 60 secondes (60000 ms). Ajustez selon vos besoins.

Ce code enverra donc un message MQTT au format suivant lorsque la mémoire est critique :

freeMemory=XXXXX ; flagMemory=1

Cela devrait te permettre de suivre l'état de la mémoire et de recevoir une alerte lorsque l'espace disponible tombe en dessous de 20 %.

Est-ce que l'on peut transformer checkMemoryAndPublish() en tache rtos pour qu'elle ne s'éxécute qu'à une fréquence définie dans le config.json (par défaut, 1 fois par joir à minuit)

Oui, il est possible de transformer la fonction checkMemoryAndPublish() en une tâche RTOS pour qu’elle s'exécute à une fréquence définie, comme une fois par jour à minuit. L'ESP32 utilise FreeRTOS, ce qui permet de créer des tâches périodiques, et le timing peut être configuré pour exécuter la tâche à une fréquence personnalisée en utilisant un paramètre dans le fichier config.json.

Voici les étapes pour transformer checkMemoryAndPublish() en tâche RTOS :

1. Lire la fréquence depuis config.json

Commence par récupérer la fréquence souhaitée depuis le fichier config.json. Par exemple, si la fréquence est définie en secondes ou en heures, elle peut être convertie en millisecondes pour l'utiliser avec FreeRTOS. Une fois par jour peut être configurée comme 86 400 000 millisecondes.

2. Créer une tâche RTOS pour checkMemoryAndPublish()

Tu peux créer une tâche RTOS qui s'exécute selon l'intervalle défini, comme suit :

#include <ArduinoJson.h>
#include "SPIFFS.h"

// Variables globales
unsigned long intervalMillis = 86400000;  // Par défaut, 1 jour en millisecondes

void checkMemoryAndPublish() {
    // Implémentation de ta fonction existante
}

void checkMemoryTask(void *parameter) {
    for (;;) {
        // Exécute la fonction principale
        checkMemoryAndPublish();
        
        // Met en pause la tâche pour l'intervalle configuré
        vTaskDelay(intervalMillis / portTICK_PERIOD_MS);
    }
}

void setup() {
    // Initialisation de SPIFFS pour lire config.json
    if (!SPIFFS.begin(true)) {
        Serial.println("Erreur de montage SPIFFS");
        return;
    }

    // Lire la configuration
    File configFile = SPIFFS.open("/config.json", "r");
    if (configFile) {
        StaticJsonDocument<512> doc;
        DeserializationError error = deserializeJson(doc, configFile);
        if (!error) {
            // Lire l'intervalle depuis config.json (en millisecondes, par exemple)
            intervalMillis = doc["checkMemoryInterval"].as<unsigned long>();
        }
        configFile.close();
    }

    // Créer la tâche RTOS
    xTaskCreate(
        checkMemoryTask,         // Fonction de tâche
        "CheckMemoryTask",       // Nom de la tâche
        4096,                    // Taille de la pile
        NULL,                    // Paramètres de tâche
        1,                       // Priorité
        NULL                     // Gestion de la tâche
    );
}

void loop() {
    // Vide, car la tâche RTOS s'exécute indépendamment
}

Explication du code

  1. Initialisation de SPIFFS : On monte SPIFFS pour accéder au fichier config.json, puis on récupère l'intervalle depuis la clé checkMemoryInterval.

  2. Fonction checkMemoryTask : Cette tâche RTOS appelle checkMemoryAndPublish() pour vérifier la mémoire et publier sur MQTT, puis elle est mise en pause pour intervalMillis avant de se relancer.

  3. Définir un intervalle en millisecondes : intervalMillis est initialisé à une valeur par défaut de 86 400 000 ms (24 heures). Ce délai peut être configuré dans config.json et mis à jour au démarrage.

Exemple de config.json

{
    "checkMemoryInterval": 86400000  // Intervalle en millisecondes (1 jour)
}

Ajustements pour exécution quotidienne à minuit

Si tu veux que l'exécution se fasse précisément à minuit, tu peux :

  • Utiliser un module RTC pour vérifier l'heure actuelle.
  • Calculer le délai jusqu'à minuit et le mettre en pause pour cette durée initialement, puis utiliser vTaskDelay() pour 24 heures ensuite.

Ce processus assure que checkMemoryAndPublish() s'exécute de façon optimisée sans bloquer les autres processus de l’ESP32, tout en respectant l’intervalle configuré.

 

Étape 2 : Supprimer les anciens logs si l’espace SPIFFS est saturé

  1. Fonction pour vérifier l’espace disponible et nettoyer :

    void cleanLogIfLowSpace() {
        // Taille et espace disponibles sur SPIFFS
        size_t totalBytes = SPIFFS.totalBytes();
        size_t usedBytes = SPIFFS.usedBytes();
    
        // Si l’espace utilisé est supérieur à 80 % de l’espace total
        if (usedBytes > (totalBytes * 0.8)) {
            SPIFFS.remove("/alarme.log"); // Supprimer l'ancien log
            File logFile = SPIFFS.open("/alarme.log", FILE_WRITE); // Recréer un fichier vide
            if (logFile) {
                logFile.println("Nouveau journal démarré après nettoyage");
                logFile.close();
            }
        }
    }
  2.  Créer une tâche journalière dans loop pour exécuter cleanLogIfLowSpace() :
unsigned long lastCleanTime = 0;
const unsigned long cleanInterval = 24 * 60 * 60 * 1000; // Intervalle de 24 heures

void loop() {
    handleMqtt(config);

    unsigned long currentMillis = millis();
    if (currentMillis - lastCleanTime >= cleanInterval) {
        cleanLogIfLowSpace();
        lastCleanTime = currentMillis;
    }
}

 

Information

Ce site est construit autour de Joomla 5, en utilisant le template Helix Ultimate et les composants : SP Builder Pro pour la construction de certaines pages, Smart Slider 3 Free pour les slideshows, Komento Free pour les commentaires sur les articles et Ignite Gallery pour les galeries de photos.

Mentions légales

Le site est édité par Christian Sammut
130, Avenue du Général Leclerc
45800 Saint Jean de Braye
Tél.: 06.01.74.90.33
E-mail: contact@sammut.fr

Ce site est hébergé par AMEN SASU
12-14, Rond Point des Champs Elysées
75008 Paris