MyModBus beta et chaudiere Okofen : probleme d'ecriture

Donc slave est ignoré, donc ce n’est pas une passerelle

Oui, « parfois », une des ecritures double revient en echec, avec des messages qui laissent a penser qu’en face ca a raccroché… Sauf que parfois, c’est la premiere ecriture qui foire, la deucieme passe, alors que je ne réouvre pas de connection entre les 2 essais…

>>> client.connect()
True
>>> client.write_register(address=54, value=registers[0], slave=8)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/var/www/html/plugins/mymodbus/ressources/_pyenv/versions/3.9.16/lib/python3.9/site-packages/pymodbus/client/mixin.py", line 147, in write_register
    return self.execute(
  File "/var/www/html/plugins/mymodbus/ressources/_pyenv/versions/3.9.16/lib/python3.9/site-packages/pymodbus/client/base.py", line 198, in execute
    return self.transaction.execute(request)
  File "/var/www/html/plugins/mymodbus/ressources/_pyenv/versions/3.9.16/lib/python3.9/site-packages/pymodbus/transaction.py", line 170, in execute
    response, last_exception = self._transact(
  File "/var/www/html/plugins/mymodbus/ressources/_pyenv/versions/3.9.16/lib/python3.9/site-packages/pymodbus/transaction.py", line 317, in _transact
    result = self._recv(response_length, full)
  File "/var/www/html/plugins/mymodbus/ressources/_pyenv/versions/3.9.16/lib/python3.9/site-packages/pymodbus/transaction.py", line 352, in _recv
    read_min = self.client.framer.recvPacket(min_size)
  File "/var/www/html/plugins/mymodbus/ressources/_pyenv/versions/3.9.16/lib/python3.9/site-packages/pymodbus/framer/__init__.py", line 60, in recvPacket
    return self.client.recv(size)
  File "/var/www/html/plugins/mymodbus/ressources/_pyenv/versions/3.9.16/lib/python3.9/site-packages/pymodbus/client/tcp.py", line 295, in recv
    return self._handle_abrupt_socket_close(
  File "/var/www/html/plugins/mymodbus/ressources/_pyenv/versions/3.9.16/lib/python3.9/site-packages/pymodbus/client/tcp.py", line 344, in _handle_abrupt_socket_close
    raise ConnectionException(msg)
pymodbus.exceptions.ConnectionException: Modbus Error: [Connection] ModbusTcpClient(192.168.0.230:502): Connection unexpectedly closed 0.00012493133544921875 seconds into read of 8 bytes without response from slave before it closed connection
>>> client.write_register(address=54, value=registers[0], slave=8)
<pymodbus.register_write_message.WriteSingleRegisterResponse object at 0xffffa28cfd90>
>>> client.close()

Et il est fait mention d’un slave…

Je pense que quand j’aurai un peu de temps, je vais réessayer des captures de paquets, pour voir, en faisant les tests manuellement… Ca ne donnera peut etre rien, mais j’ai l’impression qu’on a pas une vision complète des choses si on ne regarde pas ça aussi…

Si juste ça ne fonctionnait pas du tout en ecriture, je me dirais c’est la chaudiere, basta… Mais ça marche avec certains outils et pas avec d’autres, et ça me chargine fortement de ne pas comprendre pourquoi…

Oui, c’est la chaudière qui est considérée comme un esclave Modbus.
C’est un vocabulaire particulier en Modbus, parce q’au début ce n’était que du bus série RS485 avec un maître qui peut interroger les esclaves. Les esclave n’envoient de données qu’en réponse au maître.

Sur un réseau on parle plutôt de serveur et de client. Le maître est le client et les esclaves sont les serveurs.

Le vocabulaire maître / esclave est resté pour Modbus… même en réseau TCP/IP

OK…

Bon, je vais en rester là pour ce soir. En tout cas, merci pour ton aide. J’aurai aimé pouvoir t’aider à trouver la solution à ce problème spécifique Okofen apparemment.
Ca arrivera peut etre, mais ça sera pour un autre jour :wink:

Hello,
J’ai pensé à un truc cette nuit (des fois ça sert, l’insomnie !):
Puisque avec un envoi ca ne marche pas, mais qu’avec 2 envois ça marche, finalement, quel est l’envoi qui est pris en compte?

Donc j’ai essayé ça :

builder = BinaryPayloadBuilder(byteorder='>', wordorder='>')
builder.add_16bit_int(110)
registers = builder.to_registers()
builder2 = BinaryPayloadBuilder(byteorder='>', wordorder='>')
builder2.add_16bit_int(120)
registers2 = builder2.to_registers()

client.connect()
client.write_register(address=54, value=registers[0], slave=0)
client.write_register(address=54, value=registers2[0], slave=0)
client.close()

Et ensuite, je lis la valeur pour voir laquelle a été enregistrée.
Systématiquement, c’est la valeur du premier envoi en écriture qui est renvoyée, (en l’occurrence, 110).

Là, je vais faire des suppositions, j’extrapole complétement, et pour mémoire, je suis novice en modbus et en python, et je ne connais pas les arcanes de modbus/TCP.

Mais:
-Ca se passe comme si la première écriture avait besoin d’être « validée » d’une certaine manière, comme si la première transaction était incomplète.
-La deuxième écriture, en en initiant une nouvelle, « clôturerait » la première écriture. Mais cette 2eme valeur ne serait pas prise en compte car cette 2eme transaction resterait aussi « inachevée ».
-La clôture de la connexion (soit de manière explicite, soit par la déconnection automatique de la chaudière qui intervient au bout de quelques secondes d’inactivité) efface les écritures « inachevées »
-Si je compare avec les logs qu’on a étudié, les essais fructueux « en une fois » avec modbus doctor ne comportaient pas de numéro de transaction (0), mais ceux du plugin en ont un.
-Peut être que quand il y a un numéro de transaction, le fait de voir arriver une transaction avec un nouveau id valide la précédente. Et si id=0, alors validation d’emblée ?

Du coup, je vais au bout de l’idée, et j’envoie 3 écritures d’affilée avec 3 valeurs différentes, et ce coup ci, c’est la deuxième qui est prise en compte.
Avec 4 écritures, c’est la 3eme, etc… C’est toujours l’avant-dernière qui est validée… Ce qui empiriquement me fait penser que je suis peut être dans la bonne direction.

OK…

Avec 3 ou 4 requêtes d’écriture, tu as fait plusieurs essais ?
Et si c’est une requête d’écriture suivi d’une lecture ?

J’ai fait plusieurs essais avec chaque panachage (2, 3 et 4 requêtes), avec des registres différents a chaque fois pour savoir quelle est le rang de la valeur qui reste écrite.

Si je fais une écriture et une lecture juste après, effectivement, ça valide l’écriture !
Mais à la condition express que ce soit avec le même client, sans déconnecter entre deux.

Avec registers à la valeur 110 et registers2 à la valeur 120, et en partant d’une valeur dans le registre de la chaudiere à 500:

client.connect()
client.write_register(address=54, value=registers[0], slave=0)
reponse = client.read_holding_registers(address=54, count=1, slave=2)
decoder = BinaryPayloadDecoder.fromRegisters(reponse.registers, '>', '>')
decoder.decode_16bit_int()
client.close()

Ceci renvoie bien 110


client.connect()
client.write_register(address=54, value=registers[0], slave=0)
client.close()
client.connect()
reponse = client.read_holding_registers(address=54, count=1, slave=2)
decoder = BinaryPayloadDecoder.fromRegisters(reponse.registers, '>', '>')
decoder.decode_16bit_int()
client.close()

Ceci renvoie une erreur si je l’execute « en une fois » (copier/coller de toutes les lignes dans mon terminal. je pense que la fermeture réouverture du client est trop rapide)
Si je l’execute en deux morceaux (ecriture puis lecture) avec une très courte pause entre les deux, pas de messages d’erreur, mais la valeur retournée n’est pas celle que j’essaye d’ecrire.

Donc il y a une notion de : il faut une transaction modbus après la transaction d’ecriture pour que la valeur soit bien ecrite, au cours de la meme connection, sinon la valeur est ignorée

Donc avec le plugin, il te suffit de te mettre en mode cyclique (dans le config de l’équipement) pour faire l’essai. Dans ce mode, toutes les commandes info sont exécutées les unes à la suite des autres en reprenant depuis le début sans pause. Les écritures sont faites « au milieu » ou en fin de cycle (bas du tableau des commandes), quand le plugin « voit » qu’une commande action a été lancée. Si tu coches la case « Garder la connexion ouverte », le démon ne fermera pas la connexion en fin de cycle.

→ ça vaut le coup d’essayer.

Tu peux fermer la connexion directement après la lecture et utiliser le decoder ensuite. Les données sont dans le buffet (reponse.registers).

C’était si simple !
J’ai mis en cyclique, et a priori ça fonctionne tout à fait bien.

Un grand merci pour ton temps et ton aide avec mon problème.
C’était de plus un processus très intéressant pour moi !

1 « J'aime »

Ce sujet a été automatiquement fermé après 24 heures suivant le dernier commentaire. Aucune réponse n’est permise dorénavant.