mehrere COM-Ports unabhängig voneinander abfragen

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
SVblue
User
Beiträge: 15
Registriert: Montag 3. Februar 2014, 15:48

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))
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.
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@SVblue: das ist ein typischer Anwendungsfall von "select.select".
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

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.
In specifications, Murphy's Law supersedes Ohm's.
SVblue
User
Beiträge: 15
Registriert: Montag 3. Februar 2014, 15:48

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. ;-)
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

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.
In specifications, Murphy's Law supersedes Ohm's.
Antworten