Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    novembre 2010
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : novembre 2010
    Messages : 68
    Points : 51
    Points
    51

    Par défaut wrapper C++ pour library c

    Ca permet d'avoir l'implémentation que l'on souhaite donc c'est plutôt bien .
    Le problème c'est que j'ai déjà une classe que gère l'i2c pour les autres capteurs et j'aimerai bien faire appelle a elle dans cette Library
    via un wrapper. Comment puis je m'en sortir?

    voila ce que j'ai

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    namespace comm
    {
    	class I2c
    	{
    		I2c()
    		~I2C();
    		write (int x, int y, int z);
                    read(int x, int y, int z);
    	};
    }

    Les fonctions utilisée par la Libraire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    writeI2c(int x, int y, int z);
    readI2c(int x, int y, int z);
    je voulais créer un wrapper qui prennent en pointer ma class de façon a pourvoir faire ca:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    comm::I2c* ptrI2C
     
    writeI2c(int x, int y, int z)
    {
    ptrI2C->write(int x, int y, int z)
    }
     
    readI2c(int x, int y, int z)
    {
    ptrI2C-read(int x, int y, int z)
    }
    Mais je ne m'en sors pas.
    Merci par avance

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    Consultant informatique
    Inscrit en
    octobre 2004
    Messages
    11 176
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 176
    Points : 27 582
    Points
    27 582

    Par défaut

    Salut,
    Ben, tu n'as qu'à faire ce que tu indiques : appeler writeI2C à partir de la fonction membre write et appeler la fonction readI2C depuis la fonction read, à chaque fois en leur transmettant les paramère fournis à la fonction

    Au fait : une fonction a toujours un type de retour!!! Où ils sont les types de retour de tes fonctions write et read???

    Après, il faudra peut-être prendre en compte les politiques d'initialisation et de "nettoyage" de ta bibliothèque (car, library, ca se traduit par "bibliothèque" en francais ) I2C : la première (l'initialisation) devra sans doute être prise en charge par le constructeur de ta classe, et la deuxième (le "nettoyage") devra sans doute être pris en charge par ... le destructeur de ta classe
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    novembre 2010
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : novembre 2010
    Messages : 68
    Points : 51
    Points
    51

    Par défaut

    merci pour ta réponse,
    Effectivement ca marche, j'avais un problème de header, qui fait que la Library tirée la class d'où mon problème.

    Mais je vais quand même avoir un problème quand je voudrais inclure la Library dans mon exécutable C++. Je m'explique:

    Dans la Library j'ai donc ajouté l'include sur le header de mon wrapper cela permet de définir les fonctions read/writei2c. Pour l'instant mon pointer n'est présent que dans le .cpp. Or si je veux le setter dans mon exécutable qui a la classe I2C il faut qu'elle connaisse sa définition (il faut donc que je l'ajoute dans le header du wrapper et donc paf je me retrouve avec du code c++ tiré par la Library qui ne compile alors plus

    oui désolé j'ai écris un peu vite bien sur qu'elles ont des types de retour .
    Jai effectivement une méthode initialize() appelée dans le ctor et close membre de ma classe appelée dans le dtor mais je ne les ai pas fait apparaitre, je n'ai pas trouvé utile a mon problème .

    Je voulais en plus créer une méthode set du pointer, mais cela ne semble pas possible vu que la que la signature aura un paramètre de type class I2C.
    Qu'est ce qui est le mieux

    créer une méthode avec un paramètre void* et caster en I2C ou initialisé directement le pointer sans passer par une méthode?
    Je préfère la première car cela permet de vérifier l'état du pointer

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    void setI2cPtr(void* ptr)
    {
      ptrI2C = static_cast<comm::I2c*>(ptr);
    }
     
    ou directement 
     
    ptrI2C = this; // (dans la classe)

    Un avis?

    Cordialement,
    merci!!

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    Consultant informatique
    Inscrit en
    octobre 2004
    Messages
    11 176
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 176
    Points : 27 582
    Points
    27 582

    Par défaut

    Il serait utile que tu nous montre ton code complet, que l'on puisse travailler sur quelque chose de complet, car, tes explications ne sont pas parfaitement claires
    Jai effectivement une méthode initialize() appelée dans le ctor
    Et, dis moi: quelle est la différence entre une fonction "initialize()" et le constructeur Pourquoi ne mettrais tu pas carrément tout le contenu de ta fonction initialize() directement dans le constructeur

    Ceci dit, pourquoi crois tu que l'on travaille avec deux types de fichiers en C++ La réponse simple est que le fichier d'en-tête (*.hpp) permet simplement au compilateur de savoir que "quelque chose" existe, alors que le fichier d'implémentation (*.cpp) permet au compilateur de savoir "ce qu'il doit faire"

    Si j'ai bien compris, il faut initialiser "une bonne fois pour toutes" un (unique) pointeur vers une structure issue de la bibliothèque C Hé bien, quand on va compiler le wrapper, il n'y a que les fonctions membre de notre classe qui devront y accéder. Et, pour ce faire, rien de plus facile : nous déclarons ce pointeur uniquement dans le fichier d'implémentation . Un petit exemple pour me faire comprendre:
    Wrapper.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    #ifndef WRAPPER_HPP
    #define WRAPPER_HPP
    namespace I2C{
    class Wrapper{
    public:
        Wrapper();
        ~Wrapper();
        /* il faut nettoyer le tout lorsqu'on arrête d'utiliser la bibliothèque C */
        void shutDown();
        void read(int x, int y);
        void write(int x, int y);
    private:
        static bool willShutDown_;
    }
    }// namespace I2C
    #endif // WRAPPER_HPP
    Wrapper.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #include <Wrapper.hpp>
    // pour disposer des exceptions 
    #include <stdexcept>
    // le fichier d'en-tete de la bibliothèque C
    #include <i2c.h>
    // pour pouvoir vérifier les préconditions
    #include <cassert>
    /* l'instance i2c issue de la bibliothèque  C */
        struct i2c * instance{nullptr};
    namespace I2C{
    bool Wrapper::willShutDown_ = false;
    Wrapper::Wrapper(){
        if(! instance)
            instance = i2c_init();
        if(! instance)
            throw std::runtime_error("unable to initialize i2c");
    }
    Wrapper::~Wrapper(){
        if(willShutDown_)
           i2c_destroy(instance);
    }
    /* read et write se contentent de déléguer le travail au fonctions de la bibliothèque C */
    void Wrapper::read(int x, int y){
        assert(instance && "I2C not initialized, please do it first");
       i2c_read(x, y);
    }
    void Wrapper::write(int x, int y){
        assert(instance && "I2C not initialized, please do it first");
       i2c_write(x, y);
    }
    void Wrapper::shutDown(){
        willShutDown_=true;
    }
    } // namespace I2C
    Le tout pourrait être utilisé sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <Wrapper.hpp>
    int main(){
        /* on crée une instance du wrapper, ce qui provoque l'initialisation de la bibliothèque I2C */
        Wrapper wrap;
        /* on peut écrire ou lire à partir du wrapper */
        wrap.read(10,15);
        wrap.write(50,25);
        /* quand on a fini, on indique qu'il faut nettoyer le tout */
        wrap.shutDown();
    } //les règles de portée font que notre variable wrap est détruite ici et que son destructeur est donc appelé
      // comme willShutDown_ est égal à true (après appel de la fonction shutDown), i2c_destroy est appelée
    Maintenant, je ne sais absolument rien de la bibliothèque C que tu utilises! je me suis donc contenter d'utiliser des noms qui semblaient logiques, mais qui pourraient être tout à fait différents (pour la structure et les différentes fonctions), mais le principe est malgré tout sensiblement pareil

    Pour que tout cela fonctionne, il suffira:
    1. d'ajouter le dossier dans lequel se trouve le fichier d'en-tête de la bibliothèque C lors de la compilation, sous une forme proche deg++ -c Wrapper.cpp -I. -Idossier_en-tete_i2c
    2. d'indiquer explicitement à l'éditeur de liens qu'il doit utiliser la bibliothèque C sous une forme proche deg++ -c Wrapper.o main.o -li2c -o myapp


    (je considère que la biblioithèque C est nommée libi2c.a, et j'utilise Gcc pour la compilation... à adapter à ta propre situation
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    Consultant informatique
    Inscrit en
    octobre 2004
    Messages
    11 176
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 176
    Points : 27 582
    Points
    27 582

    Par défaut

    Au fait, je ne sais pas si c'est cette bibliothèque que tu utilise, mais, as-tu déjà jeté un œil à la doc que l'on trouve ==>ici<==
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    novembre 2010
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : novembre 2010
    Messages : 68
    Points : 51
    Points
    51

    Par défaut

    Merci pour ta réponse

    J'utilse la method initializeI2C car je vais avoir plusieurs capteurs avec lesquels communiquer et j'aimerai n'avoir qu'une seule instance de mon controlleur I2C.
    Le probleme avec ton wrapper c'est qu'il est dans une class C++, c'est tout mon probleme si je veux appeler une de ces methodes depuis la library de mon capteur.

    Ce n'etait peut etre pas clair mais je ne veux pas toucher à l'implementation du driver (.c) si jamais il y a une nouvelle version je n'aurait pas trop de probleme ou mettre a jour
    , juste l'include de mon wrapper dans le header de la library.

    Voila comment est faite cette library (en gros)
    inv_mpu.h
    inv_mpu.c

    Dans mpu.c il ya des fonctions c qui appellent des fonctions read/writeI2c qui ne possede pas de signature dans inv_mup.h/.c
    J'ai donc creer des fichiers WrapperDriverMpu.h/.c qui implementent ces fonctions. Ces fonctions appellent alors ma class MPU6050 via un pointer
    qui appelent les methodes de l'I2c Controller.

    Concerant la library i2c je ne la conaissais pas, mais il semble que mon I2cControleur fait la meme chose mais encapsulé dans une class au lieu de fonctions

    Voila ce que j'ai fait jusqu'à présent, pour l'instance cela compile mais je ne l'ai pas encore exécuter. Si tu as des commentaires n'hésite pas
    Un point qui m'embête mais je ne pense pas avoir le choix c'est que les méthodes readMPU6050 et writeMPU6050 sont public

    WrapperMpu6050ptr.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    #include "Mpu6050.hpp"
     
    static ::sensors::Mpu6050* ptrMpuController = 0;
     
    void setI2cControllerPtr(::sensors::Mpu6050* pMpuController);
    WrapperDriverMpu.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    #pragma once
     
    #include "inv_mpu.h"
     
    #ifdef __cplusplus
    extern "C" {
    #endif
     
    unsigned char i2c_read(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char *data);
     
    unsigned char i2c_write(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char const *data);
     
    #ifdef __cplusplus
    }
    #endif
    WrapperDriverMpu.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    #include "WrapperDriverMpu.hpp"
    #include "WrapperMpu6050ptr.hpp"
    #include <errno.h>
    #include <time.h>
     
    void setI2cControllerPtr(::sensors::Mpu6050* pMpuController)
    {
        ptrMpuController = pMpuController;
    }
     
    unsigned char i2c_read(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char *data)
    {
        ptrMpuController->readMpu6050(slave_addr, reg_addr, length, data);
    }
     
    unsigned char i2c_write(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char *data)
    {
        ptrMpuController->writeMpu6050(slave_addr, reg_addr, length, data);
    }

    MPU6050.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    namespace sensors
    {
      class Mpu6050
      {
        public:
     
          Mpu6050(::communications::i2c::I2cController& i2cController);
     
          ~Mpu6050();
     
          int8_t readMpu6050(const uint8_t& devAddr, const uint8_t& regAddr, const uint8_t& length, uint8_t* data);
     
          void writeMpu6050(const uint8_t& devAddr, const uint8_t& regAddr, const uint8_t& length, uint8_t* data);
     
          ::communications::i2c::I2cController& _rI2cController;
     
        private:
     
          uint16_t _fd{0};
     
          uint8_t _deviceAddr{0x50};
      };
    }
    MPU6050.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    namespace sensors
    {
      Mpu6050::Mpu6050(::communications::i2c::I2cController& i2cController)
        : _rI2cController(i2cController)
      {
        setI2cControllerPtr(this);
        _rI2cController.initializeI2c(_fd, _deviceAddr);
      }
     
      Mpu6050::~Mpu6050()
      {
     
      }
     
      int8_t Mpu6050::readMpu6050(const uint8_t& devAddr, const uint8_t& regAddr, const uint8_t& length, uint8_t* data)
      {
     
        std::cout << "************************************" << std::endl;
        //_rI2cController.readBytes(_fd, regAddr, length, data);
      }
     
      void Mpu6050::writeMpu6050(const uint8_t& devAddr, const uint8_t& regAddr, const uint8_t& length, uint8_t* data)
      {
        std::cout << "************************************" << std::endl;
        //_rI2cController.writeBytes(_fd, regAddr, length, data);
      }
     
    }
    I2cController.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    namespace communications
    {
      namespace i2c
      {
        class I2cController
        {
          public:
     
            I2cController();
     
            ~I2cController();
     
            int8_t readBytes(uint16_t& fd, const uint8_t& regAddr, const uint8_t& length, uint8_t* data);
     
            int8_t writeBytes(uint16_t& fd, const uint8_t& regAddr, const uint8_t& length, uint8_t* data);
     
            void initializeI2c(uint16_t& fd, const uint8_t& devAddr);
     
            void closeI2c(uint16_t& fd);
        };   
      }
    }
    I2cController.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
     
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <cstdint>
    #include <unistd.h>
    #include <string.h>
    #include <linux/i2c-dev.h>
     
    #include "I2cController.hpp"
     
     
    namespace communications
    {
      namespace i2c
      {
     
        I2cController::I2cController()
        {
     
        }
     
        I2cController::~I2cController()
        {
     
        }
     
        void I2cController::initializeI2c(uint16_t& fd, const uint8_t& devAddr)
        {
          fd = open("/dev/i2c-1", O_RDWR);
     
          if(fd < 0)
          {
            //LOG_ERROR("I2cController :: readBytes() Failed to open device");
            return;
          }
     
          if (ioctl(fd, I2C_SLAVE, devAddr) < 0)
          {
              //LOG_ERROR("I2cController :: readBytes() Failed to select device"); 
              close(fd);
              return;
          }
        }
     
        int8_t readBytes(uint16_t& fd, const uint8_t& regAddr, const uint8_t& length, uint8_t* data)
        {
            int8_t count = 0;
            if (write(fd, &regAddr, 1) != 1) 
            {
                //LOG_ERROR("I2cController :: readBytes() Failed to write reg"); 
                close(fd);
                return(-1);
            }
            count = read(fd, data, length);
            if (count < 0) 
            {
                //LOG_ERROR("I2cController :: readBytes() Failed to read device");
                close(fd);
                return(-1);
            } else if (count != length)
            {
                //LOG_ERROR("I2cController :: readBytes() Short read from device");
                close(fd);
                return(-1);
            }
        }
     
        int8_t writeBytes(uint16_t& fd, const uint8_t& regAddr, const uint8_t& length, uint8_t* data)
        {
            int8_t count = 0;
            uint8_t buf[128];
     
            buf[0] = regAddr;
            memcpy(buf+1,data,length);
            count = write(fd, buf, length+1);
            if (count < 0) {
                //LOG_ERROR("I2cController :: writeBytes() Failed to write device"); 
                close(fd);
                return(-1);
            } else if (count != length+1) {
                //LOG_ERROR("I2cController :: writeBytes() Short write to device");
                close(fd);
                return(-1);
            }
        }
     
        void I2cController::closeI2c(uint16_t& fd)
        {
          close(fd);
        }
     
      }
    }

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    Consultant informatique
    Inscrit en
    octobre 2004
    Messages
    11 176
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 176
    Points : 27 582
    Points
    27 582

    Par défaut

    Je crois te l'avoir déjà dit : sans connaitre la bibliothèque C que tu utilises, il sera très difficile pour nous de t'aider. Donc, avant d'aller plus loin : quelle bibliothèque C utilises tu pour le i2c

    Pour essayer de faire avancer un peu le schmilblick, à voir ton code, j'ai envie de te demander "mais pourquoi perds tu ton temps à créer un wrapper" . Car, si c'est pour que l'utilisateur de ton wrapper doive, de toutes manières utiliser des fonctions strictement identiques à celles qui sont exposées par ta bibliothèque C, quel avantage compte tu en retirer, étant donné que C++ autorise sans aucun problème la plupart des code écrits en C (et nécessite, au pire, quelques adaptations à gauche et à droite)

    De plus, ton approche basée sur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #include "Mpu6050.hpp"
     
    static ::sensors::Mpu6050* ptrMpuController = 0;
     
    void setI2cControllerPtr(::sensors::Mpu6050* pMpuController);
    fout déjà en l'air ton souhait de pouvoir avoir plusieurs périphériques

    En effet, si tu travailles avec un pointeur unique, tu ne pourras jamais avoir qu'un ... unique périphérique de ce type à chaque instant, ce qui pose une très forte restriction par rapport à tes besoin

    De plus, cette approche oblige l'utilisateur de ton wrapper à créer lui-même le périphérique avant de l'utiliser, et à veiller à le détruire correctement lorsqu'il n'en a plus besoin.

    Le mot clé en C++ pour éviter cela est RAII (Ressource Acquisition Is Initialisation : l'acquisition de ressources sert à l'initialisation) et son pendant direct RFID (Ressource Freeing Is Destruction).

    Au final, tu devrais sans doute avoir quelque chose qui serait proche de:
    1- quelque chose pour représenter les données (lues et écrites). Ne sachant pas trop bien de combien de donnée tu auras besoin, nous pourrions partir sur quelque chose permettant de représenter 256 bytes, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    using DataType = std::array<unsigned char, 255>;
    (nécessite C++11 et l'inclusion de <array> pour compiler)

    2- une classe wrappant la structure I2CDevice (comme je ne sais pas quelle bibliothèque tu utilise, je vais prendre exemple sur celle dont j'ai fourni le lien ) qui prendra en charge l'initialisation du périphérique, sur base du port sur lequel il est connecté lors de la création, et qui permet d'invoquer les fonction write et read, sous une forme proche de
    Device.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #ifndef DEVICE_HPP
    #define DEVICE_HPP
    /* Pour que l'on puisse disposer de la structure I2CDevice
    #include <i2c.h>
    namespace I2C{
    class Device{
    public:
        /* initialise correctement la donnée de type I2CDevice grâce au port sur lequel
         * on le cherche
         */
        Device(std::string const & path);
        /* ferme correctement la communication avec le périphérique */
        ~Device();
        /* parce que la copie et / ou l'affectation d'instances de cette classe risque de poser problème */
        Device(Device const &) = delete;
        Device & operator=(Device const & ) = delete;
        /* récupère "un certain nombre" de byte depuis le périphérique, et les places dans notre représentation
         * des données
         * préconditions : size <= device.page_bytes
         */
        void read(unsigned int size, DataType & datas, unsigned int addr = 0x0);
        /* envoie "un certain nombre" de bytes vers le périphérique
         * lance une exception si tous les bytes ne sont pas écrits
         * préconditions : size <= device.page_bytes
         */
       void write(unsigned int size, DataType const & datas);
        };
    private:
        I2CDevice device;
    };
    } // namespace I2C
    #endif // DEVICE_HPP
    Device.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #include <Device.hpp>
    #include <stdexcept>
    namespace I2C{
    Device::Device(std::string const & path){
        int bus = i2c_open(path.c_str());
        if(bus == -1)
            throw std::runtime_error("unable to connect the device");
        memset(&device, 0, sizeof(device));
        device.bus = bus;
        device.addr = 0x50;
        device.page_bytes = 16;
    }
    Device::~Device(){
        i2c_close(device.bus);
    }
    void Device::read(unsigned int size, DataType & datas, unsigned int addr){
        assert(size <= device.page_bytes && "to much data to read");
        unsigned int recup = i2c_read(&device, addr, reinterpret_cast<void*>(datas.datas()), size);
        if(recup!= size){
            throw std::runtime_error("error while reading datas");
        )
    }
    void Device::write(unsigned int size, DataType const & datas, unsigned int addr){
        assert(size <= device.page_bytes && "to much data to write");
        unsigned int recup = i2c_write(&device, addr, reinterpret_cast<void const *>(datas.datas()), size);
        if(recup!= size){
            throw std::runtime_error("error while reading datas");
        )
    }
    } //namespace I2C
    3- Si tu veux pouvoir copier les données d'un senseur directement sur un autre, rien ne t'empêche de créer une fonction proche de
    CopyI2C.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    #ifndef COPYI2C_HPP
    #define COPYI2C_HPP
    namespace I2C{
        class Device;
    void copy(unsigned int size, Device & src, Device const & dest, unsigned int srcAddr = 0x0, unsigned int destAddr = 0x0);
    } // namespace I2C
    #endif // COPYI2C_HPP
    CopyI2C.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <CopyI2C.hpp>
    #include <Device.hpp>
    namespace I2C{
    void copy(Device & src, Device const & dest, unsigned int srcAddr, unsigned int destAddr){
        DataType temp;
        src.read(size, temp, srcAddr);
        dest.write(size, temp, destAddr;
    }
    } // namespace I2C
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    novembre 2010
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : novembre 2010
    Messages : 68
    Points : 51
    Points
    51

    Par défaut

    Salut,

    Je te remercie pour ces explications, mais on s'ecarte du sujet principale. Je suis désolé je me suis mal exprimé.
    Mais avant pour répondre a tes questions:
    - J'utilise la libraire i2c-dev
    - J'utilise un pointer unique car l'adresse sur le bus du capteur est fixe (il ne peut y en avoir 2 identiques)
    - Je n'utilise le wrapper QUE dans le cas de la communication avec le capteur mpu6050 (detaillé plus bas **) .
    - J'ai fait une classe I2cControlleur pour respecter le principe SOLID de Single Responsability. Le bus i2c est géré par la meme class quelque soit le capteur avec lequel je souhaite communiquer. A Chaque capteur j'aurai une classe qui sera en charge d'initialiser son file descriptor. C'est cette classe capteur qui connait l'adress du capteur associé sur le bus.
    Mais je fais peut etre fausse route sur ce dernier point ?

    L'utilisateur final (Moi ) a alors juste beosin de creer les class "Capteurs".
    Lors de la creation de la class Mpu6050 il n'aura rien d'autre a faire (initialization i2c / wrapper .. tout est fait dans cette classe)


    **Explication de pourquoi j'ai besoin d'un wrapper C C++.
    La mpu6050 est un capteur qui possede un DMP qui doit etre configuré, la libraire fournit est en C et je n'ai pas envie de l'implementer dans une class C++. Par contre le reste de mon application est en C++. J'ai donc une class I2cControlleur en charge de gérer le bus (une simple encapsulation de la library i2c-dev) avec laquelle mes classes capteurs vont communiquer avec leur harware respectif.
    Concernant le driver fournit pour le capteur mpu6050 le code appelle des functions i2c_write/i2c_read.
    Dans ce driver il est explicitement dit :

    /* The following functions must be defined for this platform:
    * i2c_write(unsigned char slave_addr, unsigned char reg_addr,
    * unsigned char length, unsigned char const *data)
    * i2c_read(unsigned char slave_addr, unsigned char
    */

    Mon probleme n'est donc pas lié a la communications I2C, ma class marche tres bien avec les autres capteurs .
    Mais plutot etait comment appeler depuis une function C une methode d'un objet C++ existant?
    Que ce soit pour ecrire sur le bus i2C ou afficher un cout etait secondaire .

    actuellement je set un pointer dans mon wrapper qui me permet d'appeler la methode de mon object dans les functions i2c_write/i2c_read que j'ai redefini dans le wrapper
    Mais peut etre qu'il y a une meilleure solution.

    Cordialement.

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    Consultant informatique
    Inscrit en
    octobre 2004
    Messages
    11 176
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 176
    Points : 27 582
    Points
    27 582

    Par défaut

    Citation Envoyé par boubouboy Voir le message
    Salut,

    Je te remercie pour ces explications, mais on s'ecarte du sujet principale. Je suis désolé je me suis mal exprimé.
    Mais avant pour répondre a tes questions:
    - J'utilise la libraire i2c-dev
    - J'utilise un pointer unique car l'adresse sur le bus du capteur est fixe (il ne peut y en avoir 2 identiques)
    Je t'accorde que tes capteurs soient fixes, mais tu le dis toi-même : tu as potentiellement plusieurs capteurs en même temps. Tu n'as donc pas un capteur unique.

    En travaillant (que ce soit avec un pointeur) global et -- qui plus est -- statique, tu forces ton application à ne pouvoir utiliser qu'un seul capteur, ce qui va à l'encontre de tes besoins.

    La classe telle que je te la présente dispose bel et bien d'un capteur fixe (vu qu'elle n'offre aucun moyen de modifier le capteur "en cours de route" depuis l'extérieur et qu'aucune des fonctions membres ne prend le risque de le faire)

    Il ne manque que le moyen d'assurer "l'unicité référentielle" des différents capteur (comprend : le fait que l'adresse de chaque capteur doit être différente des autres).

    Le plus simple est donc encore de garder 'quelque part" la liste à jour des adresses déjà utilisées, et de s'assurer que l'on n'utilise pas deux fois la même adresse.

    Cela pourrait se faire assez facilement à l'aide d'un std::set et d'un petit test. Par facilité, nous créerions sans doute une petite classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class SensorCreator{
    public:
        /* permet de s'assurer que l'adresse indiquée n'est pas encore enregistrée 
         * lance une exception si l'adresse existe déjà
         */
        void checkUique(std::string const & busAddress);
        /* permet d'enregistrer l'adresse d'un nouveau capteur */
        void add(std::string const & busAddress);
        /* si on peut ajouter une nouvelle adresse, il faut pouvoir retirer
         * l'adresse d'un capteur fermé
          */
        void deviceClosed(std::string const & busAddress);
    private:
        std::set<std::sring> addresses;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void AddressKeeper::checkUnique(std::string const & busAddress){
        if(addresses.find(busAddress)!= addresses.end())
            throw("This address bus is already opened");
    }
    void AddressKeeper::add(std::string const & busAddress){
        checkUnique(bussAddress);
        addresses.insert(busAddress);
    }
    void AddressKeeper::deviceClosed(std::string const & busAddress){
        auto it = adresses.find(busAddress);
        assert(it!= addresses.end() && "inexistant adress");
        adresses.erase(it);
    }
    qui pourrait être utilisée sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    int main(){
        AddressKeeper addresses;
        try{
           addresses.add("/dev/sens-0");
           Device dev1{"/dev/sens-0"};
           /* no problem :*/
           addresses.add("/dev/sens-1");
           Device dev2{"/dev/sens-1"};
           /* oupps ... will throw ince "/dev/sens-0" exists */
           addresses.add("/dev/sens-0");
           Device dev3{"/dev/sens-0"};
     
        }catch(std::runtime_error &e){
            std::cout<<e.what();
            throw e;
        }
        return 0;
    }
    (exemple minimaliste s'il en est, largement adaptable aux besoins )
    - Je n'utilise le wrapper QUE dans le cas de la communication avec le capteur mpu6050 (detaillé plus bas **) .
    Est-ce une raison suffisante pour limiter ton wrapper à ce seul usage

    Dis toi que, si ton wrapper présente un intérêt quelconque, tu pourrais le fournir à d'autres développeurs qui utilisent peut-être d'autres capteurs. Les limiter à la seule utilisation du capteur mpu6050 les empêcherait d'y avoir recours

    Et puis, même pour toi: je ne sais pas ce que fait ce capteur, ni l'application que tu veux développer grâce à ton wrapper (je ne sais donc pas grand chose, je te l'accorde ). Mais je sais deux choses : une application évolue en permance. Et une entreprise qui n'évolue pas est une entreprise qui périclite.

    Que ce soit ton application ou l'entreprise qui l'utilisera, il arrivera forcément un moment (que ce soit dans six mois ou dans un an) où une "toute nouvelle machine" fera son apparition, avec des capteurs différents, peut-être utilisés dans des buts totalement différents (ou non ...).

    Que vas tu faire à ce moment là créer un tout nouveau wrapper spécifique à ce nouveau capteur, ou te contenter de faire évoluer ton wrapper pour lui permettre d'utiliser ce nouveau capteur en plus du MPC6050

    N'as tu pas l'impression que l'évolution de ton wrapper actuel sera plus facile, demandera moins de boulot que le fait de créer un tout nouveau wrapper à partir de rien

    Ce n'est pas mon projet, je n'ai aucune raison d'imposer ma vue dans sa conception, mais je sais comment je traiterais la chose si j'étais à ta place
    - J'ai fait une classe I2cControlleur pour respecter le principe SOLID de Single Responsability. Le bus i2c est géré par la meme class quelque soit le capteur avec lequel je souhaite communiquer.
    N'est ce pas le cas de la classe que je présente comme exemple Tout ce qu'elle fait (en respectant le SRP), c'est:
    • ouvrir la connexion avec le capteur (dans le destructeur)
    • permettre la lecture du capteur
    • permettre l'écriture (si c'est utile) vers le capteur
    • fermer la connexion avec le capteur (dans le destructeur)


    A Chaque capteur j'aurai une classe qui sera en charge d'initialiser son file descriptor.
    Non, tu n'as pas besoin d'une classe pour chaque capteur! Tu as besoin d'une instance de classe (toujours la même) pour chaque capteur! C'est toute la différence entre un type de donnée (la classe) et une variable/une donnée

    C'est cette classe capteur qui connait l'adress du capteur associé sur le bus.
    Non, c'est l'instance de la classe qui connait l'adresse du capteur associé sur le bus ! C'est la donnée (la variable) qui correspond à un capteur donné et qui connait cette adresse. La classe (toujours la même), elle, elle se contente de ... fournir le moyen de maintenir cette adresse en mémoire.

    Cette information manque effectivement dans ma classe, et nous pourrions la rajouter sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class Device{
    public:
        /* initialise correctement la donnée de type I2CDevice grâce au port sur lequel
         * on le cherche
         */
        Device(std::string const & path);
        /* ferme correctement la communication avec le périphérique */
        ~Device();
        /* parce que la copie et / ou l'affectation d'instances de cette classe risque de poser problème */
        Device(Device const &) = delete;
        Device & operator=(Device const & ) = delete;
        /* récupère "un certain nombre" de byte depuis le périphérique, et les places dans notre représentation
         * des données
         * préconditions : size <= device.page_bytes
         */
        void read(unsigned int size, DataType & datas, unsigned int addr = 0x0);
        /* envoie "un certain nombre" de bytes vers le périphérique
         * lance une exception si tous les bytes ne sont pas écrits
         * préconditions : size <= device.page_bytes
         */
       void write(unsigned int size, DataType const & datas);
        };
        std::string const & address() const;
    private:
        std::string busAddress;
        I2CDevice device;
    };
    avec adaptation du construteur;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Device::Device(std::string const & path):
        busAddress{path}{  
        /* le reste, comme précédemment */
    }
    et l'implémentation de la nouvelle fonction sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::string const & Device::address() const{
        return busAddress;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Mais je fais peut etre fausse route sur ce dernier point :D ?
    Ben oui, parce que l'on parle de donnée, et donc d'instances de la classe
    L'utilisateur final (Moi ) a alors juste beosin de creer les class "Capteurs".
    Méfie toi comme de la peste de ce genre de raisonnement, car la loi de Finagle nous dit que l'utilisateur est un imbécile distrait qui n'attend que l'occasion de faire une connerie. Ni toi ni moi ne valons mieux que les autres à ce point de vue .

    Si bien que si tu te laisse ne serait-ce que l'occasion de faire une connerie, tu ne doit même pas perdre ton temps à te demander si tu feras une connerie, vu que cette loi répond par l'affirmative à cette question. Poses toi plutôt la question de quand tu feras une connerie

    Pour notre malheur, la loi de l'emmerdement maximum y apporte la réponse : au pire moment qui soit

    Dis toi, pour éviter les problèmes, que tu n'est le développeur (celui qui sait réellement ce qu'il doit faire) d'une fonctionnalité (quelle qu'elle soit) que le temps nécessaire à en écrire (et à en tester) le code, et que, dés que cette étape est terminée, tu deviens un utilisateur "comme les autres"

    Lors de la creation de la class Mpu6050 il n'aura rien d'autre a faire (initialization i2c / wrapper .. tout est fait dans cette classe)
    De la manière dont je présente les choses, oui, c'est bel et bien ce qui se passe. Mais pas de la manière dont tu présentes les choses
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème pour library caml
    Par magnum1506 dans le forum Caml
    Réponses: 2
    Dernier message: 29/01/2010, 07h59
  2. Wrapper GLScene pour Purebasic
    Par Newbie1 dans le forum PureBasic
    Réponses: 1
    Dernier message: 13/10/2009, 22h31
  3. Réponses: 5
    Dernier message: 07/06/2009, 13h46
  4. Réponses: 9
    Dernier message: 30/10/2008, 16h04
  5. [Interop] Wrapper .NET pour les API Windows ?
    Par tomlev dans le forum C++/CLI
    Réponses: 3
    Dernier message: 31/03/2008, 10h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo