Seite 1 von 1

mehrere COM-Ports unabhängig voneinander abfragen

Verfasst: Montag 3. Februar 2014, 16:04
von SVblue
Hallo,
gleich vorab, ich fange gerade an mich in Python einzuarbeiten.
Ich habe hier ein interessantes Projekt auf den Tisch bekommen mit dem ich mich beschäftigen darf.
Ziel soll es sein Daten von mehreren Sensoren in eine Datenbank zu schreiben.
Zunächst habe ich dies zu Testzwecken bereits auf einzelnen Raspberry Pi's umgesetzt... das würde wohl auch funktionieren aber ich zweifle auf die dauerhafte Zuverlässigkeit der kleinen Rechner an.
Nun habe ich hier einen Industrierechner mit 4 COM-Ports liegen, Debian und Python ist drauf.

Begonnen habe ich nun die vorangegangen Tests auf dieses einen Rechner zu übertragen.
Schnell wird klar dass das Abfragen mehrerer COM-Ports nicht ganz trivial ist.
Hinterinander bspw. 2 COM-Ports abzufragen funktioniert, jedoch erfolgt die Weiterverarbeitung (zunächst nur die Ausgabe der empfangenen Daten) erst wenn von beiden COM-Ports Daten empfangen wurden (Zeile 16 u. 17).
Frage: Wie kann man es lösen dass beide unabhängig von einander arbeiten? Ich kann vorher nicht wissen wann auf welchem Port Daten eintreffen... keine Weiterverarbeitung eines Port-Ergebnisses sollte auf das andere warten müssen!

Hat jemand eine Idee und würde mir weiterhelfen können?

Code: Alles auswählen

import serial
import time

def readlineCR(port):
    rv = ""
    while True:
        ch = port.read()
        rv += ch
        if ch=='\r' or ch=='':
            return rv

port1 = serial.Serial('/dev/ttyS0', baudrate=115200, timeout= None)
port2 = serial.Serial('/dev/ttyS1', baudrate=115200, timeout= None)

while True:
        port1.write("\r\nSay something:")
        port2.write("\r\nSay something:")

        rcv1 = readlineCR(port1)
        rcv2 = readlineCR(port2)

        print(rcv1)
        print(rcv2)

        port1.write("\r\nYou sent:" + repr(rcv1))
        port2.write("\r\nYou sent:" + repr(rcv2))

Re: mehrere COM-Ports unabhängig voneinander abfragen

Verfasst: Montag 3. Februar 2014, 16:12
von BlackJack
@SVblue: Das liesse sich zum Beispiel mit dem `threading`-Modul lösen in dem pro Port eine Funktion parallel gestartet wird der die empfangenen Daten in eine `Queue.Queue` steckt. Im Hauptprogramm kann man dann die Daten aus der Queue lesen und weiterverarbeiten.

Edit: In Python 2 ist ``print`` keine Funktion, die Klammern gehören da also nicht hin. Das es Python 2 ist, sieht man daran, dass in Python 3 Bytes statt Zeichenketten über die seriellen Schnittstellen gehen müssten.

Re: mehrere COM-Ports unabhängig voneinander abfragen

Verfasst: Montag 3. Februar 2014, 17:26
von Sirius3
@SVblue: das ist ein typischer Anwendungsfall von "select.select".

Re: mehrere COM-Ports unabhängig voneinander abfragen

Verfasst: Montag 3. Februar 2014, 18:49
von pillmuncher
Was BlackJack und Sirius3 gesagt haben.

Wenn es threading.Thread werden soll, hier eine Anregung:

Code: Alles auswählen

import serial
from threading import Thread
from Queue import Queue
from contextlib import closing


def read_line(port):
    line = []
    while True:
        line.append(port.read())
        if line[-1] in ('\r', ''):
            return ''.join(line)


POISON = object()


class MyThread(Thread):

    def __init__(self, portname, baudrate):
        self.portname = portname
        self.baudrate = baudrate
        self._reader = Queue()
        self._writer = Queue()
        Thread.__init__(self)

    def send(self, data):
        self._writer.put(data)

    def receive(self):
        return self._reader.get()

    def run(self):
        port = serial.Serial(self.portname, baudrate=self.baudrate)
        with closing(port):
            while True:
                while not self._writer.empty():
                    data = self._writer.get()
                    if data is POISON:
                        return
                    port.write(data)
                self._reader.put(read_line(port))


def main():
    portnames = '/dev/ttyS0', '/dev/ttyS1'
    threads = [MyThread(portname, 115200) for portname in portnames]
    while True:
        for thread in threads:
            thread.send('\r\nSay something:')
            rcv = thread.receive()
            print rcv
            if rcv.lower() == 'quit':
                for thread in threads:
                    thread.send('\r\nGoodbye.')
                    thread.send(POISON)
                for thread in threads:
                    thread.join()
                return
            else:
                thread.send('\r\nYou sent:' + rcv)


if __name__ == '__main__':
    main()
Ungetestet.

Re: mehrere COM-Ports unabhängig voneinander abfragen

Verfasst: Dienstag 4. Februar 2014, 10:47
von SVblue
Ihr seid ja fix - Respekt und vielen Dank für euer Engagement.

Ich habe pillmuncher's Code mal unverändert ausprobiert.
Leider scheint es an irgendeinen Aufruf in der main zu scheitern.
Es kommt am COM nichts an.

Code: Alles auswählen

thread.send('\r\nSay something:')
Wird schon nicht übertragen.
Eine Fehlermeldung bleibt aus, das Programm lässt sich auch nicht beenden (Strg+C hilf nicht).

Ich bleibe aber dran und versuche es weiter. Wenn jemand noch etwas beisteuern kann... sehr gern, ich bin dankbar für jeden Tip da ich leider nur über wenig Erfahrung diesbzgl. verfüge.

Jeden Fortschritt werde ich hier berichten sodass der Nachwelt auch geholfen ist. ;-)

Re: mehrere COM-Ports unabhängig voneinander abfragen

Verfasst: Dienstag 4. Februar 2014, 11:56
von pillmuncher
Sorry, ich hatte vergessen, die Threads zu starten. Zwischen den Zeilen 47 und 48 bitte einfügen:

Code: Alles auswählen

    for thread in threads:
        thread.start()
Außerdem ist die weitere Ablauflogik in main() evtl. nicht das, was du möchtest. Im Moment werden alle Threads beendet, sobald von irgendeinem Port ein "quit" kommt. Vielleicht möchtest du ja von den anderen Ports weiterlesen, solange bis jeder Port dir ein "quit" geschickt hat. Oder irgendein ganz anderes Verhalten. Auch blockiert der Hauptthread, wenn von einem Port gerade nichts kommt, und das möchtest du vielleicht gar nicht. Statt dessen könnte die recieve() Methode zB. None zurückgeben, wenn self._reader gerade leer ist. Dann könnte man in main() darauf testen und mit einem continue reagieren.

Auf was es mir ankam, war nur, dass jeder Thread einen Port repräsentiert, mit dem man asynchron kommunizieren kann, ohne dass man sich im weiteren Programmverlauf besonders darum kümmern muss.