Forum: PC-Programmierung ? zu Python treading von einem Anfänger


von Thomas P. (topla)


Lesenswert?

Hallo zusammen,

ich bin absoluter Einsteiger in Python und mache da nach einem kurzen 
Einsteierlehrgang meine ersten Schritte.

Was möchte ich probieren:
Ziel ist die Kommunikation mit einem externen Gerät über eine serielle 
Schnittstelle. Das Gerät antwortet auf Anfragen und kann auch unabhängig 
von Anfragen selbst Meldungen absetzen, auf die ich reagieren sollte. 
Die Kommunikation findet über Binärdaten mit 8N1 statt. Die Daten mit 
einem Frame aus 2 Bytes und können in der Länge variabel sein.
Es muss geprüft werden, ob die empfangenen Daten korrekt (xor CRC) und 
vollständig sind. Dazu möchte ich einen eigenen Thread laufen lassen, 
der genau das macht und nur dann, wenn die Daten als korrekt erkannt 
wurden, diese dem Hauptprozess zur Verfügung stellt. Das ist derzeit 
über eine Queue geplant.

Was habe ich:
Ich habe eine Klasse implementiert, die die serielle Schnittstelle 
initialisiert, alle Zeichen entgegennimmt, prüft und in eine Queue 
stellt (sowie zu Testzwecken ausgibt). Funktioniert einwandfrei.
Das Senden der Daten läuft derzeit direkt aus der main(), funktioniert 
auch.

Wo ist das Problem:
1. Da die empfangenen Daten schon geprüft sind, würde ich gerne nur die 
Nutzdaten an den Hauptprozess weitergeben. Auf Grund der variablen Länge 
brauche ich da aber wieder Framekennzeichen und Länge. Kann ich die 
Queue auf eine Liste umstellen, der ich dann wieder je Datensatz eine 
Liste übergebe? Damit würde ich im Hauptprogramm mit einem Zugriff 
wieder alle Daten eines Datensatzes zur Verfügung haben. Wäre schön, 
aber welche Nebenwirkungen hat das?

2. Kann ich die Senderoutine mit in die Klasse
class lzv_sst(threading.Thread): aufnehmen, damit alles zusammen bleibt 
oder ist diese Klasse ausschließlich für den Empfangsthread da? Die 
Initialisierung liegt ja auch dort, aber im Moment sende ich die Daten 
mit einem direkten Aufruf - geht, aber ist nicht sauber. Hier blicke ich 
das Konzept nicht.
Bringe ich das Senden als Methode in die Klasse, funktioniert zwar das 
Senden, es gibt aber keinen Empfang mehr.

Danke für alle Hinweise (und bitte nicht gleich draufhauen, ich weiß, 
dass ich da viele Lücken habe)
Thomas

von MaWin (Gast)


Lesenswert?

Ohne Code sind die Fragen nicht beantwortbar.

von Thomas P. (topla)


Lesenswert?

Punkt 2 hat sich gerade beim Aufbereiten des Codes zum hier Einstellen 
erledigt, ich habe eine Umwandlung ".to_bytes" vergessen. Zum Senden 
nimmt es die Routine, aber das falsche Format bewirkt, dass es keine 
Antwort gibt.

zu Punkt 1:
Weiß jetzt nicht, was das Coding hier helfen könnte.
Ich empfange Daten im Format 
[Frame_1][Frame_2][Kommando+Länge]...[Data_i][CRC].
In der Empfangsroutine wird der Datensatz geprüft und in eine Queue 
gestellt, da nicht sicher ist, ob vor der Verarbeitung im main nicht 
schon neue Daten eingetroffen sind. Der komplette empfangene Datensatz 
steht in "incoming_data":
for i in incoming_data:
    self.queue.put(i)
Jetzt kann ich das im main wieder aus der Queue holen und verarbeiten, 
also schauen, dass die Framedaten passen (Synchronisation), dann die 
Länge aus dem dritten Zeichen ermitteln und danach die restlichen Bytes 
einlesen. CRC kann entfallen, die Prüfung ist ja schon erfolgt.
Ziel wäre jetzt, den gesamten Datensatz unabhängig von seiner Länge als 
ein Objekt zu übergeben und genauso im main wieder zu empfangen. Das 
würde mir die Framebytes sparen und es gäbe trotzdem keine 
Synchronisationsprobleme.

Thomas

von Sascha W. (sascha-w)


Lesenswert?

1) in Queue kannst du alles reinpacken - hinten kommt es so wieder raus. 
Also pack deinen Datensatz in ein Array oder ein Objekt und so bleibt 
alles zusammen was zusammen gehört.

2) zeig mal den Code

Sascha

von MaWin (Gast)


Lesenswert?

Thomas P. schrieb:
> Ziel wäre jetzt, den gesamten Datensatz unabhängig von seiner Länge als
> ein Objekt zu übergeben und genauso im main wieder zu empfangen

Ja dann mache das doch so.
Was soll ich jetzt ohne Code hier sagen?

von Thomas P. (topla)


Angehängte Dateien:

Lesenswert?

Moin,

anbei mal den derzeitigen Stand des proof-of-concept mit zahlreichen 
Debug-Ausgaben.
Prinzipiell scheint das zu funktionieren, wenn nach dem Senden einer 
Anfrage
1
    zentrale.write([33, 33])
2
    while zentrale.status < 2:
3
        pass
4
    #sleep(0.05)
5
    myData = zentrale.read()
6
    print("Main", myData)
und warten auf zentrale.status > 1 die Daten sofort abgeholt werden. 
Gibt es eine kleine Pause (hier: sleep(0.05)), liefert die Ausgabe nur 
einen leeren Datensatz.

Soweit ich das mit meinem Anfängerwissen eingrenzen konnte, liegt das 
Problem in der Klassendefinition, offensichtlich wird die Funktion 
self.read() auch aufgerufen und damit ist die Queue leer. Aber sollte 
nicht nur run(self) ständig laufen? Oder können Threads und Funktionen 
nicht in eine Klasse gepackt werden?
Frage: Wie baut man die Klasse richtig auf?

Thomas

von Thomas P. (topla)


Lesenswert?

Ich mache dann mal die Ingrid:
Die Klassendefinition ist unschuldig.
Das Schreiben und Lesen von Listen über die Queue funktioniert 
problemlos; danke für den Hinweis.
Der oben beschriebene Fehler liess sich da lokalisieren:
1
            elif sm == 3:           # Telegrammkörper
2
                incoming_data.append(lzv_data)
3
                crc = crc ^ lzv_data    # CRC bilden
4
                anz = anz - 1
5
                if anz == 0:        # alles da?
6
                    if crc == 0:    # und CRC ok
7
                        sm = 0
8
                        # ganzen Satz in die Queue stellen
9
                        self.queue.put(incoming_data)
10
                        if self.status == 1:
11
                            self.status = 2    # Antwort auf Anfrage
12
                        elif self.status == 0:
13
                            self.status = 3    # ungefragte Daten
14
                        #print("Daten da:", incoming_data)
15
                        #sleep(0.1)
16
                    #del incoming_data[:]
17
                    incoming_data = []
Am Ende der Sequenz wird nach dem Übergeben der Daten an die Queue die 
Liste "incoming_data" mit den eingegangenen Daten mit "del 
incoming_data[:] gelöscht. Das funktioniert nur in Verbindung mit dem 
vorhergehenden sleep, fehlt das, werden im main leere Daten aus der 
Queue gelesen.
Ersetzt man beides durch "incoming_data = []", funktioniert die Übergabe 
an die main einwandfrei.
Dass ein "incoming_data = []" eine neue Liste erzeugt und an den "alten" 
Namen bindet, weiß ich - warum "del incoming_data[:]" ohne "sleep" 
irgendwie den Schreibprozess in die Queue abschießt, leider nicht.

Thomas

von Sascha W. (sascha-w)


Lesenswert?

i.d.R. werden Objekte nicht als Kopie übergeben sondern nur als Zeiger 
auf das Objekt. Ich könnte mir daher vorstellen das in Queue.put, 
asyncron zum aufrufenden Thread erst eine Kopie des Objekts erstellt und 
an die Queue angehangen wird. Löschst du im Hauptthread das Objekt bevor 
die Kopie erstellt wurde sind sie Daten halt weg.
Müsste man sich mal die Implementierung anschauen.
Zur Not kannst du auch selbst eine Kopie an Queue.put übergeben.

Sascha

von Thomas P. (topla)


Lesenswert?

Die Implementierung zu untersuchen übersteigt meine Fähigkeiten.
Du hast aber recht, übergibt man die Daten an die Queue mit 
"queue.put(list(incoming_data))" dann funktioniert auch "del 
incoming_data[:]" ohne "sleep".
Ist irgendwie blöd, wenn ich aufeinanderfolgende Anweisungen schreibe, 
dann gehe ich doch davon aus, dass bei Ausführung der 2. Anweisung  die 
1. Anweisung vollständig abgearbeitet ist. Böse Falle...

Thomas

Beitrag #6554415 wurde von einem Moderator gelöscht.
Beitrag #6554417 wurde von einem Moderator gelöscht.
Beitrag #6554418 wurde von einem Moderator gelöscht.
von MaWin (Gast)


Lesenswert?

Thomas P. schrieb:
> Ist irgendwie blöd, wenn ich aufeinanderfolgende Anweisungen schreibe,
> dann gehe ich doch davon aus, dass bei Ausführung der 2. Anweisung  die
> 1. Anweisung vollständig abgearbeitet ist.

Ist sie ja auch.
Das Problem ist eher, dass du anscheinend nicht weißt, wie Objekte in 
Python funktionieren.
In Python ist alles pass-by-reference.

von Thomas P. (topla)


Lesenswert?

Ja, das ist bei mir ein Anfängerproblem, dass das zwar bei der 
Druckbetankung mal erwähnt wurde, aber die Konsequenzen erst am 
konkreten Beispiel sichtbar werden. Merkt man sich nach so einer Pleite 
aber besser...

Thomas

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.