Hi,
ich möchte mit QT ein Programm schreiben, wo eine serielle Schnittstelle
(FTDI) angesteuert wird.
An der Schnittstelle hängt ein kleiner Mikrocontroller.
Entweder sendet man ein Kommando, dann wird das ausgeführt. Das ist kein
Problem.
Oder aber man sendet eine Anfrage, diese wird dann beantwortet. Leider
weiss man im Voraus nicht, wie viele Bytes man bekommen wird, aber man
bekommt immer eine Zeile, am Schluss ist ein CR LF.
Ich habe das jetzt so implementiert, dass meine eigene Klasse, welche
die Funktionalität implementieren soll, einen Slot hat (rdData), und
mittels connect() habe ich das readyRead()-Signal der QSerialPort-Klasse
mit meiner eigenen Klasse verheiratet. Im Handler lese ich dann einfach
alle verfügbaren Daten vom QSerialPort aus und hänge sie zu Testzwecken
einfach man an einer Textbox an. Das funktioniert. Wie jedoch könnte ich
eine kluge Synchronisation erzielen? wenn ich eine Anfrage absende,
möchte ich eigentlich gern auf die Antwort warten, ohne jedoch, dass mir
das GUI einfriert. Wie macht man das?
Es gibt im Grunde zwei Möglichkeiten; entweder du generierst ein Signal,
wenn die Antwort da ist und machst dann weiter mit dem Programmablauf
(in dem Slot, der von dem Signal aufgerufen wird), oder du lagerst die
ganze Kommunikationslogik in einen Thread aus, der dann synchron einfach
auf die Antwort warten kann. Was besser ist, hängt vom Anwendungsfall
ab.
OK. Mir ist im Grunde nicht so wichtig, wie es genau implementiert wird.
Ich glaube aber, die Lösung mit dem separaten Thread, welcher auf die
Antwort warten kann, scheint mir gut.
Könntest du mir ein Beispiel dazu zeigen? ich bin da definitiv nicht fit
genug in C++. Wie warte ich in dem Thread z.B. elegant auf das Ende der
Antwort? Das Ende kann ja, wie gesagt, am CR LF erkannt werden.
Das bekommst man ja "kostenlos" mit wenn man Signale & Slots dafür
nutzt. Hier z.B. gibt deine Interface-Klasse ein Signal bei
abgeschlossenem Lese-Vorgang aus, das du nutzen kannst.
Geht man gegen diese asynchrone Design-Philosophie kann man das ganze
einfach in einen eigenen Thread packen. Die Kommunikations-Funktionen
dürfen dann selbstverständlich nicht im GUI-Thread aufgerufen werden,
sonst gewinnt man dadurch nichts.
Hatte die vorausgehenden Antworten nicht gesehen...
Dein ready-read bzw. das synchrone equivalent gibt dir so viel gerade im
Puffer liegt. Ich würde die Daten intern in einen Puffer schreiben und
dort nach dem CRLF suchen, dann entsprechend parsen und das Signal
senden oder welche andere Synchronisierungsstruktur auch immer genutzt
wird anstoßen und die Daten aus dem internen Puffer löschen. Damit ist
das Problem der in mehrere Blöcke zerlegten Transaktionen gelöst, bzw.
auch dass mehrere Transaktionen in einem Block gelesen werden.
Die Frage ist halt was du mit der Antwort machen willst. In einem
Fenster anzeigen? Dann reicht definitiv ein weiteres Signal, dass eine
Antwort angekommen ist, was du z.B. gegebenerfalls aus der Funktion
emittierst die nach readyRead() aufgerufen wird. Je nach Ergebnis der
Abfrage einen weiteren Befehl schicken, und das mit komplexer Struktur?
Dann kann ein separater Thread sinnvoll sein, weil die asynchrone Logik
sonst leicht in Spaghetti-Code ausartet.
Die Antwort beinhaltet zB. ein paar Messwerte, welche in einem Diagramm
angezeigt werden sollen.
Aber das hängt von der zuvor gesendeten Anfrage ab. Je nach dem, was
halt angefragt wird, muss die Antwort anders interpretiert werden! :-/
Sollte man evtl. unterschiedliche Handler für das readyRead-Signal
benutzen und diese jeweils mit connect() anders verbinden?
Tobias P. schrieb:> Sollte man evtl. unterschiedliche Handler für das readyRead-Signal> benutzen und diese jeweils mit connect() anders verbinden?
Ne, ich denke das gibt ein Chaos, weil du dann ständig
connect/disconnect machen musst. Wenn du die Antworten anhand der
Antwort unterscheiden kannst (z.B. Byte im Header was sagt auf welchen
Befehl das eine Antwort ist), unterscheide einfach anhand dessen in dem
Handler was passiert, andernfalls kannst du dir ja in einer
Membervariablen merken, auf welchen Befehl du als nächstes eine Antwort
erwartest.
Tobias P. schrieb:> OK. Mir ist im Grunde nicht so wichtig, wie es genau implementiert wird.> Ich glaube aber, die Lösung mit dem separaten Thread, welcher auf die> Antwort warten kann, scheint mir gut.
Ich sehe da wenig Sinn drin. Denn dann hast du das gleiche Problem
wieder, nur nicht bei der Kommunikation zwischen seriellem Port und GUI,
sondern zwischen deinem Empfänger-Thread und dem GUI-Thread.
Threads bringen hier eigentlich nur was, wenn die Verarbeitung sehr
aufwändig ist und viel CPU-Zeit braucht. Das würde dann ohne Threads die
GUI ausbremsen.
CuTee schrieb:> Hatte die vorausgehenden Antworten nicht gesehen...> Dein ready-read bzw. das synchrone equivalent gibt dir so viel gerade im> Puffer liegt.
Nein, readyRead() gibt gar nichts. Es sagt nur, dass neue Daten
angekommen sind.
> Ich würde die Daten intern in einen Puffer schreiben und> dort nach dem CRLF suchen, dann entsprechend parsen
Oder man benutzt im mit readyRead() verbundenen Slot einfach die
Funktion canReadLine(), um zu ermitteln, ob eine ganze Zeile vorhanden
ist, und wenn ja, liest man sie mit readLine(). Das macht man dann in
einer Schleife, für den Fall, dass inzwischen mehr als eine Zeile
angekommen ist.
Hi,
Vielleicht habe ich was überlesen ?
Es es ist doch ganz einfach:
Im Slot readyRead die Funktion canReadLine checken.
Wenn wahr: readLine nutzen.
Keine Threads, keine Sync-Probleme...
cu
N2
Ja, klar geht das. Ich hab das jetzt ja auch so implementiert. ABER ich
möchte doch folgendes tun: wenn auf einen Button geklickt wird, soll
eine entsprechende Anfrage gesendet werden und die Antwort soll in einem
Textfeld angezeigt werden. Pseudo-Beispiel:
das geht natürlich mit dem komplett asynchronen Ansatz mit dem SIGNAL
nicht wirklich. Die Frage war, ob man es irgendwie so implementieren
kann, dass meine Klasse intern irgendwie das Signal verwendet, nach
aussen hin aber so aussieht, als ob es nicht so wäre. (Mir fehlt da im
Moment der richtige Fachbegriff für. Wie nennt man das? synchron vs.
asynchron? bei Windows war das glaub ich irgendwie 'overlapped IO').
Tobias P. schrieb:> ABER ich> möchte doch folgendes tun: wenn auf einen Button geklickt wird, soll> eine entsprechende Anfrage gesendet werden und die Antwort soll in einem> Textfeld angezeigt werden.
Warum möchtest du das so machen? Also auf die Antwort warten?
Kannst du nicht einfach die Antwort plotten, wenn sie kommt?
Was machst du wenn gar keine Antwort kommt?
Eine Möglichkeit, Code ungefähr so zu schreiben, ist mit einem Callback,
also du übergibst dem send_query eine Funktion als Argument, die
aufgerufen wird wenn's fertig ist. Das kannst du noch als Lambda machen
und dann hast du ungefähr das.
Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.
Wichtige Regeln - erst lesen, dann posten!
Groß- und Kleinschreibung verwenden
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang