IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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

Python Discussion :

Enregistrer des données Compteurs avec pyModbusTCP dans MySQL


Sujet :

Python

  1. #1
    Membre du Club
    Homme Profil pro
    Dessinateur industriel
    Inscrit en
    Février 2021
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Dessinateur industriel
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2021
    Messages : 90
    Points : 42
    Points
    42
    Par défaut Enregistrer des données Compteurs avec pyModbusTCP dans MySQL
    Bonjour,
    Je fait un applicatif de suivi de consommations de compteurs d'eau qui va tourner sur un serveur local Linux (RPI ou équivalent).
    J'ai découvert le PHP et réalisé l'interface utilisateur et jusqu'à maintenant j'ai simulé des index compteurs manuellement dans MySQL. J'aimerai donc passer à la partie la plus importante : Communiquer avec le matériel (compteurs) et récupérer leurs valeurs d' "index".
    Ces index compteurs sont dans un automate programmable qui est client Modbus TCP et me permet de lire les valeurs (Ce que je fait sans soucis depuis mon PC avec ModbusDoctor)

    La fréquence "d'acquisition" est définie par l'utilisateur 15, 30, 45 min et stockée dans une table MySQL.
    L'adresse IP, le port de l'automate et le registre Modbus du compteur 1 sont eux aussi défini par l'utilisateur et stockés dans une table.
    Il y a même plusieurs compteurs, donc à chaque fois IP, Port, Registre... Chaque compteur à un ID unique

    Pour la partie communication, j'ai trouvé la librairie pyModbus :
    Exemple lecture de registres : https://pymodbustcp.readthedocs.io/e..._register.html
    Exemple Tache cyclique : https://pymodbustcp.readthedocs.io/e..._schedule.html
    Et un dernier adapté à mon besoin mais fait pour une application de domotique : https://github.com/DomoticX/domoticz-modbus
    mais je n'ai aucune idée de comment adapter ces exemples à mon projet :

    1. 1) Lire la "fréquence d'acquisition" dans la base de données.
    2. 2) Se connecter aux équipements et récupérer les index dans les bons registres
    3. 3) Insérer les valeurs dans MySQL en lien avec leur ID.
    4. 4) Faire en sorte que cela tourne en tache de fond pour se reproduire à la bonne fréquence d'acquisition


    Je part de 0, je ne suis pas du métier (dessinateur industriel / automaticien), j'ai découvert html, php et mysql sur ce projet, j'en arrive à Python mais cette fois j'ai besoin d'un (gros) coup de pouce pour me montrer le bon chemin à suivre.
    Merci de votre aide.

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Citation Envoyé par makimax Voir le message
    Je part de 0, je ne suis pas du métier (dessinateur industriel / automaticien), j'ai découvert html, php et mysql sur ce projet, j'en arrive à Python mais cette fois j'ai besoin d'un (gros) coup de pouce pour me montrer le bon chemin à suivre.
    Si on ne dispose pas des équipements, on ne pourra pas tester...

    Si on ne peut pas tester, on devra se contenter de faire une lecture (intelligente à défaut d'active) des différents articles trouvés ici ou là et des documentations et vous pousser du code à tester.

    Et si vous ne savez pas trop programmer, ça ne va pas être facile de récupérer les informations pertinentes pour comprendre ce qu'il se passe (parce qu'on ne se déplace pas!).

    Le mieux serait de trouver un club d'informatique dans votre localité pour échanger de vive voix avec des qui pourront se déplacer au cas où...

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre du Club
    Homme Profil pro
    Dessinateur industriel
    Inscrit en
    Février 2021
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Dessinateur industriel
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2021
    Messages : 90
    Points : 42
    Points
    42
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Salut,
    Si on ne dispose pas des équipements, on ne pourra pas tester... - W
    Il n'y a pas nécessairement besoin d'équipement, moi même j'avance dans mon projet sans le matériel, j'ai juste un Apache + MySQL et un Simulateur Modbus.

    Si on ne peut pas tester, on devra se contenter de faire une lecture (intelligente à défaut d'active) des différents articles trouvés ici ou là et des documentations et vous pousser du code à tester.
    C'est justement cela que je cherche, des propositions de morceaux de code d'ici ou là.

    Et si vous ne savez pas trop programmer, ça ne va pas être facile ...
    Je suis assez débrouillard, la preuve en ayant fait le frontend en partant de 0 et ne connaissant ni html, ni php, ni mySQL.

    Je sais qu'il faut que je commence par un script qui se lance au démarrage, qui se connecte à la base de données et qui fait un SELECT des paramètres pour l'étape suivante (récupérer la fréquence d'acquisition et les différentes adresses des compteurs à récupérer).
    Déjà si quelqu'un peut me guider comment commencer cela. La connexion à la base, je le fait pour l'utilisateur dans un fichier php, j'imagine qu'il faut reprendre cette partie. Et ensuite les requêtes de SELECT, je saurais les faire puisque j'en ai aussi en PDO en PHP, mais j'imagine que ce n'est pas écrit de la même façon.

    Ensuite, cela sera l'étape de connexion, je m'inspirerai des exemples cités.

  4. #4
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par makimax Voir le message
    Déjà si quelqu'un peut me guider comment commencer cela. La connexion à la base, je le fait pour l'utilisateur dans un fichier php, j'imagine qu'il faut reprendre cette partie. Et ensuite les requêtes de SELECT, je saurais les faire puisque j'en ai aussi en PDO en PHP, mais j'imagine que ce n'est pas écrit de la même façon.
    Les mécanismes d'interrogation d'une bdd MySQL (ou Postgres ou n'importe quelle autre) se font toujours de la même façon
    • on crée une instance connexion à partir d'une librairie dédiée à interfacer le langage. Cette instance reçoit les divers paramètres comme le nom de la bdd, le login, le mot de passe, etc.
    • cette instance (un objet) une fois connectée possède une fonction de création de curseur. Un curseur c'est l'outil dédié au requêtage SQL. Donc on crée le curseur (c'est bien souvent curs=connect.cursor(), "connect" étant l'instance de connexion). Et ce curseur pourra ensuite envoyer des requêtes SQL (curs.execute(requete)). Et pour la sélection, ce sera un curs.execute("select ...") suivi d'un res=curs.fetchone() si la requête ne renvoie qu'un résultat ou res=curs.fetchall() si elle en renvoie plusieurs. Certaines librairies bdd possèdent même parfois des outils où la requête est claquée dans un dictionnaire (si la table contient "nom", "prenom" alors le résultat est claqué dans un dico contenant une clef "nom" et une clef "prenom" ce qui est ensuite assez pratique pour le manipuler)
    • quand on a fini de manipuler, on ferme le curseur et quand on a fini de travailler sur la bdd on ferme la connexion


    Bon le souci c'est que je suis plus spécialisé dans Postgres donc je ne connais pas trop les librairies Python/MySQL (il y a souvent beaucoup de librairies dédiées à une même technologie donc il faut essayer de taper dans celle destinée à survivre) mais j'ai trouvé cette page qui parle de pymysql avec différents exemples. Et comme on peut voir "python36" dans certaines copies d'écran cela signifie que le truc n'est pas super vieux.

    En fait, Python c'est un peu comme une grosse pieuve. D'un côté un tentacule pour modbus, un autre tentacule pour MySQL et lui au centre il récupère tout ça et fait sa salade. Puis il renvoie le résultat soit sur un autre support (mail, fichier, etc) soit ça repart dans MySQL. Avec possibilité ensuite d'enrichissement. Si par exemple tu inclus le module configparser (qui permet de lire et d'écrire un fichier type ".ini") alors tu pourras y mettre tes paramètres de connexion ce qui t'évite de le mettre en dur dans ton code. Donc un 3° tentacule qui commence par récupérer les infos du ini et qui sont alors utilisées par le tentacule MySQL etc etc etc.
    Plus tu sépares bien les choses plus ça sera pratique à maintenir et à faire évoluler si demain les technos changent...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Membre du Club
    Homme Profil pro
    Dessinateur industriel
    Inscrit en
    Février 2021
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Dessinateur industriel
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2021
    Messages : 90
    Points : 42
    Points
    42
    Par défaut
    Merci Sve@r, je vais tenter de ce côté ! je ne pensait pas qu'il fallait une librairie pour mySQL, donc PyMySQL dans un premier temps et suivre les exemples pour lire une valeur dans une table.
    claqué dans un dico contenant une clef "nom" et une clef "prenom" ce qui est ensuite assez pratique pour le manipuler
    Cela ressemble énormément au php, avec le FetchAll(FETCH ASSOC) qui "claque" la réponse dans un Array, donc très pratique.

    Merci pour le conseil de bien dissocier les étapes, je suis dans cette optique aussi, encore faut-il que je réussisse chacune individuellement ^^.

    Citation Envoyé par wiztricks Voir le message
    Salut,
    Le mieux serait de trouver un club d'informatique dans votre localité pour échanger de vive voix avec des qui pourront se déplacer au cas où...
    J'ai un FabLab à proximité mais je n'y ai jamais mis les pieds, je ne sais pas si c'est le genre de projet qui peut être aborder. Je vais essayer de les contacter.

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Citation Envoyé par makimax Voir le message
    Il n'y a pas nécessairement besoin d'équipement, moi même j'avance dans mon projet sans le matériel, j'ai juste un Apache + MySQL et un Simulateur Modbus.
    Vous croyez que tout le monde a ce genre de trucs installé sur son PC et sait s'en servir juste parce qu'il sait programmer avec Python?

    Citation Envoyé par makimax Voir le message
    Je suis assez débrouillard, la preuve en ayant fait le frontend en partant de 0 et ne connaissant ni html, ni php, ni mySQL.

    Je sais qu'il faut que je commence par un script qui se lance au démarrage, qui se connecte à la base de données et qui fait un SELECT des paramètres pour l'étape suivante
    Mouais, si vous aviez pris l'initiative de chercher avec les mots clefs "python mysql" vous auriez trouvé la documentation de bibliothèques qui permettent de... et les exemples qui vont avec.

    En général, l'utilisation des bases de données depuis Python se fait avec une interface assez homogène basée sur la bibliothèque sqlite3 (qui est standard).
    Tous les bons tutos ont un chapitre qui explique comment çà marche.

    Citation Envoyé par makimax Voir le message
    C'est justement cela que je cherche, des propositions de morceaux de code d'ici ou là.
    Il y en a plein sur Internet! (et vous en avez trouvé).

    Après le soucis est de comprendre ce qu'ils font et de voir en quoi ils sont intéressants pour faire ce que vous voulez vous (et on n'est pas dans votre tête donc on ne sait pas vraiment trier).

    Je sais qu'il faut que je commence par un script qui se lance au démarrage, qui se connecte à la base de données et qui fait un SELECT des paramètres pour l'étape suivante (récupérer la fréquence d'acquisition et les différentes adresses des compteurs à récupérer).

    Citation Envoyé par makimax Voir le message
    Déjà si quelqu'un peut me guider comment commencer cela. La connexion à la base, je le fait pour l'utilisateur dans un fichier php, j'imagine qu'il faut reprendre cette partie. Et ensuite les requêtes de SELECT, je saurais les faire puisque j'en ai aussi en PDO en PHP, mais j'imagine que ce n'est pas écrit de la même façon.

    Ensuite, cela sera l'étape de connexion, je m'inspirerai des exemples cités.
    Si vous n'avez que la connexion à la base comme problème, vous savez qu'il n'est pas insoluble. Vous pouvez commencer le reste en simulant la récupération de l'identifiant par une simple saisie clavier... et revisiter l'aspect BDD plus tard.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par makimax Voir le message
    Merci pour le conseil de bien dissocier les étapes, je suis dans cette optique aussi, encore faut-il que je réussisse chacune individuellement ^^.
    Python possède un mécanisme assez intéressant permettant de tester ses propres modules individuellement afin de les valider : la variable "__name__".

    Un module c'est un truc qui fait un travail et que l'on peut importer ensuite dans son programme. On a alors accès à la (ou les) fonctions du module. Ce module ça peut-être un simple script ou même un dossier.

    Imaginons que tu veuilles créer un module de math que tu appelles "math.py" dont le code pourrait être le suivant
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #!/usr/bin/env python3
    # coding: utf-8
     
    def carre(n): return n*n

    Tu veux maintenant vérifier que ton script "math.py" et surtout sa fonction "carre" fonctionne. Tu pourrais rajouter en fin de script print(carre(5)).
    Le souci, c'est que dès que ton module "math.py" sera importé, le print() sera exécuté ce qui est au minimum dérangeant.

    Pour éviter ce souci tout en voulant garder ton test unitaire, tu rajoutes en fin de script
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if __name__ == "__main__":
    	print(carre(5))
    	... (tes autres tests éventuels)
    # if

    Cette variable interne Python "__name__" a un comportement différent selon l'utilisation du script. Si le script est importé, alors elle prend le nom de l'import comme contenu. Mais si le script est directement exécuté, alors elle prend la string "__main__".
    Donc tu veux tester ton module, tu appelles python3 math.py et tu verras tous tes tests se faire et tu pourras corriger ce qui ne va pas. Puis ton module étant fiabilsé, il peut alors être importé dans ton programme sans souci et sans que les tests ne soient fait de nouveau.

    Et pour l'utilisation de ton module, te suffit de mettre import math dans un programme pour que ce programme ait accès à la fonction "carre()" sous le nom math.carre(). Ou alors from math import carre ou même from math import * et là les fonctions du fichier "math.py" sont directement importées dans l'espace de nom de ton code donc "carre()" se nomme alors directement carre() (plus attrayant mais aussi plus dangereux si plusieurs modules distincts ont des fonctions de même nom). Et là enfin le projet commence à s'articuler...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Citation Envoyé par Sve@r Voir le message
    Et là enfin le projet commence à s'articuler...
    Découper le problème en fonctions qui réalisent les différentes étapes mentionnées telles que:
    1) Lire la "fréquence d'acquisition" dans la base de données.
    2) Se connecter aux équipements et récupérer les index dans les bons registres
    3) Insérer les valeurs dans MySQL en lien avec leur ID.
    4) Faire en sorte que cela tourne en tache de fond pour se reproduire à la bonne fréquence d'acquisition
    quitte à simuler certaines qui paraissent difficiles comme récupérer la fréquence d'acquisition par le retour d'une constante ou le choix aléatoire dans une liste ou l'écriture des valeurs par des "print" simples permet d'avancer côté "structure" et "articulation".

    Comme on part de rien... on peut tout avoir dans un même script et tester le bon déroulement de la séquence des opérations.

    Après on peut ajouter de la viande autour de ce squelette en étoffant une opération particulière. Il pourra alors être utile de découper la fonction initiale en sous fonctions.

    Une fois satisfait du résultat voir comment pousser la chose dans un module (mais ce n'est pas nécessaire) en ouvrant un tuto qui raconte comment çà marche pour isoler ce tas là du reste qui est en chantier...

    Et comme on a pris le soin de découper son problème, en cas de difficultés on pourra plus facilement demander de l'aide en postant un code qui permet de le reproduire (sans avoir à palabrer sur le contexte puisque le code suffit) après avoir cherché sur Internet.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  9. #9
    Membre du Club
    Homme Profil pro
    Dessinateur industriel
    Inscrit en
    Février 2021
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Dessinateur industriel
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2021
    Messages : 90
    Points : 42
    Points
    42
    Par défaut
    Merci à vous deux pour vos conseils,

    J'ai installé la librairie pyMySQL et tenté la connexion à la base de donnée et une requête simple avec print du résultat. Cela à fonctionner du premier coup. Je pouvais en effet considéré que c'était bon d'avance et simuler pour passer la suite j'ai souhaité démarré sur une partie en théorie simple.

    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
    import pymysql.cursors
     
    # Connect to the database
    connection = pymysql.connect(host='127.0.0.1',
                                 user='root',
                                 password='',
                                 database='database',
                                 charset='utf8mb4',
                                 cursorclass=pymysql.cursors.DictCursor)
     
    with connection:
        with connection.cursor() as cursor:
            # Read a single record
            sql = "SELECT `dev_id`, `dev_name` FROM `devicestatus` WHERE `dev_used`= 1"
            cursor.execute(sql,)
            result = cursor.fetchall()
            print(result)
    J'obtient un résultat sous cette forme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [{'dev_id': 7, 'dev_name': 'Compteur 07'}, {'dev_id': 8, 'dev_name': 'Compteur 08'}, {'dev_id': 9, 'dev_name': 'Compteur 09'}, {'dev_id': 10, 'dev_name': 'Compteur 10'}, {'dev_id': 11, 'dev_name': 'Compteur 11'}, {'dev_id': 12, 'dev_name': 'Compteur 12'}]
    J'ai installé la librairie pyModbus. J'ai démarré un soft qui simule un équipement modbus et saisie une valeur dans le registre 100. Pour vérifier que cela fonctionne avant de me lancer dans python, j'ai tester de lire ce registre avec l'utilitaire client : ModbusDoctor, de ce côté, c'était bon j'ai récupéré ma valeur 123 dans le registre 100.
    J'ai récupéré des examples de script pour faire un "ReadHoldingRegisters" avec python et je n'ai eu que des erreurs. J'ai fini par réussir en remplaçant l'utilitaire simulateur par un soft d'automate gratuit que j'ai passé en simulation (MachineExpertBasic) et ensuite c'était bon. J'ai pu récupérer ma valeur 123 de ce registre 100.

    Avec le bout de script, je récupère une 'liste' (si je ne me trompe pas sur le terme) avec à l'intérieur des entier de 16bits. Si j'ai demandé une lecture du registre 100 et d'une longueur de 2, j'ai donc [123, 0] en résultat. Dans mon cas la valeur d'index du compteur est un mot de 32bit non signé, j'ai donc trouvé un bout de code qui remet en forme le résultat lorsqu'il utilise les 2 int.

    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
    from pymodbus.constants import Endian
    from pymodbus.payload import BinaryPayloadDecoder
    from pymodbus.client.sync import ModbusTcpClient
     
    def validator(instance):
        if not instance.isError():
            '''.isError() implemented in pymodbus 1.4.0 and above.'''
            decoder = BinaryPayloadDecoder.fromRegisters(
                instance.registers,
                byteorder=Endian.Big, wordorder=Endian.Little
            )   
            return format(decoder.decode_32bit_uint())
     
        else:
            # Error handling.
            print("There isn't the registers, Try again.")
            return None
     
    # Paramètres pour la connexion au matériel Modbus TCP
    Setting_TCP_IP = '127.0.0.1'
    Setting_TCP_PORT = 502
    Setting_Device_ID = 1
     
    # Connexion au matériel Modbus TCP
    client = ModbusTcpClient(host=Setting_TCP_IP, port=Setting_TCP_PORT, auto_open=True, auto_close=True, timeout=2)  # Specify the port.
    connection = client.connect()
     
    # Paramètres pour la lecture des registres du matériel Modbus TCP
    Setting_Register_Number = 100
    Register_Count = 2
     
     
    if connection:
        request = client.read_holding_registers(Setting_Register_Number, Register_Count, unit=1)  # Specify the unit.
        # Afficher le résultat décodé en Entier de 32 Bits non signé
        data = validator(request)
        print(data)
        # Afficher le résultat brut
        result = request.registers
        print(result)
     
        client.close()
     
    else:
        print('Connection lost, Try again')
    Si je simule un index de 777777, j'otiens les 'print' suivant :
    Le premier est décodé., le second non, c'est la liste des 2 uint16

    j'ai donc pour le moment deux parties (mysql et modbus), aucun lien entre elles mais le but c'est que le résultat de la requête en partie 1 sert de paramètres pour la récupération d'index en partie 2.

    Si j'ai bien saisie vos remarques sur le scindement des fonctions, j'aurais donc une 1ère fonction connexion, une 2de pour la requête pour avoir la liste des compteurs et de leur paramètres et registres à aller chercher, puis une boucle pour chaque compteur : 3è fonction : la lecture en utilisant les paramètres et registres, 4è fonction : écrire dans la base de données fin de boucle

    Dans un example de code qui m'intéresse il utilise par exemple : self.address, ou sefl.port, là ou moi je met address

  10. #10
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Citation Envoyé par makimax Voir le message
    Si j'ai bien saisie vos remarques sur le scindement des fonctions, j'aurais donc une 1ère fonction connexion, une 2de pour la requête pour avoir la liste des compteurs et de leur paramètres et registres à aller chercher, puis une boucle pour chaque compteur : 3è fonction : la lecture en utilisant les paramètres et registres, 4è fonction : écrire dans la base de données fin de boucle
    Vous n'êtes pas obligé de découper votre code en "fonctions" et le garder comme une seule séquence d'instructions.

    On essaie juste de vous suggérer de "bonnes pratiques"... ouvrir un tuto. pour apprendre les bases en fait partie. Et si vous ne voulez pas vous y appliquer, c'est votre choix. Mais au plus votre code deviendra "compliqué" au plus il sera difficile de vous aider... d'autant qu'il sera visible que vous ne vous êtes pas donné les moyens de vos ambitions.

    Citation Envoyé par makimax Voir le message
    Dans un example de code qui m'intéresse il utilise par exemple : self.address, ou sefl.port, là ou moi je met address
    self.port est un attribut d'instance, port est une variable,... c'est dans tous les tutos.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  11. #11
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par makimax Voir le message
    Si j'ai bien saisie vos remarques sur le scindement des fonctions, j'aurais donc une 1ère fonction connexion, une 2de pour la requête pour avoir la liste des compteurs et de leur paramètres et registres à aller chercher, puis une boucle pour chaque compteur : 3è fonction : la lecture en utilisant les paramètres et registres, 4è fonction : écrire dans la base de données fin de boucle
    Il s'agit de scinder les domaines, pas les fonctions. Les fonctions ne sont que des outils de travail.
    Si par exemple dans le domaine bdd il est nécessaire de séparer "connexion" de "requêtage" (parce que par exemple il y aura plusieurs requêtes mais une seule connexion) alors oui séparer les deux est utile. Sinon ça ne l'est pas.

    Citation Envoyé par makimax Voir le message
    Dans un example de code qui m'intéresse il utilise par exemple : self.address, ou sefl.port, là ou moi je met address
    Probablement que l'exemple en question est basé sur les objets et les classes...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  12. #12
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Il s'agit de scinder les domaines, pas les fonctions. Les fonctions ne sont que des outils de travail.
    Ce sont plutôt des abstractions que le programmeur crée pour effectuer son travail qui n'est pas d'écrire un programme (çà c'est le résultat) mais de programmer sa construction en identifiant chaque étape et les informations nécessaires en entrée et à récupérer en sortie (pour faire la suite).

    Et ce qui est important, c'est la cohérence dans l'enchainement des opérations.

    La grosse différence entre le débutant (qui débute) est qu'il se focalise sur les petits bouts pour essayer de les assembler ensuite. C'est une démarche du bas vers le haut, car on essaie de structurer un tas de boue comme on peut.

    A l'opposé, si on découpe chaque étape en fonction avec passage de paramètres et retour, on se focalise sur le flux d'informations qui doit être produit à chaque étape sans trop se préoccuper du détail (mais il faut avoir une idée de la difficulté du "détail" en question).

    Par exemple, je peux écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    db_cnx = db_open(...)
    dev_unit = 1
    records = db_query(db_cnx, dev_unit)
    Et réaliser ces fonctions lights:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def db_open():
          return None
    def db_query(cnx, dev_unit):
          return 1, 2, 3
    On a la structure et le flux d'informations et on peut produire des données récupérées dans "records" qui seront utilisables pour la suite.

    A défaut, on se retrouve avec des tas de lignes de code à raccorder les uns aux autres avec des jointures dégueulasses.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  13. #13
    Membre du Club
    Homme Profil pro
    Dessinateur industriel
    Inscrit en
    Février 2021
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Dessinateur industriel
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2021
    Messages : 90
    Points : 42
    Points
    42
    Par défaut
    J'ai avancé en suivant quelques tutos et exemples mais cela ne correspond pas totalement à ce que je souhaiterais.
    Dans le code suivant je me connecte à la base de données puis récupère les infos des devices Modbus, puis dans une boucle je récupère les valeurs des registres, les convertis en 32bit et le insert un par un dans la base de donnée et recommence en boucle a une fréquence définie

    J'aurai préféré que la communication / lecture des valeurs en modbus se fasse de facon régulière et assez rapidement, mais que l'envoi dans la base de données se fasse à une autre fréquence. L'idée est d'avoir en mémoire une valeur récente et de n'enregistrer celle-ci que toutes les X minutes.
    Dois-je sortir l'envoi en base de données de la boucle

    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
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    import time
    import pymysql.cursors
    from pymodbus.constants import Endian
    from pymodbus.payload import BinaryPayloadDecoder
    from threading import Thread, Lock
    from pyModbusTCP.client import ModbusClient
     
    # connect database
    sqlconnection = pymysql.connect(host='127.0.0.1',
                                 user='root',
                                 password='',
                                 database='database',
                                 charset='utf8mb4',
                                 cursorclass=pymysql.cursors.DictCursor)
     
    # read devices informations (ID, IP, Port, Register address)
    with sqlconnection:
        with sqlconnection.cursor() as cursor:
     
            sql = "SELECT `dev_id`, H0.hw_address, H0.hw_port FROM `devicestatus` LEFT JOIN `hardware` H0 ON H0.hw_id = devicestatus.dev_hw_id WHERE H0.hw_enabled = 1 AND devicestatus.dev_used = 1 ORDER BY H0.hw_address, dev_name"
            cursor.execute(sql,)
            result = cursor.fetchall()
            #print(result)
            #exemple : [{'dev_id': 7, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 8, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 9, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 10, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 11, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 12, 'hw_address': '192.168.100.202', 'hw_port': 502}]
     
     
    # set global variables
    regs = []
    sf_value = 0
     
     
    # init a thread lock
    regs_lock = Lock()
     
     
    # modbus polling thread
    def polling_thread():
        global regs
     
     
        # polling loop
        while True:
     
     
            REGISTER_ADDRESS = 98 #First device is at address %MW100, i dont' understand why i need to do -2
     
            # For each dictionnary of the list, "extract" each value of element : information useful to launch Modbus requests, one per device.
            for val in result:
     
                # set modbusTCP variables :
                DEV_ID = val['dev_id']
                SERVER_HOST = val['hw_address']
                SERVER_PORT = val['hw_port']
                SERVER_ID = 1
                REGISTER_ADDRESS = REGISTER_ADDRESS + 2 # to read %MD100 %MD102 %MD104 ...
                REGISTER_COUNT = 2
                print("Target :", SERVER_HOST, SERVER_PORT, REGISTER_ADDRESS)
     
                client = ModbusClient(host=SERVER_HOST, port=int(SERVER_PORT), unit_id=int(SERVER_ID), auto_open=True, auto_close=True, timeout=2)
     
                # keep TCP open
                if not client.is_open():
                    client.open()
                # do modbus reading on socket
                reg_read = client.read_holding_registers(REGISTER_ADDRESS, REGISTER_COUNT)
     
     
                # if read is ok, store result in regs (with thread lock synchronization)
                if reg_read:
                    # decode the 2 registers into a 32 bit integer
                    decoder = BinaryPayloadDecoder.fromRegisters(reg_read, byteorder=Endian.Big, wordorder=Endian.Little)
                    sf_value = decoder.decode_32bit_int()
     
                    with regs_lock:
                        regs = sf_value
                        print("dev_ID:", DEV_ID, "Valeur:", regs)
                        # To do : insert each data into sql table
     
                        # connect database
                        sqlconnection = pymysql.connect(host='127.0.0.1',
                                                     user='root',
                                                     password='',
                                                     database='database',
                                                     charset='utf8mb4',
                                                     cursorclass=pymysql.cursors.DictCursor)
                        with sqlconnection:
                            with sqlconnection.cursor() as cursor:
                                sql = "INSERT INTO `meter` (`meter_devlog_id`,`meter_value`,`meter_usage`) VALUES (%s, %s, %s)"
                                cursor.execute(sql, ('23', regs, 0))
                            sqlconnection.commit()
            # 1s before next polling
            time.sleep(5)
     
     
    # start polling thread
    tp = Thread(target=polling_thread)
    # set daemon: polling thread will exit if main thread exit
    tp.daemon = True
    tp.start()
     
    # display loop (in main thread)
    while True:
        # print regs (with thread lock synchronization)
        with regs_lock:
            #print(regs)
            print(" ici mettre l'envoi en base de données plutôt que dans la tâche de fond")
        # 1s before next print
        time.sleep(10)
    Merci de votre aide.

  14. #14
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Citation Envoyé par makimax Voir le message
    J'aurai préféré que la communication / lecture des valeurs en modbus se fasse de facon régulière et assez rapidement, mais que l'envoi dans la base de données se fasse à une autre fréquence. L'idée est d'avoir en mémoire une valeur récente et de n'enregistrer celle-ci que toutes les X minutes.
    Vous pourriez stockez vos données dans une liste/tableau plutôt que de les écrire immédiatement en base. Elles pourraient être enregistrées s'il y en a assez ou au bout d'un certain temps.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  15. #15
    Membre du Club
    Homme Profil pro
    Dessinateur industriel
    Inscrit en
    Février 2021
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Dessinateur industriel
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2021
    Messages : 90
    Points : 42
    Points
    42
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Vous pourriez stockez vos données dans une liste/tableau plutôt que de les écrire immédiatement en base. Elles pourraient être enregistrées s'il y en a assez ou au bout d'un certain temps.

    - W
    Merci j'ai tenté cette piste et cela fonctionne bien, la boucle lit les valeurs modbus en tâche de fond et met à jour un tableau lui même réinitialisé ensuite.
    A une fréquence défini j'envoi les valeurs du tableau qui sont bien les dernières valeurs modbus les plus à jour.

    Je n'ai pas créé de fonction mais est-ce normal de devoir remettre une connexion à la base de données, si je ne la mets pas j'ai une erreur qui dit qu'elle est fermée

    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
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    import time
    import pymysql.cursors
    from pymodbus.constants import Endian
    from pymodbus.payload import BinaryPayloadDecoder
    from threading import Thread, Lock
    from pyModbusTCP.client import ModbusClient
     
    # connect database
    sqlconnection = pymysql.connect(host='127.0.0.1',
                                 user='root',
                                 password='',
                                 database='database',
                                 charset='utf8mb4',
                                 cursorclass=pymysql.cursors.DictCursor)
     
    # read devices informations (ID, IP, Port, Register address)
    with sqlconnection:
        with sqlconnection.cursor() as cursor:
     
            sql = "SELECT `dev_id`, H0.hw_address, H0.hw_port FROM `devicestatus` LEFT JOIN `hardware` H0 ON H0.hw_id = devicestatus.dev_hw_id WHERE H0.hw_enabled = 1 AND devicestatus.dev_used = 1 ORDER BY H0.hw_address, dev_name"
            cursor.execute(sql,)
            result = cursor.fetchall()
            #print(result)
            #exemple : [{'dev_id': 7, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 8, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 9, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 10, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 11, 'hw_address': '192.168.100.202', 'hw_port': 502}, {'dev_id': 12, 'hw_address': '192.168.100.202', 'hw_port': 502}]
     
     
    # set global variables
    regs = []
    decoded_value = 0
     
     
    # init a thread lock
    regs_lock = Lock()
     
     
    # modbus polling thread
    def polling_thread():
        global regs
     
     
        # polling loop
        while True:
     
            # purger le dictionnaire :
            regs = []
     
            # définir le registre de départ :
            REGISTER_ADDRESS = 100 #Start register address
     
            # For each list of the dictionnary, "extract" each value of element : information useful to launch Modbus requests, one per device.
            for val in result:
     
                # purger la liste :
                resultat = {}
     
                # set modbusTCP variables :
                READ_DEV_ID = val['dev_id']
                SERVER_HOST = val['hw_address']
                SERVER_PORT = val['hw_port']
                SERVER_ID = 1
                REGISTER_COUNT = 2
                #print("Target :", SERVER_HOST, SERVER_PORT, REGISTER_ADDRESS)
     
                client = ModbusClient(host=SERVER_HOST, port=int(SERVER_PORT), unit_id=int(SERVER_ID), auto_open=True, auto_close=True, timeout=2)
     
                # keep TCP open
                if not client.is_open():
                    client.open()
                # do modbus reading on socket
                reg_read = client.read_holding_registers(REGISTER_ADDRESS, REGISTER_COUNT)
     
     
                # if read is ok, store result in regs (with thread lock synchronization)
                if reg_read:
                    # decode the 2 registers into a 32 bit integer
                    decoder = BinaryPayloadDecoder.fromRegisters(reg_read, byteorder=Endian.Big, wordorder=Endian.Little)
                    decoded_value = decoder.decode_32bit_int()
     
                    # On mets à jour la liste
                    resultat['dev_id'] = READ_DEV_ID
                    resultat['meter_value'] = decoded_value
     
                    with regs_lock:
                        # On ajoute la liste au dictionnaire
                        regs.append(resultat)
                        #print(regs)
     
                REGISTER_ADDRESS = REGISTER_ADDRESS + 2 # Increment registrer address for next read
     
            # 1s before next polling
            time.sleep(1)
     
     
    # start polling thread
    tp = Thread(target=polling_thread)
    # set daemon: polling thread will exit if main thread exit
    tp.daemon = True
    tp.start()
     
    # display loop (in main thread)
    while True:
        # print regs (with thread lock synchronization)
        with regs_lock:
            print("Données à envoyer en base de données : ")
            #print(regs)
     
            # For each list of the dictionnary, "extract" each value of element :
            for val2 in regs:
                print('dev_id', val2['dev_id'], ':', val2['meter_value'])
     
                # connect database
                sqlconnection = pymysql.connect(host='127.0.0.1',
                                             user='root',
                                             password='',
                                             database='database',
                                             charset='utf8mb4',
                                             cursorclass=pymysql.cursors.DictCursor)
                with sqlconnection:
                    with sqlconnection.cursor() as cursor:
                        sql = "INSERT INTO `meter` (`meter_devlog_id`,`meter_value`,`meter_usage`) VALUES (%s, %s, %s)"
                        cursor.execute(sql, (val2['dev_id'], val2['meter_value'], 0))
                    sqlconnection.commit()
     
            #print(" --- Données de la boucle en tâche de fond : ")
        # 1s before next print
        time.sleep(3)

  16. #16
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Citation Envoyé par makimax Voir le message
    Je n'ai pas créé de fonction mais est-ce normal de devoir remettre une connexion à la base de données, si je ne la mets pas j'ai une erreur qui dit qu'elle est fermée
    C'est le comportement normal du with... que l'on constate avec un simple fichier:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> f = open('test.dat')
    >>> f.closed
    False
    >>> with f:
    ...     pass
    ...
    >>> f.closed
    True
    >>>
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

Discussions similaires

  1. sauvegarde des données d'un formulaire dans mysql avec php
    Par MC BAO dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 28/07/2017, 16h31
  2. Réponses: 8
    Dernier message: 17/02/2016, 17h14
  3. Réponses: 2
    Dernier message: 23/04/2009, 11h16
  4. Enregistrement des données d'un formulaire dans SharePoint
    Par fanfan49 dans le forum SharePoint
    Réponses: 1
    Dernier message: 06/06/2007, 23h12
  5. Réponses: 2
    Dernier message: 14/05/2007, 09h40

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