[solved] Bidirektionale Kommunikation SOCKET/PYSERIAL

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
tao
User
Beiträge: 15
Registriert: Sonntag 12. November 2006, 21:34
Wohnort: basel
Kontaktdaten:

Hi Forum!

seit ein par tagen bin ich da an einem script welches mein bescheidenes latein ans ende bringt.
die aufgabe ist folgende:
ich habe einen flash xml-socket und will auf einen python socket-server verbinden welcher die gesendeten daten via pyserial an mein arduino(microcontrollerboard) schickt und wieder zurück. ich werte damit verschiedene sensoren aus und brauche auch die resultate.

nun habe ich mal einen server angelegt und mit threading zwei verschiedene threads gestartet, wobei der einte von flash daten liest und diese an mein arduino sendet und der andere wartet bis mein arduino sendet und leitet die daten an flash weiter.

das problem ist nun folgendes:
ein weg funktioniert problemlos! egal ob ich vom board sende oder oder von flash aus. solange nur ein thread läuft kommt alles dort an wo es soll. starte ich aber beide passiert nicht mehr als wenn ich einen starte.
ich denke mal ich habe da ein problem mit threading und finde keine lösung.

bin also dankbar um jede hilfe, hier mal mein code:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#---------------------------------------------------------
import serial
import socket
import threading
#---------------------------------------------------------
ser = serial.Serial('/dev/cu.usbserial-A50018g9', 9600, timeout=1)
#---------------------------------------------------------
HOST = ''
PORT = 2500
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
#---------------------------------------------------------
class arduino(threading.Thread):
    
    def __init__(self):
        print 'arduino is running'
        threading.Thread.__init__(self)
        self.cancel = threading.Event()
        
    def run(self):
        while True:
            data02 = ser.read(100)   
            if not data02:break
            if data02:
                print data02
                t.send(data02)
        s.close()
#---------------------------------------------------------
class processing(threading.Thread):
    
    def __init__(self):
        print 'processing is running'
        threading.Thread.__init__(self)
        self.cancel = threading.Event()
        
    def run(self):
        while True:
            data01 = t.recv(1024)
            if not data01: break
            if data01:
                print data01
                ser.write(data01)
        ser.close()
#---------------------------------------------------------
def main():
    ard = arduino()
    ard.start()
    pro = processing()
    pro.start()
#---------------------------------------------------------
if __name__ == "__main__":
    main()
#---------------------------------------------------------
Zuletzt geändert von tao am Mittwoch 3. Oktober 2007, 12:07, insgesamt 2-mal geändert.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

tao hat geschrieben:ich denke mal ich habe da ein problem mit threading und finde keine lösung.
Hallo tao!

Du hast sicher ein Problem mit Threading. Du darfst z.B. nicht, ohne Vorbereitungen, von mehreren Threads aus auf die selben Objekte schreibend zugreifen. Du kannst für den threadsicheren Datenaustausch eine Queue verwenden.

Du kannst nicht einfach von einem Thread aus auf die Serielle schreiben und von einem anderen Thread aus die gleiche Serielle zum Lesen verwenden. Ein Thread kümmert sich um die Serielle und ein anderer Thread kann sich um den Socket kümmern. Aber bitte nicht mischen.

Lasse einen Thread ständig an der Seriellen horchen, ob dort Daten anliegen. Wenn JA, dann kann dieser Thread die neuen Daten in eine Queue des anderen Threads legen. Der kann dann damit tun was er will.
Nach dem Lesen, also noch im selben Durchlauf prüft der Serielle-Thread ob etwas in der "serial_out"-Queue ist. Wenn JA, dann ab mit den Daten durch den seriellen Port. ;-)

Der Socket-Thread horcht ständig am Socket ob dort Daten anliegen. Wenn JA, dann werden diese Daten ausgelesen und in die "serial_out"-Queue geschrieben. Der Serial-Thread liest diese Queue immer wieder aus und wenn dort etwas liegt, dann werden vom Serial-Thread die Daten über die Serielle verschickt.
Nachdem die Daten vom Socket-Thread ausgelesen und evt. sogar an die "serial_out"-Queue abgegeben wurde, prüft der Socket-Thread ob Daten für diesen in der "tcp_out"-Queue liegen. Wenn ja, dann werden diese Daten vom Socket-Thread versendet.

Und so kommt sich kein Thread in die Quere.

Vorsicht! Ungetesteter Pseudocode!

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import serial
import socket
import threading
import Queue


HOST = ''
PORT = 2500


class Arduino(threading.Thread):
   
    def __init__(self, serial_out, tcp_out):
        print 'arduino is running'
        threading.Thread.__init__(self)
        
        self.serial_out = serial_out
        self.tcp_out = tcp_out
    
    
    def run(self):
        ser = serial.Serial('/dev/cu.usbserial-A50018g9', 9600, timeout=1)
        
        while True:
            # Lesen
            data = ser.read(100)   
            if not data: #??? Ich kann es nicht nachprüfen ob und wie das mit dem Timeout läuft
                break    #???
            print data
            self.tcp_out.put(data)
            # Schreiben
            try:
                data = self.serial_out.get_nowait()
                ser.write(data)
            except Queue.Empty:
                pass
        ser.close()


class Processing(threading.Thread, serial_out, tcp_out):
   
    def __init__(self):
        print 'processing is running'
        threading.Thread.__init__(self)
        
        self.serial_out = serial_out
        self.tcp_out = tcp_out
    
    
    def run(self):
        
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind((HOST, PORT))
        sock.listen(1)
        conn, addr = sock.accept()
        
        while True:
            # Lesen

            # Hier muss ein Timeout her.
            # Also entweder mit ``socket.setdefaulttimeout()`` arbeiten
            # oder mit dem Modul ``select`` arbeiten.
            data = conn.recv(1024)

            if not data: 
                break
            print data
            self.serial_out.put(data)
            # Schreiben
            try:
                data = self.tcp_out.get_nowait()
                conn.send(data)
            except Queue.Empty:
                pass
        sock.close()


def main():
    
    serial_out = Queue.Queue()
    tcp_out = Queue.Queue()
    
    ard = Arduino(serial_out, tcp_out)
    pro = Processing(serial_out, tcp_out)
    
    # Starten und ewig warten
    ard.start()
    pro.start()
    ard.join()
    pro.join()


if __name__ == "__main__":
    main()
Vielleicht kannst du damit etwas anfangen.

Ich glaube, dass du aber auch auf Threads verzichten könntest. Du könntest in einer Schleife zuerst das Socket nach Daten abfragen, diese an die Serielle schicken und dann die Serielle nach Daten abfragen und diese an das Socket schicken und zwischendrinn mal kurz Pause machen und wieder von vorne beginnen. <verschnauf> :-)

Ich möchte aber auch verlautbaren, dass ich nicht weiß was ein "flash xml-socket" ist... Vielleicht gibt es viel interessantere Lösungen für dein Problem.

Und ob man mit einem Socket nicht doch vielleicht gleichzeitig lesen und schreiben kann, weiß ich auch nicht. Damit meine ich, ob sich Python vielleicht selber darum kümmert, dass sich die Daten nicht in die Quere kommen... also threadsafe ist.

Wie auch immer. Ohne eine Umgebung zum Testen, kann ich nicht mehr dazu sagen.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

gerold hat geschrieben:Und ob man mit einem Socket nicht doch vielleicht gleichzeitig lesen und schreiben kann, weiß ich auch nicht. Damit meine ich, ob sich Python vielleicht selber darum kümmert, dass sich die Daten nicht in die Quere kommen... also threadsafe ist.
Doch, kann man. Die beiden "Richtungen" bei Sockets sind voellig unabhaengig voneinander. (Was nicht unbedingt Pythons Verdienst ist).
tao
User
Beiträge: 15
Registriert: Sonntag 12. November 2006, 21:34
Wohnort: basel
Kontaktdaten:

danke für die tolle antwort gerold! :D

ich musst nur ein zwei leichte korrekturen vornehmen und jetzt macht das ding genau das was ich von ihm erwarte.
mit dem timeout bin ich nicht ganz klargekommen, aber alles funktioniert tadellos auch ohne.

hier mal noch das bisschen code mit leichten änderungen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import serial
import socket
import threading
import Queue

HOST = ''
PORT = 2500

class Arduino(threading.Thread):
   
    def __init__(self, serial_out, tcp_out):
        print 'arduino is running'
        threading.Thread.__init__(self)
       
        self.serial_out = serial_out
        self.tcp_out = tcp_out
   
   
    def run(self):
        ser = serial.Serial('/dev/cu.usbserial-A50018g9', 9600, timeout=1)
       
        while True:
            # Lesen
            data = ser.read(100)

            if data:
                print data
                self.tcp_out.put(data)
            #Schreiben
            try:
                data01 = self.serial_out.get()
                ser.write(data01)
            except Queue.Empty:
                pass
        ser.close()


class Processing(threading.Thread):
   
    def __init__(self, serial_out, tcp_out):
        print 'processing is running'
        threading.Thread.__init__(self)
       
        self.serial_out = serial_out
        self.tcp_out = tcp_out
   
   
    def run(self):
       
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind((HOST, PORT))
        sock.listen(1)
        conn, addr = sock.accept()
       
        while True:
            # Lesen
            #socket.setdefaulttimeout(1)    #bin nicht sicher ob man den braucht
            data = conn.recv(1024)

            if data:
                print data
                self.serial_out.put(data)

             # Schreiben
            try:
                data01 = self.tcp_out.get()
                conn.send(data01)
            except Queue.Empty:
                pass
            
        sock.close()


def main():
    serial_out = Queue.Queue()
    tcp_out = Queue.Queue()
    
    ard = Arduino(serial_out, tcp_out)
    pro = Processing(serial_out, tcp_out)
   
    # Starten und ewig warten
    pro.start()
    ard.start()
    pro.join()
    ard.join()


if __name__ == "__main__":
    main()
nochmals danke für die flotte hilfe!!!
grüsse
tao
tao
User
Beiträge: 15
Registriert: Sonntag 12. November 2006, 21:34
Wohnort: basel
Kontaktdaten:

So nun hatte ich zeit mir alles nochmals genau anzuschauen,
so weit war das nicht schlecht oben, aber man musste noch vom
client her pollen um die antwort ausgelesen zu bekommen.
das habe ich noch geändert und nun ist es wirklich ein PROXY mit einer kleinen config datei.

PROXYCODE:

Code: Alles auswählen

#! /usr/bin/env python
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------        
import serial
import select
import socket
import threading
import Queue
#-------------------------------------------------------------------------------        
import config as cfg
#-------------------------------------------------------------------------------
HOST = cfg.HOST
PORT = cfg.PORT
SERIAL = cfg.SERIAL
BAUD = cfg.BAUD
TIMEOUT = cfg.TIMEOUT
BUFFER = cfg.BUFFER
BYTESIZE =cfg.BYTSIZE
STOPBIT = cfg.STOPBIT
#-------------------------------------------------------------------------------        
class Arduino(threading.Thread):
    
    def __init__(self, serial_out, tcp_out):
        print '\n\n--------------------------\nSERIALPROXY BY A.TELLINI\n--------------------------\n\narduino is running'
        threading.Thread.__init__(self)
       
        self.serial_out = serial_out
        self.tcp_out = tcp_out
   
    def run(self):
        ser = serial.Serial(SERIAL, BAUD, timeout=TIMEOUT, bytesize= BYTESIZE,stopbits = STOPBIT)
        print 'Serialdevice name: ',ser.portstr
        ser.flushInput()
        ser.flushOutput()
       
        while True:
            # Lesen
            dataA = ser.readline()
            if dataA:
                self.tcp_out.put_nowait(dataA)
            #Schreiben
            try:
                data01 = self.serial_out.get_nowait()
                ser.write(data01)
            except Queue.Empty:
                pass
            
        ser.close()
#-------------------------------------------------------------------------------        
class Processing(threading.Thread):
    #
    def __init__(self, serial_out, tcp_out):
        print 'processing is running'
        threading.Thread.__init__(self)
       #
        self.serial_out = serial_out
        self.tcp_out = tcp_out
       #
    def run(self):
         #
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind((HOST, PORT))
        sock.listen(1)
        conn, addr = sock.accept()
        read_set = [conn]
        print 'you connect from: ', conn.getpeername()
        #
        while True:
            
            r,w,e = select.select(read_set,read_set,read_set,0.1)
            
            # Schreiben
            try:
                if w:
                    data02 = self.tcp_out.get_nowait()
                    if data02:
                        convert = str(data02)
                        conn.send(convert)
            except Queue.Empty:
                pass
            #Lesen
            if e:
            	break
            if r:
                dataP = conn.recv(BUFFER)
                if dataP:
                    self.serial_out.put_nowait(dataP)
              
        sock.close()
#-------------------------------------------------------------------------------        
def main():
    serial_out = Queue.Queue()
    tcp_out = Queue.Queue()
    
    ard = Arduino(serial_out, tcp_out)
    pro = Processing(serial_out, tcp_out)
   
    # Starten und ewig warten
    pro.start()
    ard.start()
    pro.join()
    ard.join()
#-------------------------------------------------------------------------------        
if __name__ == "__main__":
    main()
#-------------------------------------------------------------------------------        
CONFIG.PY:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#IF YOU USING FLASH, SET TO TRUE
NILS = False

#SERVER CONFIG
HOST = '127.0.0.1'
PORT = 2500
BUFFER = 1024

#SERIAL CONFIG
SERIAL = '/dev/cu.usbserial-A50018g9'
BAUD = 9600
TIMEOUT = 1
BYTSIZE = 8
STOPBIT = 1
#etc
um aus dem queue auszulesen ist unbedingt 'get_nowait' und um hineinzuschreiben auch 'put_nowait' zu verwenden, da das ganze sonst
blockiert.

grüsse
tao
Antworten