Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

interruption port série

8 réponses
Avatar
val
Bonjour,

Est-il possible qu'une réception sur le port série génère une
interruption dans un script python sous windows 10 ? j'utilise python
2.7.15 et bottle pour l'interface utilisateur et j'aimerais qu'une lecture
du port série ne soit pas bloquante, une sorte de fonction
onSerialEvent()...

Merci pour toute suggestion !

8 réponses

Avatar
Nicolas
Le 27/01/2019 à 13:32, val a écrit :
Bonjour,
Est-il possible qu'une réception sur le port série génère une
interruption dans un script python sous windows 10 ? j'utilise python
2.7.15 et bottle pour l'interface utilisateur et j'aimerais qu'une lecture
du port série ne soit pas bloquante, une sorte de fonction
onSerialEvent()...
Merci pour toute suggestion !

Une interruption et un serveur WEB ne sont pas vraiment du même monde.
Quel est le cas d'usage ?
En fonction de ce que vous voulez faire, les solutions ne seront pas les
mêmes.
Avatar
val
Le 28/01/2019 à 08:58, Nicolas a écrit :
Une interruption et un serveur WEB ne sont pas vraiment du même monde.
Quel est le cas d'usage ?
En fonction de ce que vous voulez faire, les solutions ne seront pas les
mêmes.

C'est pour un banc de contrôle. Les instruments ramènent des infos par
les ports séries. L'utilisateur peut aussi scanner des données avec un
lecteur de code barres. L'interface utilisateur permet de choisir des
options de réglages ou entrer des paramètres de calibration. S'il était
possible que le port série génère une interruption cela aurait
l'avantage de simplifier l'interface utilisateur. Par exemple si
l'utilisateur scanne un document le programme pourrait l'orienter
directement en fonction du document scanné.
Avatar
Nicolas
Le 28/01/2019 à 10:33, val a écrit :
Le 28/01/2019 à 08:58, Nicolas a écrit :
Une interruption et un serveur WEB ne sont pas vraiment du même monde.
Quel est le cas d'usage ?
En fonction de ce que vous voulez faire, les solutions ne seront pas les
mêmes.

C'est pour un banc de contrôle. Les instruments ramènent des infos par
les ports séries. L'utilisateur peut aussi scanner des données avec un
lecteur de code barres. L'interface utilisateur permet de choisir des
options de réglages ou entrer des paramètres de calibration. S'il était
possible que le port série génère une interruption cela aurait
l'avantage de simplifier l'interface utilisateur. Par exemple si
l'utilisateur scanne un document le programme pourrait l'orienter
directement en fonction du document scanné.

<petite digression>
Perso, je fais des bancs de contrôle avec des applications desktop
(basées sur wxPython), pas avec des technos WEB. Les technos WEB sont
très à la mode mais, à mon avis, pas du tout adaptées à ce genre
d'application.
</petite digression>
Je ne connais pas bottle, je ne peux donc pas dire ce qu'il est possible
de faire avec.
Ce que je ferais, c'est un thread séparé qui gère le port série.
Lorsqu'un évènement utile arrive, un message est envoyé au serveur WEB.
Pas d'interruption, mais c'est un peu tout comme. Mais le serveur en
fera quoi de ce message ? Le client fait une demande de mise à jour
régulièrement ?
Pour avoir une mise à jour "instantanée", il faut un serveur WebSocket
en plus.
Nicolas
Avatar
val
Le 28/01/2019 à 16:14, Nicolas a écrit :
<petite digression>
Perso, je fais des bancs de contrôle avec des applications desktop
(basées sur wxPython), pas avec des technos WEB. Les technos WEB sont
très à la mode mais, à mon avis, pas du tout adaptées à ce genre
d'application.
</petite digression>

Ah, ok. Ça m'avait l'air d'une bonne idée cette architecture
client/serveur pour faciliter l'écriture du programme.
Je ne connais pas bottle, je ne peux donc pas dire ce qu'il est possible
de faire avec.
Ce que je ferais, c'est un thread séparé qui gère le port série.
Lorsqu'un évènement utile arrive, un message est envoyé au serveur WEB.
Pas d'interruption, mais c'est un peu tout comme. Mais le serveur en
fera quoi de ce message ? Le client fait une demande de mise à jour
régulièrement ?
Pour avoir une mise à jour "instantanée", il faut un serveur WebSocket
en plus.
Nicolas

Merci pour le conseil de faire un thread, je ne sais pas si c'est comme
cela qu'il faut faire mais ça a l'air de marcher et je peux récupérer
les données du port série sans que cela soit bloquant pour l'application
principale. Maintenant je vais voir si je peux adapter à Bottle pour la
GUI.
***********************************************
# -*- coding: iso-8859-1 -*-
#
# essai écoute port com4
import time, os, serial
from threading import Thread
def listenCOM4(i):
# écoute de COM4 9600N81
global dta, k
ser = serial.Serial("COM4", timeout=None)
while ser.inWaiting()==0 :
time.sleep(0.1)
time.sleep(0.1) # pour une réception de qlq dizaines de char max
k=i
dta=ser.read(ser.inWaiting())
ser.close()
# on relance le thread après la lecture
t = Thread(target=listenCOM4, args=(i+1,))
t.start()
#démarre thread une première fois...
t = Thread(target=listenCOM4, args=(1,))
t.start()
# ... et passe à autre chose
dta=""; k=0
for j in range(10):
print "temps : ", j, " reception ", k, " chaine : ", dta
time.sleep(1)
os._exit(0)
*********************************************
ce qui donne :
C:...>testThreadSerial2.py
temps : 0 reception 0 chaine :
temps : 1 reception 0 chaine :
temps : 2 reception 0 chaine :
temps : 3 reception 1 chaine : 456789
temps : 4 reception 1 chaine : 456789
temps : 5 reception 1 chaine : 456789
temps : 6 reception 1 chaine : 456789
temps : 7 reception 2 chaine : 123
temps : 8 reception 2 chaine : 123
temps : 9 reception 2 chaine : 123
Avatar
Nicolas
Le 29/01/2019 à 17:23, val a écrit :
Merci pour le conseil de faire un thread, je ne sais pas si c'est comme
cela qu'il faut faire mais ça a l'air de marcher et je peux récupérer
les données du port série sans que cela soit bloquant pour l'application
principale. Maintenant je vais voir si je peux adapter à Bottle pour la
GUI.
***********************************************
# -*- coding: iso-8859-1 -*-
#
# essai écoute port com4
import time, os, serial
from threading import Thread
def listenCOM4(i):
# écoute de COM4 9600N81
global dta, k
ser = serial.Serial("COM4", timeout=None)
while ser.inWaiting()==0 :
time.sleep(0.1)
time.sleep(0.1) # pour une réception de qlq dizaines de char max
k=i
dta=ser.read(ser.inWaiting())
ser.close()
# on relance le thread après la lecture
t = Thread(target=listenCOM4, args=(i+1,))
t.start()
#démarre thread une première fois...
t = Thread(target=listenCOM4, args=(1,))
t.start()
# ... et passe à autre chose
dta=""; k=0
for j in range(10):
print "temps : ", j, " reception ", k, " chaine : ", dta
time.sleep(1)
os._exit(0)
*********************************************
ce qui donne :
C:...>testThreadSerial2.py
temps : 0 reception 0 chaine :
temps : 1 reception 0 chaine :
temps : 2 reception 0 chaine :
temps : 3 reception 1 chaine : 456789
temps : 4 reception 1 chaine : 456789
temps : 5 reception 1 chaine : 456789
temps : 6 reception 1 chaine : 456789
temps : 7 reception 2 chaine : 123
temps : 8 reception 2 chaine : 123
temps : 9 reception 2 chaine : 123

Il y a plusieurs erreurs de conception :
1- Le thread doit être lancé une fois et tourner indéfiniment jusqu'à
sortie du programme (ou pour une raison bien précise).
2- La façon de lire des données du port série n'est pas optimale
3- Telle que la communication entre thread est faite, il y aura des
pertes de données, voire des incohérences dans l'affichage (il manque
une synchronisation et une protection inter-threads)
4 utilisation de variables globales... Brrr quelle horreur !
Concernant le point 1 : lancer un thread est "couteux" en temps. De la
même façon, ouvrir le port série est "couteux". Pire, entre la fermeture
et la réouverture du port série, des données peuvent être (seront) perdues.
Le port série doit être ouvert une fois et la lecture du port se faire
indéfiniment.
Concernant le point 2 : Ouvrir le port série avec un timeout et lire une
quantité de données précise est une solution beaucoup plus efficace. Et
plus compréhensible également.
Voici un code (non testé) qui reprend les points ci-dessus :
from threading import *
import serial
import time
class ThreadCom(Thread):
def __init__(self, com_port):
Thread.__init__(self)
self.com_port = com_port
self.lock = Lock()
self.rx_data = b""
#self.start() # Pour auto-run
def run(self) :
ser = serial.Serial(self.com_port, timeout=0.1)
while 1 :
rx_data = ser.read(1024)
self.lock.acquire()
self.rx_data += rx_data
self.lock.release()
def get_data(self) :
self.lock.acquire()
data = self.rx_data
self.rx_data = b""
self.lock.release()
return data
com_thread = ThreadCom("COM4")
com_thread.start()
for j in range(10):
print "temps : ", j, " reception chaine : ", com_thread.get_data()
time.sleep(1)
Ici, c'est le thread principal qui vient chercher les données reçues par
le thread COM.
Il est également possible de prévenir le thread principal que des
données sont prêtes.
Nicolas
Avatar
val
Le 30/01/2019 à 09:15, Nicolas a écrit :
Ici, c'est le thread principal qui vient chercher les données reçues par
le thread COM.

Je viens de tester et ça marche. Merci beaucoup d'avoir revisité mon
bricolage !
Il est également possible de prévenir le thread principal que des
données sont prêtes.

Vous pourriez me dire comment ? Cela m'intéresse car ça permettrait de
déclencher des actions quand certains équipements envoient des données
de tests.
Avatar
Nicolas
Le 30/01/2019 à 15:18, val a écrit :
Le 30/01/2019 à 09:15, Nicolas a écrit :
Ici, c'est le thread principal qui vient chercher les données reçues par
le thread COM.

Je viens de tester et ça marche. Merci beaucoup d'avoir revisité mon
bricolage !
Il est également possible de prévenir le thread principal que des
données sont prêtes.

Vous pourriez me dire comment ? Cela m'intéresse car ça permettrait de
déclencher des actions quand certains équipements envoient des données
de tests.

Tout dépend de ce que vous voulez faire, comment vous voulez faire et
avec quoi vous voulez le faire.
Quelles actions sont déclenchées ?
A quel niveau ces actions sont t'elles déclenchées ?
- Dans un thread de management général ?
- Dans l'interface graphique ?
- Autre ?
Avatar
Nicolas
Le 31/01/2019 à 14:21, val a écrit :
Le 31/01/2019 à 08:43, Nicolas a écrit :
Tout dépend de ce que vous voulez faire, comment vous voulez faire et
avec quoi vous voulez le faire.
Quelles actions sont déclenchées ?
A quel niveau ces actions sont t'elles déclenchées ?
- Dans un thread de management général ?
- Dans l'interface graphique ?
- Autre ?

J'imagine une interface graphique où l'utilisateur aurait des choix et où
les résultats (venant des ports série) pourraient aussi s'afficher.
Par exemple l'utilisateur démarre le programme (ce dernier va d'ores et
déjà écouter le/les ports série).
Etape 1.L'utilisateur scanne un code-barre sur un OF (Ordre de Fabrication,
feuille qui accompagne les produits tout au long de la production) -> le
programme récupère le code-barre et l'analyse, ce qui va lui donner le
type de produit à tester, leur nombre. A partir du type de produit le
programme récupère le type de test à faire dans une base de données,
par exemple, tester chaque produit à tel courant, c'est l'étape 2 (les
produits sont des instruments de mesure du courant). Là aussi on utilise
le lecteur de code-barre pour scanner le numéro de série du produit. Une
fois scanné le numéro de série le programme envoie les données de tests
au 2ème port série pour programmer l'instrument de test et attendre le
résultat que l'on va archiver dans une BDD pour éditer des certificats
plus tard. Puis l'opérateur change de produit à tester et scanne le
nouveau numéro de série, et on boucle au début de l'étape 2 jusqu'à ce
que tous les produits de cet OF soient testés.
Jusque là c'est très linéaire mais j'aimerais que l'utilisateur ait
accès à d'autres opérations, par exemple en fin d'étape 1 il pourrait
demander (étape 3) à changer légèrement les valeurs de tests pour que
le courant de test soit mieux ajusté. Pour cela il doit juste cliquer sur
un bouton de l'interface graphique, laquelle va renvoyer une autre
interface graphique avec des choix d'ajustement de l'instrument de test.
Ensuite on revient à la première interface graphique, à l'étape 2.
Je ne sais pas si c'est bien compréhensible mais voilà, ce que j'aimerais
c'est un processus fluide où l'utilisateur en général n'aurait même pas
besoin d'utiliser le clavier pour un contrôle "normal" étape 1 + n étape
2.
Je pense utiliser "Bottle" car ça a l'air suffisamment simple de
programmer des interfaces graphiques pour quelqu'un comme moi avec des
connaissances sommaires en programmation. Il faut que je regarde la doc de
plus près car j'ai d'un côté des actions déclenchées à partir de
l'interface graphique (étape 3 par ex.), ce qui semble simple à gérer,
mais aussi des actions déclenchées par des réceptions sur les ports
séries, et il va falloir que je comprenne comment articuler cela.

De mon point de vue, une application basée sur des technos WEB n'est
utile que dans le cas d'un écran/clavier déporté.
Il existe des GUI framework (wxPython, PyQt, PySide...) pour réaliser
des applications desktop très faciles à mettre en oeuvre. Il est même
relativement facile de générer un installateur pour l'application que
l'on a conçue. Le déploiement est ainsi très simple.
Pour une application WEB... il faut demander à un spécialiste du domaine.