Auf empfangene Daten synchronisieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
whaeva
User
Beiträge: 66
Registriert: Mittwoch 25. Februar 2009, 15:30

Ich empfange Daten mit pySerial von einem Gerät, das ständig einen doppelten Datensatz mit konstanter Länge sendet. Ca. so:
DATA1\nDATA1\n
pause
DATA2\nDATA2\n

DATA1 kann gleich DATA2 sein
pause ist unterschiedlich lang

Auslesen der Schnittstelle mit readline() und Timeout bringt also max. die Hälfte eines Datensatzes.

Vielleicht sollte man ohne timeout zwei Datensätze mit read(n) lesen, aber das funktioniert nur, wenn man zufällig in pause den Eingangspuffer geleert hat. Und es könnten genausogut zwei halbe Datensatze gelesen werden.

Der Vergleich mit einem zweiten Lesevorgang bringt nichts, da das genausogut auch ein anderer Datensatz sein könnte (es sei denn, das timeout ist kürzer als pause).

Ha, das probiere ich mal - fallen Euch noch schönere Lösungen ein?
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Kannst du den Input vor dem Empfangen manipulieren (d.h. am Absender o.ä.)? Dann könntest du eine Art Hier-Ist-Ende-Signal anbringen.
whaeva
User
Beiträge: 66
Registriert: Mittwoch 25. Februar 2009, 15:30

Nee, am Format kann ich nix ändern. Auch das serielle Format ist lustig: 7 Datenbits, ungerade Parität (7O1) - pySerial und mein USB-RS232 Adapter mit FT232BM bekommen da regelmäßig Probleme:

Code: Alles auswählen

ftdi_sio: ftdi_set_termios FAILED to set databits/stopbits/parity
ftdi_sio: ftdi_set_termios urb failed to set baudrate
ftdi_sio: urb failed to clear flow control
ftdi_sio: update_mctrl Error from MODEM_CTRL urb: DTR HIGH, RTS HIGH
Aber es funktioniert mit dem Vergleich von zwei readline() mit kleinem Timeout; das ist nur True, wenn zwei identische Zeilen unmittelbar hintereinander gelesen werden.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

whaeva hat geschrieben:pause ist unterschiedlich lang
Hallo whaeva!

Vielleicht kannst du die Länge der Pause als Trenner verwenden. Wenn du z.B. von einer Mindestpausenlänge von einer halben Sekunde ausgehen kannst, dann könnte das Programm vielleicht wie folgt aussehen. ACHTUNG! Ungetestet --> soll nur als Denkanstoß dienen.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import serial
import threading
import Queue
import datetime
import time

#DATA1\nDATA1\n
#pause
#DATA2\nDATA2\n 

# Connection-Parameter
PORT = 0
BAUDRATE = 9600
BYTESIZE = serial.SEVENBITS
PARITY = serial.PARITY_ODD
STOPBITS = serial.STOPBITS_ONE
TIMEOUT = 1 # sec.

# Pause
MIN_PAUSE = datetime.timedelta(milliseconds = 500)


class MySerialConnection(threading.Thread):
    
    def __init__(self):
        threading.Thread.__init__(self)
        self.cancel = threading.Event()
        self.queue = Queue.Queue()

    
    def run(self):
        while True:
            if self.cancel.isSet():
                break
            ser = serial.Serial(
                port = PORT, baudrate = BAUDRATE, bytesize = BYTESIZE,
                parity = PARITY, stopbits = STOPBITS, timeout = TIMEOUT
            )
            try:
                while True:
                    if self.cancel.isSet():
                        break
                    if not ser.isOpen():
                        break
                    line = ser.readline()
                    if line:
                        self.queue.put(datetime.datetime.now(), line)
            finally:
                ser.close()
        
        print "Thread stopped"


    def stop(self):
        print "Thread stopping..."
        self.cancel.set()


def main():
    conn = MySerialConnection()
    conn.start()
    last_timestamp = datetime.datetime(1900, 1, 1)
    while True:
        try:
            time.sleep(0.5)
            try:
                (timestamp, line) = conn.queue.get_nowait()
                if timestamp < (last_timestamp + MIN_PAUSE):
                    print "Teil 2:", line.rstrip()
                    print "---"
                else:
                    print "Teil 1:", line.rstrip()
                last_timestamp = timestamp
            except Queue.Empty:
                pass
        except KeyboardInterrupt:
            print "KeyboardInterrupt"
            break
    conn.stop()
    conn.join()


if __name__ == "__main__":
    main()
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
whaeva
User
Beiträge: 66
Registriert: Mittwoch 25. Februar 2009, 15:30

Hallo Gerold,
vielen Dank für dein ausführliches Codebeispiel!
Wie erwähnt benutze ich schon die Timoutfunktion von serial.readline() - ein Leseversuch in der Pause gibt keine Zeichen zurück, d.h. sobal zwei aufeinanderfolgende Lesevorgänge gleich sind, kann ich sicher sein, das Paket erfasst und geprüft zu haben.

Aber wenn man kein eingebautes Timeout hat, ist dein Ansatz sicherlich hilfreich. Zeigt auch schön Threading...
Antworten