Solution logicielle C# pour acquisition de données d'humidité

Architecture du système

Module de communication série

using System.IO.Ports;

public class GestionnairePortSerie : IDisposable
{
    private SerialPort _portConnexion;
    private const int VitesseTransmission = 115200;
    private const Parity ModeParite = Parity.None;
    private const StopBits BitsArret = StopBits.One;
    private const int BitsDonnees = 8;

    public event Action<string> DonnéesReçues;

    public void EtablirConnexion(string nomPort)
    {
        _portConnexion = new SerialPort(nomPort, VitesseTransmission, ModeParite, BitsDonnees, BitsArret);
        _portConnexion.DataReceived += (sender, args) =>
        {
            string contenu = _portConnexion.ReadExisting();
            DonnéesReçues?.Invoke(contenu);
        };
        _portConnexion.Open();
    }

    public void FermerConnexion() => _portConnexion?.Close();

    public void Dispose() => _portConnexion?.Dispose();
}

Traiteur de données

public class TraiteurDonnées
{
    public (double Temperature, double Humidite)? AnalyserMessage(string trame)
    {
        if (string.IsNullOrEmpty(trame) || trame.Length < 8)
            return null;

        try
        {
            var parties = trame.Split(':');
            double temperature = double.Parse(parties[0]);
            double humidite = double.Parse(parties[1]);
            
            return (temperature, humidite);
        }
        catch
        {
            return null;
        }
    }
}

Graphiqeu dynamique

using OxyPlot;
using OxyPlot.Series;

public class GraphiqueTempsReel
{
    private PlotModel _modeleGraphique;
    private LineSeries _serieTemperature;
    private LineSeries _serieHumidite;
    private int _compteurPoints = 0;
    private const int CapaciteTampon = 200;

    public GraphiqueTempsReel()
    {
        _modeleGraphique = new PlotModel { Title = "Données environnementales" };
        
        _serieTemperature = new LineSeries
        {
            Title = "Température",
            Color = OxyColors.Crimson,
            StrokeThickness = 2
        };
        
        _serieHumidite = new LineSeries
        {
            Title = "Humidité",
            Color = OxyColors.SteelBlue,
            StrokeThickness = 2
        };

        _modeleGraphique.Series.Add(_serieTemperature);
        _modeleGraphique.Series.Add(_serieHumidite);
    }

    public void AjouterMesure(double temp, double humi)
    {
        _serieTemperature.Points.Add(new DataPoint(_compteurPoints, temp));
        _serieHumidite.Points.Add(new DataPoint(_compteurPoints, humi));
        
        if (_serieTemperature.Points.Count > CapaciteTampon)
        {
            _serieTemperature.Points.RemoveAt(0);
            _serieHumidite.Points.RemoveAt(0);
        }
        
        _compteurPoints++;
        _modeleGraphique.InvalidatePlot(true);
    }

    public PlotModel ObtenirModele() => _modeleGraphique;
}

Fonctionnalités avancées

Système d'alertes

public class GestionnaireAlarmes
{
    private readonly double[] _seuilsTemp = { 5.0, 35.0 };
    private readonly double[] _seuilsHumi = { 20.0, 80.0 };
    
    public EvenementAlarme VerifierSeuils(double temp, double humi)
    {
        if (temp > _seuilsTemp[1] || temp < _seuilsTemp[0])
            return new EvenementAlarme("Temperature", DateTime.Now);
            
        if (humi > _seuilsHumi[1] || humi < _seuilsHumi[0])
            return new EvenementAlarme("Humidite", DateTime.Now);
            
        return null;
    }
    
    public void DeclencherAlerte(EvenementAlarme evenement)
    {
        // Logique d'alerte sonore et visuelle
    }
}

Persistance des données

using LiteDB;

public class DepotDonnées
{
    private readonly LiteDatabase _baseDonnées;
    private const string CheminFichier = "mesures.db";

    public DepotDonnées()
    {
        _baseDonnées = new LiteDatabase(CheminFichier);
        InitialiserCollection();
    }

    private void InitialiserCollection()
    {
        var collection = _baseDonnées.GetCollection<Mesure>("historique");
        collection.EnsureIndex(x => x.Horodatage);
    }

    public void EnregistrerMesure(double temperature, double humidite)
    {
        var nouvelleMesure = new Mesure
        {
            Horodatage = DateTime.UtcNow,
            ValeurTemperature = temperature,
            ValeurHumidite = humidite
        };
        
        _baseDonnées.GetCollection<Mesure>("historique").Insert(nouvelleMesure);
    }
    
    public List<Mesure> ConsulterHistorique(DateTime debut, DateTime fin)
    {
        return _baseDonnées.GetCollection<Mesure>("historique")
            .Find(x => x.Horodatage >= debut && x.Horodatage <= fin)
            .ToList();
    }
}

Optimisations techniques

Traitement asynchrone

public class CollecteurAsynchrone
{
    private CancellationTokenSource _annulationSource;
    private Task _tachePrincipale;
    
    public async Task DemarrerAcquisitionAsync(Func<Task> traitementDonnées)
    {
        _annulationSource = new CancellationTokenSource();
        _tachePrincipale = Task.Run(async () =>
        {
            while (!_annulationSource.Token.IsCancellationRequested)
            {
                await traitementDonnées();
                await Task.Delay(500, _annulationSource.Token);
            }
        }, _annulationSource.Token);
    }
    
    public void ArreterAcquisition()
    {
        _annulationSource?.Cancel();
        _tachePrincipale?.Wait();
    }
}

Gestion de la mémoire

public class TamponCirculaire<T>
{
    private readonly T[] _elements;
    private int _positionEcriture;
    private int _compteur;

    public TamponCirculaire(int capacite)
    {
        _elements = new T[capacite];
    }

    public void Ajouter(T element)
    {
        _elements[_positionEcriture] = element;
        _positionEcriture = (_positionEcriture + 1) % _elements.Length;
        
        if (_compteur < _elements.Length)
            _compteur++;
    }

    public T[] ExtraireDonnées()
    {
        var resultat = new T[_compteur];
        int indexDepart = (_positionEcriture - _compteur + _elements.Length) % _elements.Length;
        
        for (int i = 0; i < _compteur; i++)
        {
            resultat[i] = _elements[(indexDepart + i) % _elements.Length];
        }
        
        return resultat;
    }
}

Procédures de test

Simulation de capteur

public class SimulateurCapteur
{
    private Random _generateur = new Random();
    
    public (double temp, double humi) GenererMesure()
    {
        double baseTemp = 22.0 + _generateur.NextDouble() * 6;
        double baseHumi = 45.0 + _generateur.NextDouble() * 15;
        
        // Ajout de variations temporelles
        double variation = Math.Sin(DateTime.Now.Second * 0.1) * 2;
        
        return (baseTemp + variation, baseHumi - variation);
    }
    
    public string CreerTrame(double temp, double humi)
    {
        return $"{temp:F1}:{humi:F1}";
    }
}

Déploiement

Configuration système

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
    <ParametresSerie>
        <Port>COM3</Port>
        <Vitesse>115200</Vitesse>
    </ParametresSerie>
    
    <ParametresAlarmes>
        <SeuilTempHaute>35.0</SeuilTempHaute>
        <SeuilTempBasse>5.0</SeuilTempBasse>
        <SeuilHumiHaute>80.0</SeuilHumiHaute>
        <SeuilHumiBasse>20.0</SeuilHumiBasse>
    </ParametresAlarmes>
    
    <Stockage>
        <CheminBase>C:\\DonneesCapteurs\\mesures.db</CheminBase>
        <IntervalleSauvegarde>60</IntervalleSauvegarde>
    </Stockage>
</Configuration>

Étiquettes: C# acquisition-données Communication-Serie visualisation-données gestion-alarmes

Publié le 2 août à 06h22